WPF:当某个值更改时重新应用DataTemplateSelector

所以这是我拥有的XAML:

 

这是我的ListTemplateSelector类:

 public class ListTemplateSelector : DataTemplateSelector { public DataTemplate GroupTemplate { get; set; } public DataTemplate ItemTemplate { get; set; } public override DataTemplate SelectTemplate(object item, DependencyObject container) { GroupList list = item as GroupList; if (list != null && !list.IsLeaf) return GroupTemplate; return ItemTemplate; } } 

GroupTemplate数据模板本身引用了ListTemplateSelector,所以这就是我设置的原因。 这是我可以放在一起的唯一递归黑客。 但那不是我遇到的问题。

我的问题是,我想在IsLeaf属性更改时从ItemTemplate更改为GroupTemplate。 这是第一次读取房产以来的第一次。 但是一旦此属性更改,模板选择器就不会重新应用。 现在,我可以使用触发器绑定到值并适当地设置项模板,但我需要能够为每个项设置不同的模板,因为它们可能处于不同的状态。

例如,假设我有一个像这样的组列表:

第1组:IsLeaf = false,因此template = GroupTemplate

第2组:IsLeaf = true,因此template = ItemTemplate

第3组:IsLeaf = false,因此template = GroupTemplate

一旦组1的IsLeaf属性更改为true,模板就需要自动更改为ItemTemplate。

编辑:

这是我的临时解决方案。 有没有更好的方法呢?

                   

关于你的编辑,DataTemplate Trigger不是足够而不是使用Style? 那是:

             

我发现这种解决方法对我来说似乎更容易。 从TemplateSelector中听取您关心的属性,然后重新应用模板选择器以强制刷新。

 public class DataSourceTemplateSelector : DataTemplateSelector { public DataTemplate IA { get; set; } public DataTemplate Dispatcher { get; set; } public DataTemplate Sql { get; set; } public override DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container) { var ds = item as DataLocationViewModel; if (ds == null) { return base.SelectTemplate(item, container); } PropertyChangedEventHandler lambda = null; lambda = (o, args) => { if (args.PropertyName == "SelectedDataSourceType") { ds.PropertyChanged -= lambda; var cp = (ContentPresenter)container; cp.ContentTemplateSelector = null; cp.ContentTemplateSelector = this; } }; ds.PropertyChanged += lambda; switch (ds.SelectedDataSourceType.Value) { case DataSourceType.Dispatcher: return Dispatcher; case DataSourceType.IA: return IA; case DataSourceType.Sql: return Sql; default: throw new NotImplementedException(ds.SelectedDataSourceType.Value.ToString()); } } } 

回到原始解决方案并且“模板选择器无法重新应用”的问题:您可以刷新您的视图

 CollectionViewSource.GetDefaultView(YourItemsControl.ItemsSource).Refresh(); 

为了简洁起见,您的ItemsControl被添加到XAML的名称(“YourItemsControl”)引用:

  

唯一的问题可能是如何在项目中为此刷新指令选择正确的位置。 它可以进入代码隐藏视图,或者,如果您的IsLeaf是DP,正确的位置将是依赖项属性更改的回调。

我用绑定代理做到了。

它的工作方式类似于普通绑定代理(但有2个Props – 将数据从DataIn复制到DataOut),但只要Trigger值发生变化,就会将DataOut设置为NULL并返回DataIn值:

 public class BindingProxyForTemplateSelector : Freezable { #region Overrides of Freezable protected override Freezable CreateInstanceCore() { return new BindingProxyForTemplateSelector(); } #endregion public object DataIn { get { return (object)GetValue(DataInProperty); } set { SetValue(DataInProperty, value); } } public object DataOut { get { return (object) GetValue(DataOutProperty); } set { SetValue(DataOutProperty, value); } } public object Trigger { get { return (object) GetValue(TriggerProperty); } set { SetValue(TriggerProperty, value); } } public static readonly DependencyProperty TriggerProperty = DependencyProperty.Register(nameof(Trigger), typeof(object), typeof(BindingProxyForTemplateSelector), new PropertyMetadata(default(object), OnTriggerValueChanged)); public static readonly DependencyProperty DataInProperty = DependencyProperty.Register(nameof(DataIn), typeof(object), typeof(BindingProxyForTemplateSelector), new UIPropertyMetadata(null, OnDataChanged)); public static readonly DependencyProperty DataOutProperty = DependencyProperty.Register(nameof(DataOut), typeof(object), typeof(BindingProxyForTemplateSelector), new PropertyMetadata(default(object))); private static void OnTriggerValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // this does the whole trick var sender = d as BindingProxyForTemplateSelector; if (sender == null) return; sender.DataOut = null; // set to null and then back triggers the TemplateSelector to search for a new template sender.DataOut = sender.DataIn; } private static void OnDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var sender = d as BindingProxyForTemplateSelector; if (sender == null) return; sender.DataOut = e.NewValue; } } 

像这样用它:

       

所以你不直接绑定到你的DataContext,而是绑定到BindingProxy的DataOut,它反映了原始的DataContext,但差别很小:当触发器改变时(在这个例子中是’Item’中的bool值),TemplateSelector得到再次触发。

您不必为此更改TemplateSelector。

也可以添加更多触发器,只需添加一个Trigger2即可。