0

I have a contentcontrol that is bound to an object (data property of DataContext in the example). Whenever the object referenced by data changes, I want to re-select the datatemplate. How can I do that?

<ContentControl Name="rootData" Content="{Binding data}" 
            ContentTemplateSelector="{StaticResource myTemplateSelector}"/>
schwarz
  • 501
  • 7
  • 28
  • Is data type of DataContext.data same or different? Do you want to apply different type of object to DataContext.data property? – Rajnikant Nov 04 '14 at 11:43

3 Answers3

1

If you have same data type and different values for Data then you can use DataTemplate Selector as below, Otherwise just use DataType attribute of DataTemplate and you don't even need datatemplate selector. Below is sample code to select template every time you change the data.

MainWindow.xaml

<Window x:Class="TextBindingFormatting.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TextBindingFormatting"
    Title="MainWindow" Height="350" Width="555">
<Window.Resources>
    <local:MyTemplateSelector x:Key="MyTemplateSelector"></local:MyTemplateSelector>
    <DataTemplate x:Key="Template1">
        <StackPanel Orientation="Horizontal">
            <Label>Label 1</Label>
            <Label>Label 2</Label>
        </StackPanel>
    </DataTemplate>
    <DataTemplate x:Key="Template2">
        <StackPanel Orientation="Vertical">
            <Label>Label 1</Label>
            <Label>Label 2</Label>
        </StackPanel>
    </DataTemplate>

</Window.Resources>
<Grid>
    <StackPanel>
    <ContentControl Content="{Binding Data}" ContentTemplateSelector="{StaticResource MyTemplateSelector}"></ContentControl>
        <Button Content="Change DataTemplate" Click="ButtonBase_OnClick"></Button>
    </StackPanel>
</Grid>

Below is the Code Behind, Ideally button click should be handled using command, but for quick example I have implemented in code behind just to trigger change of data.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new MainWindowViewModel() {Data = "1"};
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        var vm = this.DataContext as MainWindowViewModel;
        vm.Data = "2";
    }
}

Below is the ViewModel for MainWindow

namespace TextBindingFormatting.ViewModels
{
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        private string _data;
        public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    public string Data
    {
        get { return _data; }
        set
        {
            _data = value;
            OnPropertyChanged("Data");
        }
    }
}

}

DataTemplate Selector

namespace TextBindingFormatting
{
    public class MyTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            var element = container as FrameworkElement;
            if (element == null || item == null)
                return base.SelectTemplate(item, container);

        if (item.ToString() == "1")
            return element.FindResource("Template1") as DataTemplate;

        if (item.ToString() == "2")
            return element.FindResource("Template2") as DataTemplate;

        return base.SelectTemplate(item, container);
    }
    }
}
Rajnikant
  • 2,176
  • 24
  • 23
  • Unfortunately this does not solve my problem! The object changes completely. The data property points to objects of different types. – schwarz Nov 04 '14 at 13:16
  • Added another example, you only need to take away x:Key and use DataType={x:Type typeofyourModel} as shown in below example. And remove ContentTemplateSelector. – Rajnikant Nov 04 '14 at 14:36
1

In Case you are changing whole data type then your view should be as below, Only difference is removing key and using DataType for DataTemplate, this is called implicit data template

<Window x:Class="TextBindingFormatting.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TextBindingFormatting"
    xmlns:viewModels="clr-namespace:TextBindingFormatting.ViewModels"
    Title="MainWindow" Height="350" Width="555">
<Window.Resources>
    <local:MyTemplateSelector x:Key="MyTemplateSelector"></local:MyTemplateSelector>
    <DataTemplate DataType="{x:Type viewModels:Student}">
        <StackPanel Orientation="Horizontal">
            <Label>Id</Label>
            <TextBlock Text="{Binding Id}"></TextBlock>
            <Label>Name</Label>
            <TextBlock Text="{Binding Name}"></TextBlock>
        </StackPanel>
    </DataTemplate>
    <DataTemplate DataType="{x:Type local:Parent}">
        <StackPanel Orientation="Vertical">
            <Label>Name</Label>
            <TextBlock Text="{Binding Name}"></TextBlock>
        </StackPanel>
    </DataTemplate>

</Window.Resources>
<Grid>
    <StackPanel>
    <ContentControl Content="{Binding Data}" >
    </ContentControl>
        <Button Content="Change DataTemplate" Click="ButtonBase_OnClick"></Button>
    </StackPanel>
</Grid>

code behind

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new MainWindowViewModel() {Data = new Student{Id = 1, Name = "Student"}};
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        var vm = this.DataContext as MainWindowViewModel;
        vm.Data = new Parent() {Name = "This is parent"};
    }
}

I have two classes as below

public class Student : INotifyPropertyChanged
{
    private string _name;
    private int _id;
    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    public string Name {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        }
    }

    public int Id
    {
        get { return _id; }
        set
        {
            _id = value;
            OnPropertyChanged("Id");
        }
    }
}

And another

public class Parent : INotifyPropertyChanged 
{

    private string _name;
    public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged(string propertyName)

    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        }
    }
}
Rajnikant
  • 2,176
  • 24
  • 23
  • I guess you didn't understand what I meant :) First of all I need a default template which wouldn't work in this case. Secondly, like I wrote in the answer I posted myself, selectTemplate is not fired everytime the property changes. It is just fired once ... AFAIK! – schwarz Nov 04 '14 at 15:21
  • Is your data property raise change notification? As in my example it does every time you change data property. – Rajnikant Nov 05 '14 at 09:28
  • Interesting! In my case yes I do raise the propChanged, but did you see the answer I posted? In that link, the answer from Rachel says that SelectTemplate is not fired everytime a prop changes! – schwarz Nov 05 '14 at 09:51
  • @Schwarz, There is difference in the case for which you added the link, in that case property Data is complex object and they want to change the template based on property of the object assigned to Data. Here we are changing whole data object so it will fire every time. Agree if not entire object but only one property of that object is changed in that case SelectionTemplate will not fire and you will have to use datatrigger and that is what data triggers for. Here you are saying that you are changing whole data object in that case it you can straight away use implicit data template. – Rajnikant Nov 06 '14 at 11:03
0

Found a possible solution here from Rachel: Change Data template dynamically

DataTemplateSelector wont trigger on PropertyChange but setting triggers does the job for me!

Community
  • 1
  • 1
schwarz
  • 501
  • 7
  • 28