使用依赖属性在自定义类中设置属性

Posted

技术标签:

【中文标题】使用依赖属性在自定义类中设置属性【英文标题】:Set property in custom class with dependency properties 【发布时间】:2021-07-08 04:46:42 【问题描述】:

我有一个带有一些属性的自定义类。

public class Person

    public string Name  get; set; 
    public int Age  get; set; 

UserControl 中,我向自定义类添加了一个依赖属性。

public class MyUserControl : UserControl

    public static readonly DependencyProperty Person1Property = DependencyProperty.Register
    (
        nameof(Person1),
        typeof(Person),
        typeof(MyUserControl),
        new PropertyMetadata(null, Person1Changed)
    );

    public Person Person1
    
        get  return (Person) GetValue(Person1Property); 
        set  SetValue(Person1Property, value); 
    

    static void Person1Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
    
        if (e.NewValue != e.OldValue && d is MyUserControl)
        
            var instance = d as MyUserControl;
            instance.Person1 = e.NewValue as Person;
        
    

在 MainWindow.xaml 中我想做:

<Window x:Class="MyApplication.MainWindow"
        xmlns:local="clr-namespace:MyApplication"
        Title="MainWindow" Height="450" Width="800">

    <local:MyUserControl
        Person1.Name="Binding SomeNameInViewModel"
        Person1.Age="Biding SomeAgeInViewModel"
    />

</Window>

但这不起作用:( 我只能这样做:

<Window>
    <local:MyUserControl Person1="Binding Person1InViewModel" />
</Window>

我开始思考这是否可能。 但后来我想到了 DockPanel:

<DockPanel>
    <Label DockPanel.Dock="Left" />
    <Label DockPanel.Dock=Right" />
</DockPanel>

是否有通过依赖属性访问 XAML 中的类属性的解决方案?

【问题讨论】:

为什么在视图模式下没有 Person 属性?顺便说一句:在 Person1Changed 中设置 instance.Person1 根本没有用 - DP 已经设置为新值。 ***.com/a/1483837/38368 这能回答你的问题吗? WPF Binding to local variable 感谢您的回复!我已经知道如何使用 INotificationPropertychanged 接口。我在帖子中忽略了它的所有引用,以免弄乱示例。 【参考方案1】:

绑定不起作用,因为绑定目标must be a dependency property。

目标属性必须是依赖属性。大多数UIElement属性是依赖属性,大多数依赖属性,除了只读属性,默认支持数据绑定。 (只有派生自DependencyObject 的类型可以定义依赖属性;所有UIElement 类型派生自DependencyObject。)

由于 Person1Property 及其 getter 和 setter 是一个依赖属性,因此它有效。

<local:MyUserControl Person1="Bidning AnyPersonInstance" />

但是,NameAge 是在 Person 类上定义的常规 CLR 属性,它不是 DependencyObject(因此甚至无法定义上述依赖属性) .

<local:MyUserControl Person1.Name="Binding SomeNameInViewModel"
                     Person1.Age="Biding SomeAgeInViewModel"/>

当然,您可以在 MyUserControl 上公开显式的 NameAge 依赖属性,而不是 Person1,但您甚至不必这样做。

您可以简单地从您的视图模型中绑定一个Person 实例并绑定它的NameAge 属性。如果您以双向模式绑定它们,它们将被正确更新。根本不需要定义其他依赖属性或Person1Changed,因为那时它是多余的。在任何情况下,如果您绑定其属性,您应该在 Person 类型中添加 implement the INotifyPropertyChanged interface,以便用户界面收到这些属性更改的通知并更新绑定。

<Label DockPanel.Dock="Left" />

关于Dock属性,这就是所谓的attached property,用途不同。

附加属性是 XAML 定义的概念。附加属性旨在用作一种可在任何对象上设置的全局属性。在 Windows Presentation Foundation (WPF) 中,附加属性通常定义为一种特殊形式的依赖属性,它没有常规属性“包装器”。

附加属性的一个目的是允许不同的子元素为父元素中定义的属性指定唯一值。此场景的一个特定应用是让子元素通知父元素它们将如何在用户界面 (UI) 中呈现。一个例子是DockPanel.Dock 属性。 DockPanel.Dock 属性被创建为附加属性,因为它被设计为设置在包含在 DockPanel 中的元素上,而不是设置在 DockPanel 本身上。 DockPanel 类定义名为 DockProperty 的静态 DependencyProperty 字段,然后提供 GetDockSetDock 方法作为附加属性的公共访问器。

【讨论】:

感谢@thatguy 提供如此详细的答案。在我的帖子中,我忽略了所有 INotificationPropertyChanged,因为它使示例代码变得混乱。我会考虑绑定到 Person1 依赖属性。这是一个很好的解决方案,但不是我实际问题的最佳解决方案。【参考方案2】:

我开始思考这是否可能

你是对的。 不可能在 XAML 中设置由Person1 依赖属性返回的Person 对象的Name 属性,如下所示:

<local:MyUserControl
    Person1.Name="Binding SomeNameInViewModel" />

但后来我想到了 DockPanel

DockPanel.Dock 是一个attached property。您没有设置从DockPanel 对象返回的对象的Dock 属性。您正在设置附加的 Dock 属性。

因此,如果您希望能够在 XAML 标记中设置 Person1.NamePerson1.Age,则应在称为 Person1 的类型中定义两个单独的附加属性,称为 NameAge

您无法使用单个 Person 依赖属性来解决此问题。

【讨论】:

@Mc_Topaz:如果您的问题已经解决,请accept the answer。【参考方案3】:

我不知道这对你有多大帮助,但你可以使用一个特殊的外部类来绑定到 CLR 属性。

类似类的一个例子:

using System.Windows;

namespace Common.Proxy

    public class ProxyBridge : Freezable
    
        protected override Freezable CreateInstanceCore()
            => new ProxyBridge();


        /// <summary>The binding source.</summary>
        public object Source
        
            get  return (object)GetValue(SourceProperty); 
            set  SetValue(SourceProperty, value); 
        

        /// <summary><see cref="DependencyProperty"/> for property <see cref="Source"/>.</summary>
        public static readonly DependencyProperty SourceProperty =
            DependencyProperty.Register(nameof(Source), typeof(object), typeof(ProxyBridge), new PropertyMetadata(null, SourceChanged));

        private static void SourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        
            ((ProxyBridge)d).Target = e.NewValue;
        


        /// <summary>The binding target.</summary>
        public object Target
        
            get  return (object)GetValue(TargetProperty); 
            set  SetValue(TargetProperty, value); 
        

        /// <summary><see cref="DependencyProperty"/> для свойства <see cref="Target"/>.</summary>
        public static readonly DependencyProperty TargetProperty =
            DependencyProperty.Register(nameof(Target), typeof(object), typeof(ProxyBridge), new PropertyMetadata(null));


    

其使用示例:

<UniformGrid Columns="2">
    <FrameworkElement.Resources>
        <local:Person x:Key="person" Name="Alex" Age="35"/>
        <proxy:ProxyBridge x:Key="bridgeName"
                           Target="Binding Name, Source=StaticResource person, Mode=OneWayToSource"
                           Source="Binding Text, ElementName=tbName"/>
        <proxy:ProxyBridge x:Key="bridgeAge" 
                           Target="Binding Age, Source=StaticResource person, Mode=OneWayToSource"
                           Source="Binding Text, ElementName=tbAge"/>
    </FrameworkElement.Resources>
    <TextBlock Text="Binding Name, Source=StaticResource person"/>
    <TextBlock Text="Binding Age, Source=StaticResource person"/>
    <TextBlock Text="Binding Target, Source=StaticResource bridgeName"/>
    <TextBlock Text="Binding Target, Source=StaticResource bridgeAge"/>
    <TextBox x:Name="tbName"/>
    <TextBox x:Name="tbAge"/>
</UniformGrid>

此类具有应用程序功能。

如果您对这样的解决方案感兴趣,那么我会写下它们。

【讨论】:

以上是关于使用依赖属性在自定义类中设置属性的主要内容,如果未能解决你的问题,请参考以下文章

Delphi 2009 - 在自定义 Delphi 组件中设置默认属性值

Symfony 验证 - 在自定义验证行为中设置属性路径

在自定义单元格中设置自定义附件类型

在自定义 uitableviewcell 中设置图像会导致延迟

在自定义 UIview 中设置 UItableview 的委托和数据源

如何在自定义单元格中设置详细配件类型