公开内部控件属性以在 WPF 中进行绑定 [英] Exposing inner Control properties for binding in WPF

查看:28
本文介绍了公开内部控件属性以在 WPF 中进行绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

:我自己想出了如何做到这一点.我发布了我的解决方案,希望它可以节省其他人几天的谷歌搜索.如果您是 WPF 专家,请查看我的解决方案,让我知道是否有更好/更优雅/更有效的方法来做到这一点.特别是,我有兴趣知道我不知道的东西......这个解决方案将如何让我陷入困境?问题归结为暴露内部控件属性.

问题:我正在创建一些代码以在 WPF 中为 XML 文件自动生成数据绑定 GUI.我有一个 xsd 文件,可以帮助我确定节点类型等.简单的 Key/Value 元素很容易.

Problem: I am creating some code to auto-generate a data-bound GUI in WPF for an XML file. I have an xsd file that can help me determine the node types, etc. Simple Key/Value elements are easy.

当我解析这个元素时:

<Key>value</Key>

我可以创建一个新的KeyValueControl"并将 DataContext 设置为此元素.KeyValueControl 被定义为一个 UserControl 并且只是有一些简单的绑定.它适用于任何简单的 XElement.

I can create a new 'KeyValueControl' and set the DataContext to this element. The KeyValueControl is defined as a UserControl and just has some simple bindings on it. It works great for any simple XElement.

此控件中的 XAML 如下所示:

The XAML inside this control looks like this:

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

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

The result is a line that has a label with the element name and a text box with the value that I can edit.

现在,有时我需要显示查找值而不是实际值.我想创建一个类似于上述 KeyValueControl 的KeyValueComboBox",但能够指定(基于文件中的信息)ItemsSourceDisplayMemberPathValueMemberPath.'DisplayMemberPath' 和 'ValueMemberPath' 绑定将与 KeyValueControl 相同.

Now, there are times where I need to display lookup values instead of the actual value. I would like to create a 'KeyValueComboBox' similar to the above KeyValueControl but be able to specify (based on information in the file) the ItemsSource, DisplayMemberPath, and ValueMemberPath. The 'DisplayMemberPath' and 'ValueMemberPath' bindings would be the same as the KeyValueControl.

我不知道标准用户控件是否可以处理这个问题,或者我是否需要从 Selector 继承.

I don't know if a standard user control can handle this, or if I need to inherit from Selector.

控件中的 XAML 如下所示:

The XAML in the control would look something like this:

<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]/>

在我的代码中,我会做这样的事情(假设这个节点是一个事物"并且需要显示一个事物列表以便用户可以选择 ID:

In my code, I would then do something like this (assuming that this node is a 'Thing' and needs to display a list of Things so the user can select the ID:

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

所以我的问题是:

1) 我应该从 Control 还是 Selector 继承我的 KeyValueComboBox?

1) Should I inherit my KeyValueComboBox from Control or Selector?

2) 如果我应该从 Control 继承,我如何公开内部 Combo Box 的 ItemsSourceDisplayMemberPathValueMemberPath 用于绑定?

2) If I should inherit from Control, how do I expose the inner Combo Box's ItemsSource, DisplayMemberPath, and ValueMemberPath for binding?

3)如果我需要从 Selector 继承,有人可以提供一个小例子来说明我如何开始使用它吗?再说一次,我是 WPF 的新手,所以如果这是我需要走的路,那么一个不错的简单示例将非常有帮助.

3) If I need to inherit from Selector, can someone provide a small example of how I might get started with that? Again, I'm new to WPF so a nice, simple example would really help if that's the road I need to take.

推荐答案

我最终想出了如何自己做这件事.我在这里发布答案,以便其他人可以看到有效的解决方案,也许 WPF 专家会过来告诉我一个更好/更优雅的方法来做到这一点.

I ended up figuring how how to do this on my own. I'm posting the answer here so that others can see a solution that works, and maybe a WPF guru will come by and show me a better/more elegant way to do this.

所以,答案最终是#2.暴露内部属性被证明是正确的答案.设置它实际上很容易......一旦你知道如何去做.没有太多完整的例子(我能找到),所以希望这个例子能帮助遇到这个问题的其他人.

So, the answer ended up being #2. Exposing the inner properties turns out to be the right answer. Setting it up is actually pretty easy.. once you know how to do it. There aren't many complete examples of this (that I could find), so hopefully this one will help someone else that runs into this problem.

ComboBoxWithLabel.xaml.cs

此文件中重要的一点是 DependencyProperties 的使用.请注意,我们现在所做的只是公开属性(LabelContentItemsSource).XAML 将负责将内部控件的属性连接到这些外部属性.

The important thing in this file is the use of DependencyProperties. Note that all we're doing right now is just exposing the properties (LabelContent and ItemsSource). The XAML will take care of wiring the internal control's properties to these external properties.

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 文件中声明属性(如上),然后使用 VS2010 设计器从属性窗格设置绑定源.本质上,这是我所知道的将内部控件的属性绑定到基本控件的唯一方法.如果有更好的方法,请告诉我.

The XAML is pretty straightforward, with the exception of the bindings on the Label and the ComboBox ItemsSource. I found that the easiest way to get these bindings right is to declare the properties in the .cs file (as above) and then use the VS2010 designer to setup the binding source from the properties pane. Essentially, this is the only way I know of to bind an inner control's properties to the base control. If there's a better way to do it, please let me know.

<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 技术设置 ItemsSourceLabelContent.

The XAML to use this is not interesting at all.. which is exactly what I wanted. You can set the ItemsSource and the LabelContent via all the standard WPF techniques.

<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

For Completeness Sake, here is the 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();

    }
}

这篇关于公开内部控件属性以在 WPF 中进行绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆