C# 从父用户控件wpf绑定到嵌套对象
我正在编写一个新的用户控件。它需要能够显示可观察的项目集合。这些项的属性也是一个可观察的集合,因此它类似于二维锯齿阵列。控件类似于文本编辑器,因此外部集合是行,内部集合是单词。我希望控件的使用者不仅能够指定行的绑定,还能够指定单词的绑定。到目前为止,我的做法如下:C# 从父用户控件wpf绑定到嵌套对象,c#,wpf,xaml,C#,Wpf,Xaml,我正在编写一个新的用户控件。它需要能够显示可观察的项目集合。这些项的属性也是一个可观察的集合,因此它类似于二维锯齿阵列。控件类似于文本编辑器,因此外部集合是行,内部集合是单词。我希望控件的使用者不仅能够指定行的绑定,还能够指定单词的绑定。到目前为止,我的做法如下: public class ExampleClass { ObservableCollection<InnerClass> InnerItems {get; private set;} } public class
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;}
}
<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。在此控件内,它有一个嵌套的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 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;}
}
<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>
公共类示例类
{
ObservableCollection InnerItems{get;private set;}
}
公共类InnerClass:BaseModel//声明OnPropertyChanged
{
私有字符串\u名称;
公共字符串名称//这是作为示例属性提供的,不是必需的
{
得到
{
返回_name;
}
设置
{
_名称=值;
OnPropertyChanged(Name of(Name));
}
}
....
}
公共类视图模型
{
公共可观测集合项{get;private set;}
}
XAML声明如下:
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;}
}
<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>
最后,我希望我创建的用户控件能够在外部items级别提供ItemsControl模板,但我希望用户能够在内部items控件级别提供datatemplate。我希望控件的使用者能够为外部项和嵌套项提供绑定。我能够想出一个适合我的解决方案。也许有更好的方法,但我就是这么做的 首先,在外部ItemsControl上,我订阅了ItemContainerGenerator的StatusChanged。在该函数中,我应用ContentPresenter的模板,然后搜索内部ItemsControl。找到后,我使用属性NestedItems绑定到ItemsSource属性。我最初遇到的问题之一是绑定不正确。我修复了这个问题,并将NestedItems更改为字符串。此外,我还添加了一个名为NestedDataTemplate的新属性,该属性的类型为DataTemplate,以便用户可以指定内部items控件的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);
}
}
}
public static readonly dependencProperty nestedemsproperty=dependencProperty.Register(“NestedItems”、typeof(string)、typeof(TextDocumentEditor),
新属性元数据((字符串)null));
public static readonly dependencProperty NestedDataTemplateProperty=dependencProperty.Register(“NestedDataTemplate”、typeof(DataTemplate)、typeof(TextDocumentEditor),
新的PropertyMetadata((DataTemplate)null));
公共数据模板嵌套数据模板
{
获取{return(DataTemplate)GetValue(NestedDataTemplateProperty);}
设置
{
SetValue(NestedDataTemplateProperty,值);
}
}
公共字符串嵌套
{
获取{return(string)GetValue(nestedemsproperty);}
设置
{
SetValue(NestedItemsProperty,value);
}
}
私有void ItemContainerGenerator_状态已更改(对象发送者,事件参数e)
{
if(((ItemContainerGenerator)sender).Status!=GeneratorStatus.ContainerGenerated)
返回;
ContentPresenter值;
ItemsControl ItemsControl;
对于(int x=0;xYes,首先,在创建用户控件时,你不应该使用用户控件以外的任何东西……你应该从高层角度解释你试图实现的目标,而不是问如何实现“技术XXX”的目标,人们可能会勾勒出另一种方法,因为它非常不清楚/混乱。此外,还有一个simple/让用户指定模板的有效机制:请参阅参考资料字典和WPF主题定义。我添加了一些更详细的信息,希望能让它更清楚。我可以将用户控件重新创建为自定义控件,只是第一次将其实现为用户控件更容易。我能够找到如何实现的方法,我将永远发布答案别人的利益。