加载并创建数据模板实例

Load and create instance of data template

我有一个 ItemsControl 用于显示 View 个这样的项目:

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

下面是简短的 mcve 给你一个想法:

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

如您所见,每个项目都已预先创建View(不幸的是,这无法更改t/shouldn),可以是任何内容,包括绑定等


我的问题:如何将 View 的创建移动到 xaml(作为数据模板)?

伪xaml:

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

伪代码

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

与其在视图模型中创建 UI 控件,例如 TextBlockCheckBox,不如创建一个 CLR 对象:

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

...

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

然后您可以在视图中使用 DataTemplate 将 CLR 对象的实例与控件相关联:

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

当您设置 DataTemplateDataType 属性 而不指定 x:Key 时,DataTemplate 会自动应用于该类型的数据对象: https://msdn.microsoft.com/en-us/library/system.windows.datatemplate.datatype(v=vs.110).aspx

如果您绝对必须在视图模型(而不是模板)中使用 UIElement,同时想在 xaml 中声明它们,那么

  • 不要使用 DataTemplate
  • 在 UIElement
  • 上使用 x:Shared="False"
<Window.Resources>
    <TextBlock x:Key="type1" x:Shared="False" Text="{Binding Text}"/>
    <CheckBox x:Key="type2" x:Shared="False" Content="{Binding Text}"/>
</Window.Resources>

每次请求资源,都会得到一份新的副本

LoadTemplate 方法简化为 FindResource

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