如何强制重新选择 DataTemplateSelector
How to force re-selection of DataTemplateSelector
我在 UWP 中的 ListBox 上使用 DataTemplateSelector,我想为现有的 ListBoxItem 强制重新选择 DataTemplate。我能让它工作的唯一方法是从绑定集合中删除该项目,然后重新添加该项目。这真的很像一个kluge。我只想使列表框项的布局无效并让它再次调用数据模板选择器。似乎无法执行此操作。
这是页面...
<Page.Resources>
<local:MyTemplateSelector x:Key="TemplateSelector"/>
</Page.Resources>
<Page.DataContext>
<local:MyViewModel/>
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<ListBox x:Name="listBox1" Margin="5" ItemsSource="{Binding QuantityRows}"
ItemTemplateSelector="{StaticResource TemplateSelector}">
<ListBox.Resources>
<DataTemplate x:Key="Detail">
<StackPanel Orientation="Horizontal">
<Button Click="OnShowSummary">Show Summary</Button>
<TextBlock Text="Name "/>
<TextBox Width="40" Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="Summary">
<StackPanel Orientation="Horizontal">
<Button Click="OnShowDetail">Show Detail</Button>
<TextBlock Text="{Binding Summary}"/>
</StackPanel>
</DataTemplate>
</ListBox.Resources>
</ListBox>
</StackPanel>
</Grid>
这是模板选择器...
public class MyTemplateSelector : DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
QuantityRow row = item as QuantityRow;
ListBox lb = UWPUtilities.GetParent<ListBox>(container);
if (row != null && lb != null)
{
if (row.IsDetail)
{
return lb.Resources["Detail"] as DataTemplate;
}
else
{
return lb.Resources["Summary"] as DataTemplate;
}
}
return base.SelectTemplateCore(item, container);
}
}
这是视图模型...
public class QuantityRow
{
public bool IsDetail { get; set; }
public int ID { get; set; }
public string Name { get; set; }
public string Summary
{
get
{
return "Name = " + Name;
}
}
}
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<QuantityRow> QuantityRows { get; set; }
public MyViewModel()
{
QuantityRows = new ObservableCollection<QuantityRow>();
QuantityRows.Add(new QuantityRow() { IsDetail = false, ID = 1, Name = "Length" });
QuantityRows.Add(new QuantityRow() { IsDetail = true, ID = 2, Name = "Diameter" });
QuantityRows.Add(new QuantityRow() { IsDetail = false, ID = 3, Name = "Temperature" });
QuantityRows.Add(new QuantityRow() { IsDetail = false, ID = 4, Name = "Pressure" });
QuantityRows.Add(new QuantityRow() { IsDetail = true, ID = 5, Name = "Angle" });
}
}
这是背后的代码...
public MyViewModel ViewModel
{
get
{
return DataContext as MyViewModel;
}
}
private void OnShowSummary(object sender, RoutedEventArgs e)
{
QuantityRow row = (sender as Button).DataContext as QuantityRow;
row.IsDetail = false;
// UpdateLayout1(row);
UpdateLayout2(sender);
}
private void OnShowDetail(object sender, RoutedEventArgs e)
{
QuantityRow row = (sender as Button).DataContext as QuantityRow;
row.IsDetail = true;
// UpdateLayout1(row);
UpdateLayout2(sender);
}
private void UpdateLayout1(QuantityRow row)
{
int index = ViewModel.QuantityRows.IndexOf(row);
ViewModel.QuantityRows.RemoveAt(index);
ViewModel.QuantityRows.Insert(index, row);
}
private void UpdateLayout2(object sender)
{
ListBoxItem lbi = UWPUtilities.GetParent<ListBoxItem>(sender as DependencyObject);
lbi.InvalidateArrange();
}
最后是效用函数...
public static class UWPUtilities
{
public static T GetParent<T>(DependencyObject d) where T : class
{
while (d != null && !(d is T))
{
d = VisualTreeHelper.GetParent(d);
}
return d as T;
}
}
每个列表项上都有一个按钮,用于在摘要模板和详细信息模板之间切换。 UpdateLayout1 工作正常,但代价是绑定列表中有一点变动。 UpdateLayout2 不起作用。在我看来,这是一个更简洁的实现。为什么 ListBoxItem 上的 InvalidateArrange() 不强制重新选择模板?
非常感谢@Justin XL 提出的这个想法。为了让模板选择器再次触发,您必须将其设置为 null,然后将其设置回相同的引用。这确实是另一个 kluge,但我喜欢它比我的第一个 kluge 稍微好一点。没有优雅可言。
private void UpdateLayout2(object sender)
{
ListBoxItem lbi = UWPUtilities.GetParent<ListBoxItem>(sender as DependencyObject);
DataTemplateSelector dts = lbi.ContentTemplateSelector;
lbi.ContentTemplateSelector = null;
lbi.ContentTemplateSelector = dts;
}
我在 UWP 中的 ListBox 上使用 DataTemplateSelector,我想为现有的 ListBoxItem 强制重新选择 DataTemplate。我能让它工作的唯一方法是从绑定集合中删除该项目,然后重新添加该项目。这真的很像一个kluge。我只想使列表框项的布局无效并让它再次调用数据模板选择器。似乎无法执行此操作。
这是页面...
<Page.Resources>
<local:MyTemplateSelector x:Key="TemplateSelector"/>
</Page.Resources>
<Page.DataContext>
<local:MyViewModel/>
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<ListBox x:Name="listBox1" Margin="5" ItemsSource="{Binding QuantityRows}"
ItemTemplateSelector="{StaticResource TemplateSelector}">
<ListBox.Resources>
<DataTemplate x:Key="Detail">
<StackPanel Orientation="Horizontal">
<Button Click="OnShowSummary">Show Summary</Button>
<TextBlock Text="Name "/>
<TextBox Width="40" Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="Summary">
<StackPanel Orientation="Horizontal">
<Button Click="OnShowDetail">Show Detail</Button>
<TextBlock Text="{Binding Summary}"/>
</StackPanel>
</DataTemplate>
</ListBox.Resources>
</ListBox>
</StackPanel>
</Grid>
这是模板选择器...
public class MyTemplateSelector : DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
QuantityRow row = item as QuantityRow;
ListBox lb = UWPUtilities.GetParent<ListBox>(container);
if (row != null && lb != null)
{
if (row.IsDetail)
{
return lb.Resources["Detail"] as DataTemplate;
}
else
{
return lb.Resources["Summary"] as DataTemplate;
}
}
return base.SelectTemplateCore(item, container);
}
}
这是视图模型...
public class QuantityRow
{
public bool IsDetail { get; set; }
public int ID { get; set; }
public string Name { get; set; }
public string Summary
{
get
{
return "Name = " + Name;
}
}
}
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<QuantityRow> QuantityRows { get; set; }
public MyViewModel()
{
QuantityRows = new ObservableCollection<QuantityRow>();
QuantityRows.Add(new QuantityRow() { IsDetail = false, ID = 1, Name = "Length" });
QuantityRows.Add(new QuantityRow() { IsDetail = true, ID = 2, Name = "Diameter" });
QuantityRows.Add(new QuantityRow() { IsDetail = false, ID = 3, Name = "Temperature" });
QuantityRows.Add(new QuantityRow() { IsDetail = false, ID = 4, Name = "Pressure" });
QuantityRows.Add(new QuantityRow() { IsDetail = true, ID = 5, Name = "Angle" });
}
}
这是背后的代码...
public MyViewModel ViewModel
{
get
{
return DataContext as MyViewModel;
}
}
private void OnShowSummary(object sender, RoutedEventArgs e)
{
QuantityRow row = (sender as Button).DataContext as QuantityRow;
row.IsDetail = false;
// UpdateLayout1(row);
UpdateLayout2(sender);
}
private void OnShowDetail(object sender, RoutedEventArgs e)
{
QuantityRow row = (sender as Button).DataContext as QuantityRow;
row.IsDetail = true;
// UpdateLayout1(row);
UpdateLayout2(sender);
}
private void UpdateLayout1(QuantityRow row)
{
int index = ViewModel.QuantityRows.IndexOf(row);
ViewModel.QuantityRows.RemoveAt(index);
ViewModel.QuantityRows.Insert(index, row);
}
private void UpdateLayout2(object sender)
{
ListBoxItem lbi = UWPUtilities.GetParent<ListBoxItem>(sender as DependencyObject);
lbi.InvalidateArrange();
}
最后是效用函数...
public static class UWPUtilities
{
public static T GetParent<T>(DependencyObject d) where T : class
{
while (d != null && !(d is T))
{
d = VisualTreeHelper.GetParent(d);
}
return d as T;
}
}
每个列表项上都有一个按钮,用于在摘要模板和详细信息模板之间切换。 UpdateLayout1 工作正常,但代价是绑定列表中有一点变动。 UpdateLayout2 不起作用。在我看来,这是一个更简洁的实现。为什么 ListBoxItem 上的 InvalidateArrange() 不强制重新选择模板?
非常感谢@Justin XL 提出的这个想法。为了让模板选择器再次触发,您必须将其设置为 null,然后将其设置回相同的引用。这确实是另一个 kluge,但我喜欢它比我的第一个 kluge 稍微好一点。没有优雅可言。
private void UpdateLayout2(object sender)
{
ListBoxItem lbi = UWPUtilities.GetParent<ListBoxItem>(sender as DependencyObject);
DataTemplateSelector dts = lbi.ContentTemplateSelector;
lbi.ContentTemplateSelector = null;
lbi.ContentTemplateSelector = dts;
}