使用依赖属性在自定义类中设置属性
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" />
但是,Name
和 Age
是在 Person
类上定义的常规 CLR 属性,它不是 DependencyObject
(因此甚至无法定义上述依赖属性) .
<local:MyUserControl Person1.Name="Binding SomeNameInViewModel"
Person1.Age="Biding SomeAgeInViewModel"/>
当然,您可以在 MyUserControl
上公开显式的 Name
和 Age
依赖属性,而不是 Person1
,但您甚至不必这样做。
您可以简单地从您的视图模型中绑定一个Person
实例并绑定它的Name
和Age
属性。如果您以双向模式绑定它们,它们将被正确更新。根本不需要定义其他依赖属性或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
字段,然后提供GetDock
和SetDock
方法作为附加属性的公共访问器。
【讨论】:
感谢@thatguy 提供如此详细的答案。在我的帖子中,我忽略了所有 INotificationPropertyChanged,因为它使示例代码变得混乱。我会考虑绑定到 Person1 依赖属性。这是一个很好的解决方案,但不是我实际问题的最佳解决方案。【参考方案2】:我开始思考这是否可能
你是对的。 不可能在 XAML 中设置由Person1
依赖属性返回的Person
对象的Name
属性,如下所示:
<local:MyUserControl
Person1.Name="Binding SomeNameInViewModel" />
但后来我想到了 DockPanel
DockPanel.Dock
是一个attached property。您没有设置从DockPanel
对象返回的对象的Dock
属性。您正在设置附加的 Dock
属性。
因此,如果您希望能够在 XAML 标记中设置 Person1.Name
和 Person1.Age
,则应在称为 Person1
的类型中定义两个单独的附加属性,称为 Name
和 Age
。
您无法使用单个 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 组件中设置默认属性值
在自定义 uitableviewcell 中设置图像会导致延迟