65.9K
CodeProject 正在变化。 阅读更多。
Home

支持附加到依赖属性的类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (9投票s)

2011年6月16日

CPOL

3分钟阅读

viewsIcon

25308

downloadIcon

262

提供一种连接到外部 DependencyProperty 的方法

引言

有时,当观察者不是DependencyProperty的所有者,并且所有者没有设置任何通知方式时,能够观察到DependencyProperty的更改会很方便。 Microsoft 已经设置了一种使用static方法DependencyProperty.RegisterAttached附加到DependencyProperty的方式,但是,最好将该方法封装在一个类中。

背景

通常,不需要从外部类附加到DependencyProperty更改事件。 如果需要,通常可以通过提供可以订阅的事件来实现观察者模式。 但是,可能需要观察一个类中的属性,而该类无法或不应进行修改以提供此类事件。 依赖属性的优点之一是,可以观察DependencyProperty,而无需自定义包含需要观察的DependencyProperty的类。 我遇到了一个案例,当时我正在创建一个行为来识别悬停状态,并且需要知道控件中包含的按钮何时处于按下状态。 使用DependencyPropertyRegisterAttached方法附加到DependencyProperty非常容易。 但是,由于行为的短暂性,我需要包含清除依赖属性的功能,以防止潜在的内存泄漏。 为了能够清除DependencyProperty,必须维护指向由DependencyProperty.RegisterAttached方法创建的DependencyProperty的指针,即使执行DependencyProperty.RegisterAttached方法的唯一目的是将PropertyChangedCallback附加到现有的DependencyProperty。 但是,要清除DependencyPropertyFrameworkElement.ClearValue(DependencyProperty)),您必须具有对DependencyProperty的引用。 如果该类被static字段引用,则应清除该值以确保没有内存泄漏。

以下是一个使用类的本地实例创建绑定的类。 如果包含想要绑定的DependencyProperty的控件存在差异,这是最佳选择。 其中包含Dispose方法,以确保在取消引用AttachedDependencyProperty时不会发生内存泄漏。

public class AttachedDependencyProperty<T> : IDisposable
{
    DependencyProperty _dependencyProperty;
    FrameworkElement _element;
 
    public AttachedDependencyProperty(string propertyName, FrameworkElement element, 
               PropertyChangedCallback callback)
    {
        _element = element;
 
        //Bind to a dependency property
        _dependencyProperty = DependencyProperty.RegisterAttached("Attached" + propertyName,
                            typeof(T), typeof(object), new PropertyMetadata(callback));
        element.SetBinding(_dependencyProperty,  new Binding(propertyName) 
			{ Source = element });
    }
 
    public void Dispose()
    {
        _element.ClearValue(_dependencyProperty);
    }
}

如果类的所有实例都绑定到同一属性,则可以使用以下类。 注意不要从使用实例中将此用于两个不同的控件,因为在此实现中没有办法区分两个不同的属性。

public class AttachedDependencyPropertyStatic<T>
{
    DependencyProperty _dependencyProperty;
    string _propertyName;
 
    public AttachedDependencyPropertyStatic
	(string propertyName, PropertyChangedCallback callback)
    {
        _propertyName = propertyName;
        //Bind to a dependency property
        _dependencyProperty = DependencyProperty.RegisterAttached("Attached" + propertyName,
                            typeof(T), typeof(object), new PropertyMetadata(callback));
    }
 
    public void Add (FrameworkElement element)
    {
        Binding b = new Binding(_propertyName) { Source = element };
        element.SetBinding(_dependencyProperty, b);
    }
 
    public void Dispose(FrameworkElement element)
    {
        element.ClearValue(_dependencyProperty);
    }
}

请注意,在这两种情况下,我都使用typeof(object)而不是使用特定的类型。 typeof(object)可以正常工作,这意味着类需要传递的信息更少。

Using the Code

我创建了一个非常简单的应用程序来演示如何使用两个DependencyProperty类。 在主页中,我附加到按钮的“IsPressed”属性,然后仅显示上次按下或释放按钮的日期/时间。 这是一个人为设计的示例,因为捕获按钮向上(MouseLeftButtonUp)和向下(MouseLeftButtonDown)事件非常容易。 当我最初创建此代码时,它是用于一个容器,我必须扫描内容以查找按钮并连接到IsPressed DependencyProperty,以便我可以在按下任何按钮时更改行为。

使用这两个类中的任何一个都非常容易。 以下显示了如何使用实例版本

private AttachedDependencyProperty<bool> property1;

public MainPage()
{
    InitializeComponent();
    property1 = new AttachedDependencyProperty<bool>
		("IsPressed", TestButton1, IsPressedChangedCallback);
}

private void IsPressedChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    …
}

这是一个如何实现静态版本的示例

private static AttachedDependencyPropertyStatic<bool> property2 =
			            new AttachedDependencyPropertyStatic<bool>
					("IsPressed", IsPressedChangedCallbackStatic);
 
public MainPage()
{
    InitializeComponent();
    property2.Add(TestButton2);
}
 
private static void IsPressedChangedCallbackStatic(DependencyObject d, 
DependencyPropertyChangedEventArgs e)
{
    …
}

关注点

如果您查看DependencyProperty代码,您会看到我在注册DependencyProperty时并不担心所有者的类型。 我使用对象类型。 这似乎没有任何问题,我不确定为什么 Microsoft 要求所有者类型。

确保在附加到DependencyProperty时使用正确的类型。 如果使用了错误的类型,那么似乎不会捕获任何事件,并且没有迹象表明为什么会发生这种情况。

历史

  • 2011 年 6 月 15 日:初始版本
© . All rights reserved.