公开WPF中绑定的内部控件属性

[编辑]:我想出了如何自己做这个。 我发布了我的解决scheme,希望能在几天的Googlesearch中省掉其他人。 如果你是一个WPF大师,请看看我的解决scheme,让我知道如果有一个更好/更优雅/更有效的方式来做到这一点。 特别是,我有兴趣知道我不知道什么…这个解决scheme将如何把我拧在路上? 这个问题真的归结为暴露内部控制属性。

问题:我正在创build一些代码来为WPF中的XML文件自动生成一个数据绑定的GUI。 我有一个xsd文件,可以帮助我确定节点types等。简单的键/值元素很容易。

当我parsing这个元素时:

<Key>value</Key> 

我可以创build一个新的“KeyValueControl”并将DataContext设置为此元素。 KeyValue被定义为一个UserControl,并且只有一些简单的绑定。 它适用于任何简单的XElement。

这个控件中的XAML如下所示:

 <Label Content={Binding Path=Name} /> <TextBox Text={Binding Path=Value} /> 

结果是一行有一个带有元素名称的标签和一个可以编辑的值的文本框。

现在,有时我需要显示查找值而不是实际的值。 我想创build一个类似于上述KeyValueControl的“KeyValueComboBox”,但是能够指定(根据文件中的信息)ItemsSource,Display和Valuepath。 “名称”和“值”绑定将与KeyValueControl相同。

我不知道如果一个标准的用户控件可以处理这个,或者如果我需要从select器inheritance。

控件中的XAML看起来像这样:

 <Label Content={Binding Path=Name} /> <ComboBox SelectedValue={Binding Path=Value} ItemsSource={Binding [BOUND TO THE ItemsSource PROPERTY OF THIS CUSTOM CONTROL] DisplayMemberPath={Binding [BOUND TO THE DisplayMemberPath OF THIS CUSTOM CONTROL] SelectedValuePath={Binding [BOUND TO THE SelectedValuePath OF THIS CUSTOM CONTROL]/> 

在我的代码中,我会做这样的事情(假设这个节点是'东西',需要显示一个东西的列表,所以用户可以selectID:

 var myBoundComboBox = new KeyValueComboBox(); myBoundComboBox.ItemsSource = getThingsList(); myBoundComboBox.DisplayMemberPath = "ThingName"; myBoundComboBox.ValueMemberPath = "ThingID" myBoundComboBox.DataContext = thisXElement; ... myStackPanel.Children.Add(myBoundComboBox) 

所以我的问题是:

1)我应该inheritance我的KeyValueComboBox从控制或select?

2)如果我应该从Controlinheritance,如何公开内部combobox的ItemsSource,DisplayMemberPath和ValueMemberPath进行绑定?

3)如果我需要从Selectorinheritance,有人可以提供一个小例子来说明如何开始? 再一次,我是WPF的新手,所以如果这是我需要采取的道路,一个很好的简单的例子真的会有所帮助。

我最终决定如何自己做到这一点。 我在这里发布的答案,以便其他人可以看到一个解决scheme的作品,也许WPF大师会来,并向我展示一个更好/更优雅的方式来做到这一点。

所以,答案最终是#2。 公开内部属性certificate是正确的答案。 设置它实际上很简单..一旦你知道如何做到这一点。 这个(我可以find)没有很多完整的例子,所以希望这个能帮助别人解决这个问题。

ComboBoxWithLabel.xaml.cs

这个文件中重要的是使用了DependencyProperties。 请注意,我们现在正在做的只是暴露属性(LabelContent和ItemsSource)。 XAML将负责将内部控制的属性连接到这些外部属性。

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Collections; namespace BoundComboBoxExample { /// <summary> /// Interaction logic for ComboBoxWithLabel.xaml /// </summary> public partial class ComboBoxWithLabel : UserControl { // Declare ItemsSource and Register as an Owner of ComboBox.ItemsSource // the ComboBoxWithLabel.xaml will bind the ComboBox.ItemsSource to this // property public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } public static readonly DependencyProperty ItemsSourceProperty = ComboBox.ItemsSourceProperty.AddOwner(typeof(ComboBoxWithLabel)); // Declare a new LabelContent property that can be bound as well // The ComboBoxWithLable.xaml will bind the Label's content to this public string LabelContent { get { return (string)GetValue(LabelContentProperty); } set { SetValue(LabelContentProperty, value); } } public static readonly DependencyProperty LabelContentProperty = DependencyProperty.Register("LabelContent", typeof(string), typeof(ComboBoxWithLabel)); public ComboBoxWithLabel() { InitializeComponent(); } } } 

ComboBoxWithLabel.xaml

Xaml非常简单,除了Label和ComboBox ItemsSource上的绑定之外。 我发现获得这些绑定的最简单方法就是在.cs文件中声明属性(如上),然后使用VS2010devise器从属性窗格设置绑定源。 实质上,这是我知道将内部控件的属性绑定到基本控件的唯一方法。 如果还有更好的办法,请告诉我。

 <UserControl x:Class="BoundComboBoxExample.ComboBoxWithLabel" 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" d:DesignHeight="28" d:DesignWidth="453" xmlns:my="clr-namespace:BoundComboBoxExample"> <Grid> <DockPanel LastChildFill="True"> <!-- This will bind the Content property on the label to the 'LabelContent' property on this control--> <Label Content="{Binding Path=LabelContent, RelativeSource={RelativeSource FindAncestor, AncestorType=my:ComboBoxWithLabel, AncestorLevel=1}}" Width="100" HorizontalAlignment="Left"/> <!-- This will bind the ItemsSource of the ComboBox to this control's ItemsSource property --> <ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:ComboBoxWithLabel, AncestorLevel=1}, Path=ItemsSource}"></ComboBox> <!-- you can do the same thing with SelectedValuePath, DisplayMemberPath, etc, but this illustrates the technique --> </DockPanel> </Grid> </UserControl> 

MainWindow.xaml

使用这个XAML根本就没什么意思..这正是我想要的。 您可以通过所有标准的WPF技术设置ItemsSource和LabelContent。

 <Window x:Class="BoundComboBoxExample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="86" Width="464" xmlns:my="clr-namespace:BoundComboBoxExample" Loaded="Window_Loaded"> <Window.Resources> <ObjectDataProvider x:Key="LookupValues" /> </Window.Resources> <Grid> <my:ComboBoxWithLabel LabelContent="Foo" ItemsSource="{Binding Source={StaticResource LookupValues}}" HorizontalAlignment="Left" Margin="12,12,0,0" x:Name="comboBoxWithLabel1" VerticalAlignment="Top" Height="23" Width="418" /> </Grid> </Window> 

完整性清单,这里是MainWindow.xaml.cs

 /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { ((ObjectDataProvider)FindResource("LookupValues")).ObjectInstance = (from i in Enumerable.Range(0, 5) select string.Format("Bar {0}", i)).ToArray(); } } 

我试过你的解决scheme,但对我来说却失败了。 它并没有把价值转嫁到内部控制上。 我所做的是在外部控制中声明相同的依赖性属性,并像这样声明内部到外部:

  // Declare IsReadOnly property and Register as an Owner of TimePicker (base InputBase).IsReadOnly the TimePickerEx.xaml will bind the TimePicker.IsReadOnly to this property // does not work: public static readonly DependencyProperty IsReadOnlyProperty = InputBase.IsReadOnlyProperty.AddOwner(typeof(TimePickerEx)); public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof (bool), typeof (TimePickerEx), new PropertyMetadata(default(bool))); public bool IsReadOnly { get { return (bool) GetValue(IsReadOnlyProperty); } set { SetValue(IsReadOnlyProperty, value); } } 

比在xaml:

  <UserControl x:Class="CBRControls.TimePickerEx" x:Name="TimePickerExControl" ... > <xctk:TimePicker x:Name="Picker" IsReadOnly="{Binding ElementName=TimePickerExControl, Path=IsReadOnly}" ... /> </UserControl>