WPF用户控件,访问组件元素的依赖属性 [英] WPF user control, access dependency properties of component elements

查看:46
本文介绍了WPF用户控件,访问组件元素的依赖属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一个用户 WPF 控件由多个标准控件组成.

A user WPF control is made up of multiple standard controls.

在实现父(用户)控件时,如何在 XAML 中访问组件(基本或标准)控件的多个依赖属性,无需创建附加属性?

How can multiple dependency properties of the component (base or standard) controls be accessed in XAML, when implementing the parent (user) control, without creating additional properties?

创建额外的依赖属性"是什么意思?嗯,这是我所知道的访问组件控件属性的唯一方法:通过实现附加属性,如 MSDN 此处.

What do I mean by "creating additional dependency properties"? Well, that is the only way I know of accessing properties of the component controls: by implementing attached properties, as described at MSDN here.

但是,它存在以下问题:

However, it presents the following problems:

  • 必须将现有的依赖属性复制为新的属性,这违背了 DRY 原则.
  • 如果要进行数据绑定,则必须做更多的工作来将现有的依赖属性绑定到新公开的依赖属性.

我想知道是否有办法在用户控件中遍历"基本控件,以访问它们的属性 - 从 XAML 中.

I'm wondering if there is a way to "walk" the base controls within the user control, to access their properties - from within XAML.

例如,我创建了一个从 UserControl 继承的用户 WPF 控件.它很简单 - 它由一个 StackPanel 组成,其中包含一个 Label 和一个 TextBlock:

For example, I make a user WPF control that inherits from UserControl. It is simple - it consists of a StackPanel containing a Label and a TextBlock:

<UserControl x:Class="MyApp.CustomControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <Label Name="BaseLabel">Label Here</Label>
        <TextBlock Name="BaseTextBlock">Some text here.</TextBlock>
    </StackPanel>
</UserControl>

现在,当我在 XAML 的其他地方使用我的 UserControl 时,我一厢情愿地认为可以做这样的事情来编辑我的 Label 的内容......虽然我不知道有什么办法:

Now, when I use my UserControl elsewhere in XAML, I'm wishfully thinking something like this could be done to edit my Label's content... although I don't know of a way:

<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyApp">
    <StackPanel>
        <!-- This won't work, don't try at home kids. -->
        <local:CustomControl BaseLabel.Content="I did it!"></local:CustomControl>
    </StackPanel>
</Window>

非常感谢.

推荐答案

接下来的解决方案如何:1. 创建 AttachedProperty(因为您必须是一个入口点)并将此属性绑定到数据集合.此数据集合将包含您希望对窗口内使用的主用户控件的子控件执行的更改.该集合将在主窗口视图模型中定义.2.在附加属性更改回调中获取绑定集合,将其数据解析为子控件属性.这是解决方案:3.Xaml代码:

How about the next solution: 1. Create the AttachedProperty (because you must an entry point) and bind this property to the collection of data.This collection of data will contain changes you want perform on sub-controls of a main user control used inside the window. This collection will be defined inside the main window view model. 2. In attached property changed callback get the binded collection, parse it data into sub-controls properties. Here is the solution: 3. Xaml code:

<Window.DataContext>
    <nirHelpingOvalButton:MainWindowViewModel />
</Window.DataContext>

<Grid>
    <nirHelpingOvalButton:InnerControl x:Name="MyInnerControl" 
                                       nirHelpingOvalButton:Helper.InnerControlPropertiesAccessor="{Binding InnerData, Mode=Default, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>

4.附加属性代码(bindig 支持):

4. Attached property code (bindig support):

public static readonly DependencyProperty InnerControlPropertiesAccessorProperty = DependencyProperty.RegisterAttached(
        "InnerControlPropertiesAccessor", typeof (ObservableCollection<TargetControlData>), typeof (Helper), new PropertyMetadata(default(ObservableCollection<TargetControlData>), InnerValueAccessProviderPropertyChangedCallback));

    public static void SetInnerControlPropertiesAccessor(DependencyObject element, ObservableCollection<TargetControlData> value)
    {
        element.SetValue(InnerControlPropertiesAccessorProperty, value);
    }

    public static ObservableCollection<TargetControlData> GetInnerControlPropertiesAccessor(DependencyObject element)
    {
        return (ObservableCollection<TargetControlData>) element.GetValue(InnerControlPropertiesAccessorProperty);
    }
    private static void InnerValueAccessProviderPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
    {
        var control = sender as Control;
        if (control == null) return;
        var valuesMap = args.NewValue as ObservableCollection<TargetControlData>;
        if (valuesMap == null)
            return;
        valuesMap.ToList().ForEach(data => TryToBind(control, data));
    }

    private static void TryToBind(Control control, TargetControlData data)
    {
        var innerControl = control.FindName(data.SubControlName) as DependencyObject;
        if (innerControl == null) return;

        var myBinding = new Binding
        {
            Source = data,
            Path = new PropertyPath("Data"),
            Mode = BindingMode.TwoWay,
            UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
        };
        var descriptors = TypeDescriptor.GetProperties(innerControl);
        var propertyDescriptor = descriptors.Find(data.SubConrolProperty, true);
        var descriptor = DependencyPropertyDescriptor.FromProperty(propertyDescriptor);
        if (descriptor == null) return;
        var dependencyProperty = descriptor.DependencyProperty;
        BindingOperations.SetBinding(innerControl, dependencyProperty, myBinding);
    }

5.内部控制xaml:

5. Inner control xaml:

<UserControl x:Class="NirHelpingOvalButton.InnerControl"
         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="300" d:DesignWidth="300">
<UniformGrid>
    <Button x:Name="InnerControlButton"></Button>
    <TextBlock x:Name="InnerContentTextBlock"></TextBlock>
</UniformGrid>

6. 视图模型代码:

    public class MainWindowViewModel:BaseObservableObject
{
    private static int _staticCount = 0;
    private List<Brush> _list = new List<Brush> {Brushes.Green, Brushes.Red, Brushes.Blue};

    public MainWindowViewModel()
    {
        InnerData = new ObservableCollection<TargetControlData>
        {
            new TargetControlData
            {
                SubControlName = "InnerControlButton",
                SubConrolProperty = "Content",
                Data = "Click Me",
            },
            new TargetControlData
            {
                SubControlName = "InnerControlButton",
                SubConrolProperty = "Command",
                Data = new RelayCommand(CommandMethod),
            },
            new TargetControlData
            {
                SubConrolProperty = "Text",
                SubControlName = "InnerContentTextBlock",
                Data = "Hello"
            },
            new TargetControlData
            {
                SubConrolProperty = "Background",
                SubControlName = "InnerContentTextBlock",
                Data = Brushes.Green
            },
            new TargetControlData
            {
                SubConrolProperty = "Foreground",
                SubControlName = "InnerContentTextBlock",
                Data = Brushes.White
            },
        };
    }

    private void CommandMethod()
    {
        _staticCount ++;
        var backgroundData = InnerData.FirstOrDefault(data => data.SubControlName == "InnerContentTextBlock" && data.SubConrolProperty == "Background");
        var textData = InnerData.FirstOrDefault(data => data.SubControlName == "InnerContentTextBlock" && data.SubConrolProperty == "Text");
        if (backgroundData == null || textData == null) return;
        var index = _staticCount%_list.Count;
        backgroundData.Data  = _list[index];
        textData.Data = string.Format("{0} {1}", "Hello", backgroundData.Data);
    }
    public ObservableCollection<TargetControlData> InnerData { get; set; }}

7.目标控制数据代码:

7. TargetControlData code:

    public class TargetControlData:BaseObservableObject
{
    private string _subControlName;
    private string _subConrolProperty;
    private object _data;

    public string SubControlName
    {
        get { return _subControlName; }
        set
        {
            _subControlName = value;
            OnPropertyChanged();
        }
    }

    public string SubConrolProperty
    {
        get { return _subConrolProperty; }
        set
        {
            _subConrolProperty = value;
            OnPropertyChanged();
        }
    }

    public object Data
    {
        get { return _data; }
        set
        {
            _data = value;
            OnPropertyChanged();
        }
    }
}

  1. 总结 - 您可以从配置文件中提取控件属性数据,或通过反射收集它们.

问候,

这篇关于WPF用户控件,访问组件元素的依赖属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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