【问题标题】:Changing DataTemplate during Runtime在运行时更改 DataTemplate
【发布时间】:2019-11-15 07:22:32
【问题描述】:

我使用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 时,用户界面不会使用新模板刷新。不得不提的是,我并没有改变对象本身。

                 <ctrl:TreeViewControl DataContext="{Binding}">
                    <ctrl:TreeViewControl.Resources>
                        <ResourceDictionary Source="pack://application:,,,/OldDataTemplate.xaml"/>
                    </ctrl:TreeViewControl.Resources>
                </ctrl:TreeViewControl>

我的问题: 是否可以在运行时更改 DataTemplate 并刷新 UI 而无需更改绑定对象本身?

编辑: 我继续测试:ResourceDictionary(及其模板)已更改。新添加的项目(模板更改后)使用新模板。但是旧项目没有更新。

【问题讨论】:

  • 不确定DynamicResource 是否可以在这种情况下提供帮助,但由于切换到新模板有效,只需“刷新 UI” 就足够了。尝试将DataContext 设置为null,然后恢复它(可能使用调度程序调用)。
  • 除非您在数据模板中提供可组合的部分,否则更改资源不会产生任何影响。 (这个概念属于控制模板,我不知道它们也适用于数据模板)除了你已经按照这里提到的那样直接设置它the right way
  • 你可以试试 DataTemplate Selector

标签: c# wpf datatemplate


【解决方案1】:

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 里面的 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);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-08
    • 2012-09-04
    相关资源
    最近更新 更多