天天看點

WPF:DataTemplateSelector設定控件不同的樣式

最近想實作這麼個東西,一個ListBox, 裡面的ListBoxItem可能是文本框、下拉框、日期選擇控件等等。

很自然的想到了DataTemplateSelector,并且事先定義好各類DataTemplate以顯示不同的控件。

先定義好各類資源

WPF:DataTemplateSelector設定控件不同的樣式
WPF:DataTemplateSelector設定控件不同的樣式

    <Window.Resources>

        <DataTemplate x:Key="textBox">

            <Border BorderBrush="Gray" BorderThickness="1">

                <TextBox Text="{Binding CombinedValue}"></TextBox>

            </Border>

        </DataTemplate>

        <DataTemplate x:Key="comboBox">

                <ComboBox ItemsSource="{Binding CombinedValue}"></ComboBox>

        <DataTemplate x:Key="dateTime">

                <DatePicker Text="{Binding CombinedValue}" ></DatePicker>

    </Window.Resources>

WPF:DataTemplateSelector設定控件不同的樣式

然後在ListBox中設定ItemDataTemplateSelector

WPF:DataTemplateSelector設定控件不同的樣式
WPF:DataTemplateSelector設定控件不同的樣式

<ListBox ItemsSource="{Binding}">

        <ListBox.ItemTemplateSelector>

            <local:DataTypeTemplateSelector TextBoxTemplate="{StaticResource textBox}"

                                            ComboBoxTemplate="{StaticResource comboBox}"

                                            DateTimeTemplate="{StaticResource dateTime}"></local:DataTypeTemplateSelector>

        </ListBox.ItemTemplateSelector>

    </ListBox>

WPF:DataTemplateSelector設定控件不同的樣式

建立一個類繼承DataTemplateSelector

WPF:DataTemplateSelector設定控件不同的樣式
WPF:DataTemplateSelector設定控件不同的樣式

   public class DataTypeTemplateSelector:DataTemplateSelector

    {

        public DataTemplate TextBoxTemplate { get; set; }

        public DataTemplate ComboBoxTemplate { get; set; }

        public DataTemplate DateTimeTemplate { get; set; }

        public override DataTemplate SelectTemplate(object item, DependencyObject container)

        {

            CombinedEntity entity = item as CombinedEntity; //CombinedEnity為綁定資料對象

            string typeName = entity.TypeName;

            if (typeName == "TextBox")

            {

                return TextBoxTemplate;

            }

            if (typeName == "ComboBox")

                return ComboBoxTemplate;

            if (typeName == "DateTime")

                return DateTimeTemplate;

            return null;

        }

    }

WPF:DataTemplateSelector設定控件不同的樣式

設定好DataContext,即可運作

WPF:DataTemplateSelector設定控件不同的樣式
WPF:DataTemplateSelector設定控件不同的樣式

 public partial class CombinedControl : Window

        public List<CombinedEntity> entities;

        public CombinedControl()

            InitializeComponent();

            entities = new List<CombinedEntity>()

                new CombinedEntity{ CombinedValue=new List<string>{"1","2","3"}, TypeName="ComboBox"},

                new CombinedEntity{ CombinedValue ="Test", TypeName="TextBox"},

                new CombinedEntity{ CombinedValue=DateTime.Now, TypeName="DateTime"}

            };

            this.DataContext = entities;

    public class CombinedEntity

        /// <summary>

        /// 綁定資料的值

        /// </summary>

        public object CombinedValue

            get;

            set;

        /// 資料的類型

        public string TypeName

WPF:DataTemplateSelector設定控件不同的樣式

如果運作成功,我們可以看到一個下拉框,一個文本框,一個日期選擇控件都做為ListBox的子項顯示在視窗中。

但是,我發現,在DataTypeTemplateSelector對象的SelectTemplate 方法中,居然需要把item對象轉換成我們的綁定資料對象

CombinedEntity entity = item as CombinedEntity; //CombinedEnity為綁定資料對象

這意味着前台需要引入後端的業務邏輯,代碼的味道相當不好,不過沒有關系,我們有強大的反射工具,重構下代碼:

WPF:DataTemplateSelector設定控件不同的樣式
WPF:DataTemplateSelector設定控件不同的樣式

  public override DataTemplate SelectTemplate(object item, DependencyObject container)

            Type t = item.GetType();

            string typeName = null;

            PropertyInfo[] properties = t.GetProperties();

            foreach (PropertyInfo pi in properties)

                if (pi.Name == "TypeName")

                {

                    typeName = pi.GetValue(item, null).ToString();

                    break;

                }

WPF:DataTemplateSelector設定控件不同的樣式

這樣,我們就無需引入後端的實體(Model)對象,保證了前端的幹淨。

運作起來,還是沒有問題,仔細看DataTypeTemplateSelector對象的SelectTemplate

方法,還是有點醜陋,這裡把CombinedEntity的TypeName屬性寫死,萬一TypeName改成ControlName或其他名字,控

件則無法按照預期顯示。

再次重構,首先修改綁定對象CombinedEntity

WPF:DataTemplateSelector設定控件不同的樣式
WPF:DataTemplateSelector設定控件不同的樣式

  public class CombinedEntity

        /// 顯示控件的類型

        public Type ControlType

WPF:DataTemplateSelector設定控件不同的樣式

修改ListBox綁定資料源

WPF:DataTemplateSelector設定控件不同的樣式
WPF:DataTemplateSelector設定控件不同的樣式

 entities = new List<CombinedEntity>()

                new CombinedEntity{ CombinedValue=new List<string>{"1","2","3"}, ControlType = typeof(ComboBox)},

                new CombinedEntity{ CombinedValue ="Test", ControlType = typeof(TextBox)},

                new CombinedEntity{ CombinedValue=DateTime.Now, ControlType = typeof(DatePicker)}

WPF:DataTemplateSelector設定控件不同的樣式

最後再次修改DataTypeTemplateSelector對象的SelectTemplate 方法

WPF:DataTemplateSelector設定控件不同的樣式
WPF:DataTemplateSelector設定控件不同的樣式

     public override DataTemplate SelectTemplate(object item, DependencyObject container)

            Type controlType = null;

                if (pi.PropertyType == typeof(Type))

                    controlType = (Type)pi.GetValue(item, null);

            if (controlType == typeof(TextBox))

            if (controlType == typeof(ComboBox))

            if (controlType == typeof(DatePicker))

WPF:DataTemplateSelector設定控件不同的樣式

這樣,要顯示不同的控件,在ControlType裡面定義即可,然後在XAML添加DataTemplate,在DataTemplateSelector對象中根據不同的ControlType傳回不同的DataTemplate,而且實作的方式看上去比較優雅。