Blog của Lê Văn Luật

Đời người thì có hạn mà sự học thì vô hạn!

WPF: Thừa kế Template và DataTemplateSelector

Trong WPF, việc thừa kế Style rất dễ dàng do được hỗ trợ sẵn qua thuộc tính BasedOn như ví dụ sau:

    <style>
            <Setter Property="HorizontalAlignment" Value="Center" />
            <Setter Property="FontFamily" Value="Comic Sans MS"/>
            <Setter Property="FontSize" Value="14"/>
    </style>
    <style>
            <Setter Property="FontSize" Value="26"/>
            <Setter Property="Foreground">
                <Setter.Value>
                    <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                        <LinearGradientBrush.GradientStops>
                            <GradientStop Offset="0.0" Color="#90DDDD" />
                            <GradientStop Offset="1.0" Color="#5BFFFF" />
                        </LinearGradientBrush.GradientStops>
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
    </style>

Trong ví dụ trên, style TitleText thừa kế style BaseStyle nhờ đoạn BasedOn=”{StaticResource BaseStyle}”.

Với Template, WPF không hỗ trợ thừa kế như đối với style nhưng trong thực tế nhiều khi ta cần thừa kế style nhằm giảm code XAML, tăng tính logic.

Ví dụ: ta có mô hình đối tượng như sau
InheritedTemplateclass

Xây dựng các lớp theo mô hình trên và tạo lớp tạo dữ liệu (ta sẽ dùng ObjectDataProvider):

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
namespace InheritedTemplate
{
    class Person
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public DateTime Birthdate { get; set; }
        public bool Sex { get; set; }
    }

    class Course // Môn học
    {
        public int ID { get; set; }
        public string Code { get; set; }
        public string CourseName { get; set; }
    }

    class Class // Lớp
    {
        public int ID { get; set; }
        public string Code { get; set; }
        public bool Done { get; set; }
    }

    class Trainer : Person // Giáo viên
    {
        public List<Course>  AssumedCourses { get; set; } // Môn dạy
    }

    class Student : Person // Sinh viên
    {
        public Class Class { get; set; }
    }

    class DataSource
    {
        public List<Course> Courses { get; set; }
        public List<Class> Classes { get; set; }
        public List<Person> Persons { get; set; }

        public DataSource()
        {
            Persons = new List<Person>();
            Courses = new List<Course>()
            {
                new Course(){ID=1,Code="THCB",CourseName="Tin học cơ bản"},
                new Course(){ID=2,Code="THKT",CourseName="Tin học kế toán"},
                new Course(){ID=3,Code="LTKT",CourseName="Lý thuyết kế toán"}
            };
            Classes = new List<Class>()
            {
                new Class(){ID=1,Code="LT39-1",Done=false},
                new Class(){ID=2,Code="LT39-2",Done=false}
            };
            Trainer t = new Trainer() { ID = 1, Name = "Trần Văn Tuấn", Birthdate = new DateTime(1980, 5, 28), Sex = false };
            t.AssumedCourses = new List<Course>() { Courses[0], Courses[1] };
            Persons.Add(t);
            t = new Trainer() { ID = 2, Name = "Lê Văn Trung", Birthdate = new DateTime(1981, 12, 22), Sex = false };
            t.AssumedCourses = new List<Course>() { Courses[1] };
            Persons.Add(t);
            Student s = new Student() { ID = 3, Name = "Nguyễn Thị Lan", Birthdate = new DateTime(1993, 4, 28), Sex = true, Class=Classes[0] };
            Persons.Add(s);
        }

        public List<Person> GetPersons()
        {
            return Persons;
        }

        public List<Course> GetCourses()
        {
            return Courses;
        }

        public List<Class> GetClasses()
        {
            return Classes;
        }
    }

    class PersonTemplateSelector : DataTemplateSelector
    {
        public DataTemplate TrainerTemplate { get; set; }
        public DataTemplate StudentTemplate { get; set; }

        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            if (item.GetType() == typeof(Trainer))
                return TrainerTemplate;
            else if (item.GetType() == typeof(Student))
                return StudentTemplate;
            else return null;
        }
    }
}

Với mô hình đối tượng như trên, một cách rất logic ta nghĩ ngay đến các template như sau:

+ DataTemplate PersonTemplate hiển thị những thông tin của class Person

+ DateTemplate StudentTemplate hiển thị thông tin của sinh viên (bao gồm thông tin như Person và thêm Lớp).

+ DataTemplate TrainerTemplate hiển thị thông tin của giáo viên (bao gồm thông tin như Person và thêm Các môn giảng dạy).

Như vậy phần thông tin chung trong Person lặp lại trong hai template TrainerTemplate và StudentTemplate.

Tuy nhiên, WPF kông cung cấp cơ chế rõ ràng để thừa kế template.

Một giải pháp để có thể thừa kế được thực hiện như sau:

<Window x:Class="InheritedTemplate.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:local="clr-namespace:InheritedTemplate"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <ObjectDataProvider x:Key="Persons" ObjectType="{x:Type local:DataSource}" MethodName="GetPersons"/>
        <ObjectDataProvider x:Key="Classes" ObjectType="{x:Type local:DataSource}" MethodName="GetClasses"/>
        <ObjectDataProvider x:Key="Courses" ObjectType="{x:Type local:DataSource}" MethodName="GetCourses"/>
        <Grid x:Shared="false" x:Key="PersonTemplate">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0" Grid.Column="0" Text="ID"/>
            <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding ID}"/>
            <TextBlock Grid.Row="1" Grid.Column="0" Text="Name"/>
            <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Name}"/>
            <TextBlock Grid.Row="2" Grid.Column="0" Text="Birthdate"/>
            <DatePicker Grid.Row="2" Grid.Column="1" SelectedDate="{Binding Birthdate}"/>
            <TextBlock Grid.Row="3" Grid.Column="0" Text="Sex"/>
            <ComboBox Grid.Row="3" Grid.Column="1" SelectedValue="{Binding Sex}" SelectedValuePath="Tag">
                <ComboBoxItem Content="Nam" >
                    <ComboBoxItem.Tag>
                        <sys:Boolean>false</sys:Boolean>
                    </ComboBoxItem.Tag>
                </ComboBoxItem>
                <ComboBoxItem Content="Nữ">
                    <ComboBoxItem.Tag>
                        <sys:Boolean>true</sys:Boolean>
                    </ComboBoxItem.Tag>
                </ComboBoxItem>
            </ComboBox>
        </Grid>
        <DataTemplate x:Key="TrainerTemplate" DataType="{x:Type local:Trainer}">
            <Border BorderThickness="2" BorderBrush="Red">
            <StackPanel Orientation="Vertical">
                <ContentPresenter Content="{StaticResource PersonTemplate}"/>
                    <DataGrid ItemsSource="{Binding AssumedCourses}"/>
                </StackPanel>
            </Border>
        </DataTemplate>
        <DataTemplate x:Key="StudentTemplate" DataType="{x:Type local:Student}">
            <Border BorderBrush="Blue" BorderThickness="2">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <ContentPresenter Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Content="{StaticResource PersonTemplate}"/>
                    <TextBlock Grid.Column="0" Grid.Row="1" Text="Class"/>
                    <ComboBox Grid.Column="1" Grid.Row="1" SelectedItem="{Binding Class}"/>
                </Grid>
            </Border>
        </DataTemplate>
        <local:PersonTemplateSelector x:Key="PersonTemplateSelector" 
                                      StudentTemplate="{StaticResource StudentTemplate}"
                                      TrainerTemplate="{StaticResource TrainerTemplate}"/>
    </Window.Resources>
    <ListView ItemTemplateSelector="{DynamicResource PersonTemplateSelector}" HorizontalContentAlignment="Stretch" ItemsSource="{Binding Mode=OneWay}" DataContext="{Binding Source={StaticResource Persons}}"/>
</Window>

Lưu ý:
+ PersonTemplate phải xác định x:Shared = False
+ Phần TrainerTemplate và StudentTemplate thừa kế từ PersonTemplate nằm trong đoạn:

<ContentPresenter Content="{StaticResource PersonTemplate}"/>

Kết quả như sau:
InheritedTemplateResult

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s

Information

This entry was posted on 02/05/2013 by in Lập trình C# & WPF.

Điều hướng

%d bloggers like this: