在运行时更改 DataTemplate
Changing DataTemplate during Runtime
我使用 ResourceProvider
作为我的 ResourceDictionaries 的全局管理。我将新的 ResourceDictionaries 注册到 ResourceProvider 并为每个受影响的 FrameworkElement 引发一个事件。 FrameworkElement 然后使用以下方法更新其资源:(我尝试了几种方法来解决这个问题,最后我尝试用它的 Uri 更改 DataTemplate)
public void UpdateResources(FrameworkElement elementToUpdate)
{
foreach(var controlDict in _registeredResources.Where(a => a.ControlType == elementToUpdate.GetType()))
{
//elementToUpdate.Resources.MergedDictionaries.Clear();
//elementToUpdate.Resources.MergedDictionaries.Add(controlDict);
//elementToUpdate.Resources = controlDict;
ResourceDictionary dict =new ResourceDictionary() { Source = new Uri("pack://application:,,,/ApplicationCore.UITest;component/NewDataTemplate.xaml") };
elementToUpdate.Resources = dict;
//elementToUpdate.Resources.MergedDictionaries.Clear();
//elementToUpdate.Resources.MergedDictionaries.Add(controlDict);
}
}
现在,当我按下按钮更改 DataTemplate 时,ui 不会使用新模板进行刷新。我不得不说我并没有改变对象本身。
<ctrl:TreeViewControl DataContext="{Binding}">
<ctrl:TreeViewControl.Resources>
<ResourceDictionary Source="pack://application:,,,/OldDataTemplate.xaml"/>
</ctrl:TreeViewControl.Resources>
</ctrl:TreeViewControl>
我的问题:
是否可以在运行时更改 DataTemplate 并刷新 UI 而无需更改绑定对象本身?
编辑: 我继续测试:ResourceDictionary(及其模板)已更改。新添加的项目(在模板更改后)使用新模板。但是旧项目没有更新。
1) 如果您手动重新应用模板,即使使用 StaticResources
也可以做到:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject">
<DataTemplate DataType="{x:Type local:MyContentClass1}">
<Border Background="Green" >
<TextBlock Text="{Binding MyContent}"/>
</Border>
</DataTemplate>
</ResourceDictionary>
<TreeView Name="my_trv" DataContext="{Binding}">
<TreeView.Resources>
<ResourceDictionary Source="pack://application:,,,/OldDataTemplate.xaml"/>
</TreeView.Resources>
</TreeView>
但是您必须在此处触摸 TreeView:
FrameworkElement elementToUpdate = my_trv;
ResourceDictionary dict = new ResourceDictionary() { Source = new Uri("pack://application:,,,/NewDataTemplate.xaml") };
elementToUpdate.Resources = dict;
var dataTemplateKey = new DataTemplateKey(typeof(MyContentClass1));
var dataTemplate = (DataTemplate)dict[dataTemplateKey];
my_trv.ItemTemplate = dataTemplate;
2) 如果您不想在资源中搜索特定资源,可以使用 DynamicResources
。如果你的Items中只能有一种数据,那么就比较容易了,你只需要给你的模板起个名字:
<DataTemplate DataType="{x:Type local:MyContentClass1}" x:Key="MyTemplate1">
<Border Background="LightCoral" >
<TextBlock Text="{Binding MyContent}"/>
</Border>
</DataTemplate>
<TreeView DataContext="{Binding}" ItemTemplate="{DynamicResource MyTemplate1}">
<TreeView.Resources>
<ResourceDictionary Source="pack://application:,,,/OldDataTemplate.xaml"/>
</TreeView.Resources>
</TreeView>
这样您就不必在代码隐藏中显式地触摸您的控件:
ResourceDictionary dict = new ResourceDictionary() { Source = new Uri("pack://application:,,,/NewDataTemplate.xaml") };
elementToUpdate.Resources = dict;
3) 如果您有不止一种类型的数据和不同的模板,那么需要一些小技巧。
首先,您为模板命名:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject">
<DataTemplate DataType="{x:Type local:MyContentClass1}" x:Key="MyTemplate1">
<Border Background="Green">
<TextBlock Text="{Binding MyContent}"/>
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type local:MyContentClass2}" x:Key="MyTemplate2">
<Border Background="Blue">
<TextBlock Text="{Binding MyContent}"/>
</Border>
</DataTemplate>
</ResourceDictionary>
然后,在 xaml 中,您必须使用 MergedDictionaries
而不是将 ResourceDictionary
直接放在 Resources
节点中。
然后,就是诀窍了。你把 ContentPresenter
inside DataTemplate
设置为 DynamicResources
并使用正确的名称:
<TreeView DataContext="{Binding}">
<TreeView.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/OldDataTemplate.xaml"/>
</ResourceDictionary.MergedDictionaries>
<DataTemplate DataType="{x:Type local:MyContentClass1}" >
<ContentPresenter Content="{Binding}"
ContentTemplate="{DynamicResource MyTemplate1}" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:MyContentClass2}" >
<ContentPresenter Content="{Binding}"
ContentTemplate="{DynamicResource MyTemplate2}" />
</DataTemplate>
</ResourceDictionary>
</TreeView.Resources>
</TreeView>
代码隐藏会有点变化:
elementToUpdate.Resources.MergedDictionaries.Clear();
elementToUpdate.Resources.MergedDictionaries.Add(dict);
我使用 ResourceProvider
作为我的 ResourceDictionaries 的全局管理。我将新的 ResourceDictionaries 注册到 ResourceProvider 并为每个受影响的 FrameworkElement 引发一个事件。 FrameworkElement 然后使用以下方法更新其资源:(我尝试了几种方法来解决这个问题,最后我尝试用它的 Uri 更改 DataTemplate)
public void UpdateResources(FrameworkElement elementToUpdate)
{
foreach(var controlDict in _registeredResources.Where(a => a.ControlType == elementToUpdate.GetType()))
{
//elementToUpdate.Resources.MergedDictionaries.Clear();
//elementToUpdate.Resources.MergedDictionaries.Add(controlDict);
//elementToUpdate.Resources = controlDict;
ResourceDictionary dict =new ResourceDictionary() { Source = new Uri("pack://application:,,,/ApplicationCore.UITest;component/NewDataTemplate.xaml") };
elementToUpdate.Resources = dict;
//elementToUpdate.Resources.MergedDictionaries.Clear();
//elementToUpdate.Resources.MergedDictionaries.Add(controlDict);
}
}
现在,当我按下按钮更改 DataTemplate 时,ui 不会使用新模板进行刷新。我不得不说我并没有改变对象本身。
<ctrl:TreeViewControl DataContext="{Binding}">
<ctrl:TreeViewControl.Resources>
<ResourceDictionary Source="pack://application:,,,/OldDataTemplate.xaml"/>
</ctrl:TreeViewControl.Resources>
</ctrl:TreeViewControl>
我的问题: 是否可以在运行时更改 DataTemplate 并刷新 UI 而无需更改绑定对象本身?
编辑: 我继续测试:ResourceDictionary(及其模板)已更改。新添加的项目(在模板更改后)使用新模板。但是旧项目没有更新。
1) 如果您手动重新应用模板,即使使用 StaticResources
也可以做到:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject">
<DataTemplate DataType="{x:Type local:MyContentClass1}">
<Border Background="Green" >
<TextBlock Text="{Binding MyContent}"/>
</Border>
</DataTemplate>
</ResourceDictionary>
<TreeView Name="my_trv" DataContext="{Binding}">
<TreeView.Resources>
<ResourceDictionary Source="pack://application:,,,/OldDataTemplate.xaml"/>
</TreeView.Resources>
</TreeView>
但是您必须在此处触摸 TreeView:
FrameworkElement elementToUpdate = my_trv;
ResourceDictionary dict = new ResourceDictionary() { Source = new Uri("pack://application:,,,/NewDataTemplate.xaml") };
elementToUpdate.Resources = dict;
var dataTemplateKey = new DataTemplateKey(typeof(MyContentClass1));
var dataTemplate = (DataTemplate)dict[dataTemplateKey];
my_trv.ItemTemplate = dataTemplate;
2) 如果您不想在资源中搜索特定资源,可以使用 DynamicResources
。如果你的Items中只能有一种数据,那么就比较容易了,你只需要给你的模板起个名字:
<DataTemplate DataType="{x:Type local:MyContentClass1}" x:Key="MyTemplate1">
<Border Background="LightCoral" >
<TextBlock Text="{Binding MyContent}"/>
</Border>
</DataTemplate>
<TreeView DataContext="{Binding}" ItemTemplate="{DynamicResource MyTemplate1}">
<TreeView.Resources>
<ResourceDictionary Source="pack://application:,,,/OldDataTemplate.xaml"/>
</TreeView.Resources>
</TreeView>
这样您就不必在代码隐藏中显式地触摸您的控件:
ResourceDictionary dict = new ResourceDictionary() { Source = new Uri("pack://application:,,,/NewDataTemplate.xaml") };
elementToUpdate.Resources = dict;
3) 如果您有不止一种类型的数据和不同的模板,那么需要一些小技巧。
首先,您为模板命名:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject">
<DataTemplate DataType="{x:Type local:MyContentClass1}" x:Key="MyTemplate1">
<Border Background="Green">
<TextBlock Text="{Binding MyContent}"/>
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type local:MyContentClass2}" x:Key="MyTemplate2">
<Border Background="Blue">
<TextBlock Text="{Binding MyContent}"/>
</Border>
</DataTemplate>
</ResourceDictionary>
然后,在 xaml 中,您必须使用 MergedDictionaries
而不是将 ResourceDictionary
直接放在 Resources
节点中。
然后,就是诀窍了。你把 ContentPresenter
inside DataTemplate
设置为 DynamicResources
并使用正确的名称:
<TreeView DataContext="{Binding}">
<TreeView.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/OldDataTemplate.xaml"/>
</ResourceDictionary.MergedDictionaries>
<DataTemplate DataType="{x:Type local:MyContentClass1}" >
<ContentPresenter Content="{Binding}"
ContentTemplate="{DynamicResource MyTemplate1}" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:MyContentClass2}" >
<ContentPresenter Content="{Binding}"
ContentTemplate="{DynamicResource MyTemplate2}" />
</DataTemplate>
</ResourceDictionary>
</TreeView.Resources>
</TreeView>
代码隐藏会有点变化:
elementToUpdate.Resources.MergedDictionaries.Clear();
elementToUpdate.Resources.MergedDictionaries.Add(dict);