所以这是我拥有的XAML:
<ItemsControl ItemsSource="{Binding Path=Groups}" ItemTemplateSelector="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=ListTemplateSelector}"/>
这是我的ListTemplateSelector类:
public class ListTemplateSelector : DataTemplateSelector {
public DataTemplate GroupTemplate { get; set; }
public DataTemplate ItemTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
GroupList<Person> list = item as GroupList<Person>;
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。
编辑:
这是我的临时解决方案。有没有更好的方法呢?
<ItemsControl ItemsSource="{Binding Path=Groups}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{DynamicResource ItemTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsLeaf}" Value="False">
<Setter Property="ContentTemplate" Value="{DynamicResource GroupTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
答案 0 :(得分:21)
关于你的编辑,DataTemplate Trigger不是足够而不是使用Style吗?那就是:
<ItemsControl ItemsSource="{Binding Path=Groups}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl x:Name="cc" Content="{Binding}" ContentTemplate="{DynamicResource ItemTemplate}"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsLeaf}" Value="False">
<Setter TargetName="cc" Property="ContentTemplate" Value="{DynamicResource GroupTemplate}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
答案 1 :(得分:21)
我发现这种解决方法对我来说似乎更容易。从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());
}
}
}
答案 2 :(得分:2)
返回原始解决方案并且“模板选择器无法重新应用”的问题:您可以刷新您的视图
CollectionViewSource.GetDefaultView(YourItemsControl.ItemsSource).Refresh();
为了简洁起见,您的ItemsControl被添加到您的XAML的名称(“YourItemsControl”)引用:
<ItemsControl x:Name="YourItemsControl" ItemsSource="{Binding Path=Groups}"
ItemTemplateSelector="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=ListTemplateSelector}"/>
唯一的问题可能是如何在项目中为此刷新指令选择正确的位置。它可以进入代码隐藏视图,或者,如果你的IsLeaf是DP,那么正确的位置将是依赖属性更改的回调。
答案 3 :(得分:0)
我使用绑定代理执行此操作。
它的工作方式类似于普通绑定代理(但有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;
}
}
像这样使用:
<Grid>
<Grid.Resources>
<local:BindingProxyForTemplateSelector DataIn="{Binding}" Trigger="{Binding Item.SomeBool}" x:Key="BindingProxy"/>
</Grid.Resources>
<ContentControl Content="{Binding Source={StaticResource BindingProxy}, Path=DataOut.Item}" ContentTemplateSelector="{StaticResource TemplateSelector}"/>
</Grid>
所以你不要直接绑定到你的DataContext,而是绑定到BindingProxy的DataOut,它反映了原始的DataContext,但差别很小:当触发器发生变化时(在这个例子里面是一个bool值) &#39;项目&#39;),模板选择器被重新触发。
您不必为此更改TemplateSelector。
还可以添加更多触发器,只需添加Trigger2。
答案 4 :(得分:0)
我对解决方案并不满意,我将以设法使选择器检查更改的方式发布:
Traceback (most recent call last):
File "serialize_model.py", line 60, in <module>
traced_script_module.save("model.pt")
AttributeError: 'function' object has no attribute 'save'
在XAML中的用法:
public class DynamicSelectorContentControl : ContentControl
{
// Using a DependencyProperty as the backing store for ListenToProperties. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ListenToPropertiesProperty =
DependencyProperty.Register("ListenToProperties", typeof(string),
typeof(DynamicSelectorContentControl),
new FrameworkPropertyMetadata(string.Empty));
public DynamicSelectorContentControl()
{
this.DataContextChanged += DynamicSelectorContentControl_DataContextChanged;
}
public string ListenToProperties
{
get { return (string)GetValue(ListenToPropertiesProperty); }
set { SetValue(ListenToPropertiesProperty, value); }
}
private void CheckForProperty(object sender, PropertyChangedEventArgs e)
{
if (ListenToProperties.Contains(e.PropertyName))
{
ClearSelector();
}
}
private void ClearSelector()
{
var oldSelector = this.ContentTemplateSelector;
if (oldSelector != null)
{
this.ContentTemplateSelector = null;
this.ContentTemplateSelector = oldSelector;
}
}
private void DynamicSelectorContentControl_DataContextChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
var listOfProperties = ListenToProperties.Split(',').Select(s => s.Trim());
var oldObservable = e.OldValue as INotifyPropertyChanged;
if (oldObservable != null && listOfProperties.Any())
{
PropertyChangedEventManager.RemoveHandler(oldObservable, CheckForProperty, string.Empty);
}
var newObservable = e.NewValue as INotifyPropertyChanged;
if (newObservable != null && listOfProperties.Any())
{
PropertyChangedEventManager.AddHandler(newObservable, CheckForProperty, string.Empty);
}
if (e.OldValue != null)
{
ClearSelector();
}
}
}
这可以更改为将依赖项作为列表,但是对于我的情况,字符串更好。 它运作良好,并且没有内存泄漏。此外,您可以将DataTemplates放在一个不会浪费您的主要xaml的额外文件中。
干杯, 马可