在运行时更改 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);