Sinatr
Sinatr

Reputation: 21979

Load and create instance of data template

I have an ItemsControl used to display View of items like this:

<ItemsControl ItemsSource="{Binding Items}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding View}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Below is short mcve to give you an idea:

public class Item
{
    public string Text { get; set; }
    public object View { get; set; }
    ... // more properties used in bindings
}

public partial class MainWindow : Window
{
    public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>();

    public MainWindow()
    {
        InitializeComponent();
        // 1
        {
            var control = new TextBlock();
            var item = new Item { Text = "1", View = control };
            BindingOperations.SetBinding(control, TextBlock.TextProperty, new Binding(nameof(Item.Text)) { Source = item });
            Items.Add(item);
        }
        // 2
        {
            var control = new CheckBox();
            var item = new Item { Text = "2", View = control };
            BindingOperations.SetBinding(control, CheckBox.ContentProperty, new Binding(nameof(Item.Text)) { Source = item });
            Items.Add(item);
        }
        // ... and so on
        DataContext = this;
    }
}

As you can see each item has pre-created View (unfortunately this can't/shouldn't be changed), which can be anything, includes binding, etc.


My question: how to move creating of View into xaml (as data templates)?

Pseudoxaml:

<SomeContainer.Resources>
    <DataTemplate x:Key="type1">
        <TextBlock Text="{Binding Text}" />
    </DataTemplate>
    <DataTemplate x:Key="type2">
        <CheckBox Content="{Binding Text}" />
    </DataTemplate>
</SomeContainer.Resources>
<ItemsControl ... /> <!-- same definition as early? -->

Pseudo-code

Items.Add(new Item { Text = "1", View = LoadTemplate("type1") });
Items.Add(new Item { Text = "2", View = LoadTemplate("type2") });

object LoadTemplate(string key)
{
    var resource = FindResource(key);
    ... // what next?
}

Upvotes: 0

Views: 1585

Answers (2)

ASh
ASh

Reputation: 35646

if you absolutely have to use UIElements in view model (instead of templates), and at the same time want to declare them in xaml, then

  • don't use DataTemplate
  • use x:Shared="False" on UIElement
<Window.Resources>
    <TextBlock x:Key="type1" x:Shared="False" Text="{Binding Text}"/>
    <CheckBox x:Key="type2" x:Shared="False" Content="{Binding Text}"/>
</Window.Resources>

each time you request a resource, you will get a new copy

LoadTemplate method is reduced to FindResource

object LoadTemplate(string key)
{
    return FindResource(key);
}

Upvotes: 1

mm8
mm8

Reputation: 169160

Instead of creating a UI control such as a TextBlock or a CheckBox in the view model you should create a CLR object:

public class MyTextClass
{
    public string Text { get; set; }
}

...

var view = new MyTextClass();
var item = new Item { Text = "1", View = control };

You could then use a DataTemplate in the view to associate an instance of your CLR object with a control:

<DataTemplate DataType="local:MyTextClass">
    <TextBlock Text="{Binding Text}" />
</DataTemplate>

When you set the DataType property of a DataTemplate without specifying an x:Key, the DataTemplate gets applied automatically to data objects of that type: https://msdn.microsoft.com/en-us/library/system.windows.datatemplate.datatype(v=vs.110).aspx

Upvotes: 1

Related Questions