【问题标题】:binding to nested object from the parent user control wpf从父用户控件 wpf 绑定到嵌套对象
【发布时间】:2015-12-28 19:06:34
【问题描述】:

我正在编写一个新的用户控件。它需要能够显示项目的 ObservableCollection。这些项目将具有一个也是可观察集合的属性,因此它类似于二维锯齿数组。该控件类似于文本编辑器,因此外部集合是行,内部集合是单词。我希望控件的使用者不仅能够指定线条的绑定,还能够指定单词的绑定。我到目前为止的方法如下:

用户控件继承自 ItemsControl。在此控件内部,它有一个嵌套的 ItemsControl。我希望能够从父用户控件指定此嵌套 ItemsControl 的绑定路径。 UserControl 的 XAML 是

<ItemsControl x:Class="IntelliDoc.Client.Controls.TextDocumentEditor"
          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" 
          xmlns:local="clr-namespace:IntelliDoc.Client"
          xmlns:con="clr-namespace:IntelliDoc.Client.Controls"
          xmlns:data="clr-namespace:IntelliDoc.Data;assembly=IntelliDoc.Data"
          xmlns:util="clr-namespace:IntelliDoc.Client.Utility"
          xmlns:vm="clr-namespace:IntelliDoc.Client.ViewModel"
          xmlns:sys="clr-namespace:System;assembly=mscorlib"
          xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
          mc:Ignorable="d"
          x:Name="root"
          d:DesignHeight="300" d:DesignWidth="300"
         >
<ItemsControl.Template>
    <ControlTemplate>
        <StackPanel Orientation="Vertical">
            <ItemsPresenter Name="PART_Presenter" />
        </StackPanel>
    </ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
    <DataTemplate >
        <StackPanel Orientation="Horizontal">
            <ItemsControl Name="PART_InnerItemsControl" ItemsSource="{Binding NestedBinding, ElementName=root}" >
                <ItemsControl.Template>
                    <ControlTemplate>
                        <StackPanel Name="InnerStackPanel" Orientation="Horizontal" >
                            <TextBox Text="" BorderThickness="0" TextChanged="TextBox_TextChanged" />
                            <ItemsPresenter />
                        </StackPanel>
                    </ControlTemplate>
                </ItemsControl.Template>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal" >
                            <ContentControl Content="{Binding Path=Data, Mode=TwoWay}" />
                            <TextBox BorderThickness="0" TextChanged="TextBox_TextChanged" />
                        </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
    </DataTemplate>
</ItemsControl.ItemTemplate>

后面的代码声明了这个属性

public partial class TextDocumentEditor : ItemsControl
{


    public static readonly DependencyProperty NestedItemsProperty = DependencyProperty.Register("NestedItems", typeof(BindingBase), typeof(TextDocumentEditor),
        new PropertyMetadata((BindingBase)null));

    public BindingBase NestedItems
    {
        get { return (BindingBase)GetValue(NestedItemsProperty); }
        set
        {
            SetValue(NestedItemsProperty, value);
        }
    }
...
}

预期的绑定对象如下:

public class ExampleClass
{
    ObservableCollection<InnerClass> InnerItems {get; private set;}
}

public class InnerClass : BaseModel //declares OnPropertyChanged
{
   private string _name;
   public string Name //this is provided as an example property and is not required
   {
       get
       {
         return _name;
       } 
       set
       {
          _name = value;
          OnPropertyChanged(nameof(Name));
       }
   }
....
}

public class ViewModel
{
   public ObservableCollection<ExampleClass> Items {get; private set;}
}

XAML 声明如下:

<Window x:Class="IntelliDoc.Client.TestWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    mc:Ignorable="d"
    Title="TestWindow" Height="300" Width="300">
<DockPanel>
    <TextDocumentEditor ItemsSource="{Binding Path=Items}" NestedItems={Binding Path=InnerItems} >
      <DataTemplate>
         <!-- I would like this to be the user defined datatemplate for the nested items.  Currently I am just declaring the templates in the resources of the user control by DataType which also works -->
      </DataTemplate>
    </TextDocumentEditor>
</DockPanel>

最后,我希望我创建的用户控件在外部项目级别提供 ItemsControl 模板,但我希望用户能够在内部项目控制级别提供数据模板。我希望控件的使用者能够为外部项和嵌套项提供绑定。

【问题讨论】:

  • 是的,首先,在创建 UserControl 时,您不应使用除 UserControl 之外的任何东西......您应该从高层次的角度解释您想要实现的目标,而不是询问如何实现“技术XXX”的事情,人们可能会勾勒出另一种方法而不是你的方法,因为它很不清楚/令人困惑。此外,还有一种简单/有效的机制可以让用户指定模板:查看 WPF 的资源字典和主题化。
  • 我添加了更多细节,希望能说明问题。我可以将用户控件重新创建为自定义控件,首先将其实现为用户控件更容易。我能够弄清楚如何做到这一点,我将发布答案以供其他人受益。

标签: c# wpf xaml


【解决方案1】:

我能够想出一个适合我的解决方案。可能有更好的方法,但这就是我所做的。

首先,在外部 ItemsControl 上,我订阅了 ItemContainerGenerator 的 StatusChanged。在该函数中,我应用 ContentPresenter 的模板,然后搜索 Inner ItemsControl。找到后,我使用属性 NestedItems 绑定到 ItemsSource 属性。我最初遇到的问题之一是我绑定不正确。我解决了这个问题,并将 NestedItems 更改为字符串。此外,我添加了一个名为 NestedDataTemplate 的新属性,该属性的类型为 DataTemplate,以便用户可以指定内部项目控件的 DataTemplate。有人建议我不要使用 UserControl,因为我不是从 UserControl 继承的,所以我将其更改为 CustomControl。代码更改如下

    public static readonly DependencyProperty NestedItemsProperty = DependencyProperty.Register("NestedItems", typeof(string), typeof(TextDocumentEditor),
        new PropertyMetadata((string)null));


    public static readonly DependencyProperty NestedDataTemplateProperty = DependencyProperty.Register("NestedDataTemplate", typeof(DataTemplate), typeof(TextDocumentEditor),
        new PropertyMetadata((DataTemplate)null));

    public DataTemplate NestedDataTemplate
    {
        get { return (DataTemplate)GetValue(NestedDataTemplateProperty); }
        set
        {
            SetValue(NestedDataTemplateProperty, value);
        }
    }

    public string NestedItems
    {
        get { return (string)GetValue(NestedItemsProperty); }
        set
        {
            SetValue(NestedItemsProperty, value);
        }
    }

    private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
    {
        if (((ItemContainerGenerator)sender).Status != GeneratorStatus.ContainersGenerated)
            return;
        ContentPresenter value;
        ItemsControl itemsControl;
        for (int x=0;x<ItemContainerGenerator.Items.Count; x++)
        {
            value = ItemContainerGenerator.ContainerFromIndex(x) as ContentPresenter;
            if (value == null)
                continue;
            value.ApplyTemplate();
            itemsControl = value.GetChildren<ItemsControl>().FirstOrDefault();
            if (itemsControl != null)
            {
                if (NestedDataTemplate != null)
                    itemsControl.ItemTemplate = NestedDataTemplate;
                Binding binding = new Binding(NestedItems);
                BindingOperations.SetBinding(itemsControl, ItemsSourceProperty, binding);
            }
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-01
    • 1970-01-01
    相关资源
    最近更新 更多