Home > database >  Q: How should I handle Datatemplates for Models?
Q: How should I handle Datatemplates for Models?

Time:10-30

In the MVVM pattern, the view shouldn't know anything about the models, but what if I wanna display different types differently?

For example I have two classes. The class Message and the class AttachmentMessage which inherits from Message.

Message

public class Message
{
    public string Content { get; set; }
}

AttachmentMessage

public class AttachmentMessage : Message
{
    public string Filename { get; set; }
}

Now when I use them in an ObservableCollection<Message>, I have both models in this collection, but I can't tell WPF which Datatemplate it has to use, without knowing which Models there are.

So what are solutions for this problem?

CodePudding user response:

The most common and recommended way would be to create a data template for each type you need and put that in your resources. The following code assumes your observable collection has the name Messages. Example:

<ItemsControl ItemsSource="{Binding Messages}">
    <ItemsControl.Resources>
        <DataTemplate DataType="{x:Type local:Message}">
            <TextBlock Text="{Binding Content}"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:AttachmentMessage}">
            <TextBlock Text="{Binding Filename}"/>
        </DataTemplate>
    </ItemsControl.Resources>
</ItemsControl>

The other way is to create a DataTemplateSelector. Let's say your messages all had a property that indicated their priority. You could create a template selector like the below. DataTemplateSelector can be used when you need more fine-grained control over which template is selected.

public class MyDataTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item is Message m && container is FrameworkElement fm)
        {
            if (m.Priority == Priority.High)
            {
                return fm.FindResource("HighPriorityTemplate") as DataTemplate;
            }
            else
            {
                return fm.FindResource("NormalPriorityTemplate") as DataTemplate;
            }
        }

        return null;
    }
}

And use it in xaml like the following:

<Window.Resources>
    <!-- Put your templates here-->
    <local:MyDataTemplateSelector x:Key="MyDataTemplateSelector"/>
</Window.Resources>
<ItemsControl ItemsSource="{Binding Messages}" ItemTemplateSelector="{StaticResource MyDataTemplateSelector}">

As I side note, in the MVVM pattern, usually you have three parts, Model, View and ViewModel. Some people take the shortcut of binding directly to the Model, but I generally would avoid this. You can find a discussion about this here.

  • Related