0
votes

I use a ResourceProvider as global management for my ResourceDictionaries. I register new ResourceDictionaries to the ResourceProvider and raise a event for each affected FrameworkElement. The FrameworkElement then updates its resources with the following method: (I tried several ways to fix this and at the End i tried to change the DataTemplate with its Uri)

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);
        }
    }

Right now when i press my Button to Change the DataTemplate the ui doesn't refresh with the new template. I have to mention that I'm not changing the object itself.

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

My Question: Is it possible to change a DataTemplate during Runtime and refresh the UI without changing the bound object itself?

EDIT: I continued testing: The ResourceDictionary (with its Template) is changed. A new added item (after the Template change) uses the new Template. But the old items are not updated. enter image description here

1
Not sure if DynamicResource can help in this scenario, but since switching to new templates works it might be enough to just "refresh the UI". Try to set DataContext to null and then restore it (maybe with dispatcher invoke).Sinatr
Changing resource won't make any effect unless you provide composable parts in your datatemplate. (that concept belongs to control templates i don't know they work for datatemplates too) other than that you have set it directly as mentioned here the right wayEldar
U can try with DataTemplate SelectorUgur

1 Answers

1
votes

1) You could do it even with StaticResources if you reapply the template manually:

<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>

But you'll have to touch the TreeView here:

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) If you do not want to search your resources for a specific one, you could use DynamicResources. If there could only be one type of data in your Items, then it is relatively easy, you only have to name your template:

<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>

This way you do not have to touch your control explicitly in the codebehind:

ResourceDictionary dict = new ResourceDictionary() { Source = new Uri("pack://application:,,,/NewDataTemplate.xaml") };
elementToUpdate.Resources = dict;

3) If you have more than one type of data with different templates, then a bit of a trick is necessary.

First, you name your templates:

<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>

Then, in xaml, you'll have to use MergedDictionaries instead of putting your ResourceDictionary directly in the Resources node.

Then, there is the trick. You put ContentPresenter inside DataTemplate set to DynamicResources with the correct name:

<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>

The code-behind will change a bit:

elementToUpdate.Resources.MergedDictionaries.Clear();
elementToUpdate.Resources.MergedDictionaries.Add(dict);