WPF:DataTemplateSelector设置控件不同的样式

本文详细介绍了如何在WPF中优雅地使用DataTemplateSelector来根据不同类型的绑定数据动态选择和显示不同类型的控件,如TextBox、ComboBox和DatePicker。通过重构代码,避免了硬编码和不必要的业务逻辑引用,实现了更灵活和优雅的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近想实现这么个东西,一个ListBox, 里面的ListBoxItem可能是文本框、下拉框、日期选择控件等等。

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

先定义好各类资源

ExpandedBlockStart.gifResources
    <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

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

新建一个类继承DataTemplateSelector

ExpandedBlockStart.gifDataTypeTemplateSelector
   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,即可运行

ExpandedBlockStart.gifCode 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为绑定数据对象

这意味着前台需要引入后端的业务逻辑,代码的味道相当不好,不过没有关系,我们有强大的反射工具,重构下代码:

ExpandedBlockStart.gifSelectTemplate 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

ExpandedBlockStart.gifCombinedEntity V1
  public class CombinedEntity
    {
        /// <summary>
        
/// 绑定数据的值
        
/// </summary>
        public object CombinedValue
        {
            get;
            set;
        }

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

修改ListBox绑定数据源

ExpandedBlockStart.gifData 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 方法

ExpandedBlockStart.gifSelectTemplate 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,而且实现的方式看上去比较优雅。

转载于:https://www.cnblogs.com/leodrain/archive/2012/08/23/elegant-way-using-datatemplateselector-showing-more-styles.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值