本文介绍了WPF在代码隐藏中应用数据模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一个自定义ItemsControl,其中包含一个用于显示任何内容的网格。我希望用户能够使用数据模板来显示任何内容,但是我该怎么做呢?

I'm creating a custom ItemsControl that contains a grid for display whatever. I'd like the user to be able to use datatemplates for displaying that whatever, but how do I go about doing that?

我知道如何创建模板,但是我不确定如何应用模板以使项目正确位于网格内(我的代码)

I know how to create the templates, but I'm not sure how to go about applying a template such that the items are located properly within the grid (my code) and each item is displayed as the user wants (via the datatemplate).

-编辑-

我的要求似乎有些混乱。想象一下,我想使用Grid作为布局从头开始创建我自己的ListView(这并不是我真正在做的,但是作为示例...)。给定用户的DataTemplate,我该如何使用它来确保根据模板显示每个网格单元中的元素?

There appears to be a little confusion as to what I'm asking. Imagine I wanted to create my own ListView from scratch using a Grid for the layout (that's not what I'm actually doing, but as an example ... ). Given a DataTemplate from the user, how do I use that to ensure the elements inside each grid cell are displayed according to the template?

推荐答案

您的控件可以公开自己的属性,这些属性可以在代码隐藏中声明。
如果需要单个 DataTemplate ,则可以公开 DataTemplate 类型的属性。当用户在XAML中声明您的控件类型时,她可以提供模板:

Your control can expose its own property/properties, which you declare in your code-behind.If you need a single DataTemplate, then you can expose a property of type DataTemplate. When a user declares your control type in XAML, she can provide the template:

<ns:YourControl>
    <ns:YourControl.DataTemplate>
        <DataTemplate>
            …
        </DataTemplate>
    </ns:YourControl.DataTemplate>
</ns:YourControl>

在您自己的控件中,您可以通过绑定到 DataTemplate来使用它

In your own control, you consume this by binding to the DataTemplate property. Be sure to reference the control itself in your Binding, rather than the DataContext. You'll probably want a default DataTemplate or throw a useful Exception in the event that the user does not specify a DataTemplate.

如果公开类型为的属性,则可以为用户提供更多的灵活性。 DataTemplateSelector ,然后将其应用于您的项,如果数据类型不同或用户在不同情况下可能想要不同的模板。

You can give the user some additional flexibility if you expose a property of type DataTemplateSelector and then apply that to your items, if the data types are disparate or the user is likely to want different templates under different circumstances.

MyControl.xaml

MyControl.xaml

<UserControl x:Class="MyNamespace.MyControl"
             x:Name="ThisControl">
    <ItemsControl ItemTemplate="{Binding ItemTemplate, ElementName=ThisControl}" />
</UserControl>

MyControl.xaml.cs

MyControl.xaml.cs

public partial class MyControl : UserControl
{
    public MyControl()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty ItemTemplateProperty 
        = DependencyProperty.Register("ItemTemplate", typeof (DataTemplate), 
        typeof (MyControl), new PropertyMetadata(default(DataTemplate)));

    public DataTemplate ItemTemplate
    {
        get { return (DataTemplate) GetValue(ItemTemplateProperty); }
        set { SetValue(ItemTemplateProperty, value); }
    }

    // Other dependency properties (ItemsSource, SelectedItem, etc.)
}

消费者:

<Grid>
    <ns:MyControl ItemsSource="{Binding Items}"
                  SelectedItem="{Binding SelectedItem}">
        <ns:MyControl.ItemTemplate>
            <DataTemplate>
                <Border BorderThickness="2"
                        BorderBrush="Black">
                    <TextBlock Foreground="DarkGray"
                               Text="{Binding Name}"
                               Margin="4" />
               </Border>
            </DataTemplate>
        </ns:MyControl.ItemTemplate>
    </ns:MyControl>
</Grid>



更新



好的,这是填充 Grid 并使用 DataTemplate 的工作示例。

Update

Okay, here is a working example of populating the Grid and using the DataTemplate.

MyControl 公开属性 ItemsSource ,该属性允许消费者绑定到其视图模型中的集合。 MyControl 还公开了一个属性 ItemTemplate ,该属性允许使用者指定如何显示这些项目(同样,您可以还允许用户指定 DataTemplateSelector )。

MyControl exposes a property, ItemsSource, which allows the consumer to bind to a collection in her view-model. MyControl also exposes a property, ItemTemplate, which allows a consumer to specify how to display those items (again, you could also allow the user to specify a DataTemplateSelector).

在后面的代码中,当源集合更改时,我们

In the code-behind, when the source collection changes, we


  1. 为每个项目创建 ColumnDefinition

  2. 将每个项目包装在另一个类中,该类公开 Row Column 属性,以及

  3. 将每个包装好的项目添加到私有集合中,这是我们实际上绑定到控件中的内容。

  1. create a ColumnDefinition for each item,
  2. wrap each item inside another class that exposes Row and Column properties, and
  3. add each wrapped item to a private collection, which is what we actually bind to in our control.

首先, XAML:

<UserControl x:Class="WpfApplication1.MyControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d" 
             x:Name="ThisControl"
             d:DesignHeight="300" d:DesignWidth="300">
    <ItemsControl x:Name="ItemsControl"
                  ItemsSource="{Binding BindableItems, ElementName=ThisControl, Mode=OneWay}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid IsItemsHost="True" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="{x:Type ContentPresenter}">
                <Setter Property="Grid.Row" Value="{Binding Row}" />
                <Setter Property="Grid.Column" Value="{Binding Column}" />
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <ContentPresenter Content="{Binding Content}"
                                              ContentTemplate="{Binding ItemTemplate, ElementName=ThisControl}" />
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>
</UserControl>

和后面的代码:

using System.Collections;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;

namespace WpfApplication1
{
  public partial class MyControl : UserControl
  {
    public MyControl()
    {
      InitializeComponent();
    }

    public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
      "ItemsSource", typeof (IEnumerable), typeof (MyControl), 
      new PropertyMetadata(default(IEnumerable), OnItemsSourceChanged));

    public IEnumerable ItemsSource
    {
      get { return (IEnumerable) GetValue(ItemsSourceProperty); }
      set { SetValue(ItemsSourceProperty, value); }
    }

    // This is the DataTemplate that the consumer of your control specifies
    public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register(
      "ItemTemplate", typeof (DataTemplate), typeof (MyControl), new PropertyMetadata(default(DataTemplate)));

    public DataTemplate ItemTemplate
    {
      get { return (DataTemplate) GetValue(ItemTemplateProperty); }
      set { SetValue(ItemTemplateProperty, value); }
    }

    // This is declared private, because it is only to be consumed by this control
    private static readonly DependencyProperty BindableItemsProperty = DependencyProperty.Register(
      "BindableItems", typeof (ObservableCollection<object>), typeof (MyControl), new PropertyMetadata(new ObservableCollection<object>()));

    private ObservableCollection<object> BindableItems
    {
      get { return (ObservableCollection<object>) GetValue(BindableItemsProperty); }
      set { SetValue(BindableItemsProperty, value); }
    }

    private static void OnItemsSourceChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
    {
      var myControl = dependencyObject as MyControl;
      if (myControl == null)
      {
        return;
      }

      // Get reference to the Grid using reflection. You could also walk the tree.
      var grid = (Grid) typeof (ItemsControl).InvokeMember("ItemsHost",
        BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance,
        null, myControl.ItemsControl, null);

      var columns = grid.ColumnDefinitions;
      columns.Clear();
      myControl.BindableItems.Clear();

      var items = args.NewValue as IEnumerable;
      if (items != null)
      {
        var columnIndex = 0;
        foreach (var item in items)
        {
          columns.Add(new ColumnDefinition{ Width = GridLength.Auto });
          var container = new MyItem
          {
            Row = columnIndex,
            Column = columnIndex++,
            Content = item
          };
          myControl.BindableItems.Add(container);
        }
      }
    }
  }

  public class MyItem
  {
    public object Content { get; set; }
    public int Row { get; set; }
    public int Column { get; set; }
  }
}

这篇关于WPF在代码隐藏中应用数据模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-27 14:52