WPF用户控件,组件元素的访问依赖项属性

时间:2015-10-20 14:22:16

标签: wpf xaml

问题

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

在实现父(用户)控件时,如何在XAML中访问组件(基本或标准)控件的多个依赖项属性,而不创建其他属性

详细信息

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

但是,它存在以下问题:

  • 必须将现有依赖项属性复制为新属性,从而破坏DRY原则。
  • 如果要进行数据绑定,则必须完成更多工作才能将现有依赖项属性绑定到新公开的依赖项属性。

我想知道是否有办法走路"走路"用户控件中的基本控件,用于访问其属性 - 来自XAML。

实施例

例如,我创建了一个继承自UserControl的用户WPF控件。这很简单 - 它由StackPanel组成,其中包含LabelTextBlock

<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内容...虽然我不知道某种方式:

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

非常感谢。

2 个答案:

答案 0 :(得分:0)

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

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

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

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

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:

<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. ViewModel代码:

    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。 TargetControlData代码:

    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. 摘要 - 您可以从配置文件中提取控件属性数据,或通过反射收集它们。
  2. 的问候,

答案 1 :(得分:-1)

你建议的方式 - 我不认为这是可能的。 但它可以使用普通属性而不是依赖属性来完成,例如:

UserControl xaml:

<StackPanel>
    <TextBlock x:Name="tbOne"></TextBlock>
    <TextBlock x:Name="tbTwo" Foreground="Red"></TextBlock>
</StackPanel>

后面的UserControl代码:

    public string One
    {
        get
        {
            return this.tbOne.Text;
        }
        set
        {
            this.tbOne.Text = value;
        }
    }

    public string Two
    {
        get
        {
            return this.tbTwo.Text;
        }
        set
        {
            this.tbTwo.Text = value;
        }
    }

以及用户控件的用法:

    <local:UserControl1 One="test1" Two="test2"></local:UserControl1>