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


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

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

先定義好各類資源

Resources
     < Window.Resources >
         < DataTemplate  x:Key ="textBox" >
             < Border  BorderBrush ="Gray"  BorderThickness ="1" >
                 < TextBox  Text =" {Binding CombinedValue} " ></ TextBox >
             </ Border >
         </ DataTemplate >
         < DataTemplate  x:Key ="comboBox" >
             < Border  BorderBrush ="Gray"  BorderThickness ="1" >
                 < ComboBox  ItemsSource =" {Binding CombinedValue} " ></ ComboBox >
             </ Border >
         </ DataTemplate >
         < DataTemplate  x:Key ="dateTime" >
             < Border  BorderBrush ="Gray"  BorderThickness ="1" >
                 < DatePicker  Text =" {Binding CombinedValue} "   ></ DatePicker >
             </ Border >
         </ DataTemplate >
     </ Window.Resources >

然后在ListBox中設置ItemDataTemplateSelector

ListBox
< ListBox  ItemsSource =" {Binding} " >
         < ListBox.ItemTemplateSelector >
             < local:DataTypeTemplateSelector  TextBoxTemplate =" {StaticResource textBox} "
                                            ComboBoxTemplate
=" {StaticResource comboBox} "
                                            DateTimeTemplate
=" {StaticResource dateTime} " ></ local:DataTypeTemplateSelector >
         </ ListBox.ItemTemplateSelector >
     </ ListBox >

新建一個類繼承DataTemplateSelector

DataTypeTemplateSelector
    public  class DataTypeTemplateSelector:DataTemplateSelector
    {
         public DataTemplate TextBoxTemplate {  getset; }
         public DataTemplate ComboBoxTemplate {  getset; }
         public DataTemplate DateTimeTemplate {  getset; }

         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;
        }
    }

設置好DataContext,即可運行

Code Behind
  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;
        }

         ///   <summary>
        
///  數據的類型
        
///   </summary>
         public  string TypeName
        {
             get;
             set;
        }
    }

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

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

CombinedEntity entity = item  as CombinedEntity;  // CombinedEnity為綁定數據對象

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

SelectTemplate V1
   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;
                }
            }
             if (typeName ==  " TextBox ")
            {
                 return TextBoxTemplate;
            }
             if (typeName ==  " ComboBox ")
            {
                 return ComboBoxTemplate;
            }
             if (typeName ==  " DateTime ")
            {
                 return DateTimeTemplate;
            }
             return  null;
        }

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

運行起來,還是沒有問題,仔細看DataTypeTemplateSelector對象的SelectTemplate 方法,還是有點丑陋,這里把CombinedEntity的TypeName屬性硬編碼,萬一TypeName改成ControlName或其他名字,控件則無法按照預期顯示。

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

CombinedEntity V1
   public  class CombinedEntity
    {
         ///   <summary>
        
///  綁定數據的值
        
///   </summary>
         public  object CombinedValue
        {
             get;
             set;
        }

         ///   <summary>
        
///  顯示控件的類型
        
///   </summary>
         public Type ControlType
        {
             get;
             set;
        }
    }

修改ListBox綁定數據源

Data Context
 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)}
            };
             this.DataContext = entities;

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

SelectTemplate V2
      public  override DataTemplate SelectTemplate( object item, DependencyObject container)
        {
            Type t = item.GetType();
            Type controlType =  null;
            PropertyInfo[] properties = t.GetProperties();
             foreach (PropertyInfo pi  in properties)
            {
                 if (pi.PropertyType ==  typeof(Type))
                {
                    controlType = (Type)pi.GetValue(item,  null);
                     break;
                }
            }
             if (controlType ==  typeof(TextBox))
            {
                 return TextBoxTemplate;
            }
             if (controlType ==  typeof(ComboBox))
            {
                 return ComboBoxTemplate;
            }
             if (controlType ==  typeof(DatePicker))
            {
                 return DateTimeTemplate;
            }
             return  null;
        }

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM