Files
ShrlAlgoToolkit/AntDesignWPF/Controls/PropertyChangeNotifier.cs
2025-07-31 20:12:24 +08:00

110 lines
4.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace AntDesignWPF.Controls
{
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
/// <summary>
///依赖属性描述符的 AddValueChanged 会导致内存泄漏,如您所知。
///因此,如此处所述,您可以创建自定义类 PropertyChangeNotifier 来监听
///到任何依赖属性更改。
///此类利用绑定使用弱引用来管理关联的事实
///因此,该类不会对属性更改它的对象进行 root 权限。它还使用一个 WeakReference
///以维护对它正在监视其属性的对象的引用,而无需对该对象进行 root 设置。
///通过这种方式,您可以维护这些对象的集合,以便可以取消挂钩属性
///稍后更改,而不必担心该集合会对您正在监视其值的对象进行 root。
///完整的实现可以在这里找到http://agsmith.wordpress.com/2008/04/07/propertydescriptor-addvaluechanged-alternative/
/// </summary>
internal sealed class PropertyChangeNotifier : DependencyObject, IDisposable
{
private WeakReference _propertySource;
public PropertyChangeNotifier(DependencyObject propertySource, string path)
: this(propertySource, new PropertyPath(path))
{
}
public PropertyChangeNotifier(DependencyObject propertySource, DependencyProperty property)
: this(propertySource, new PropertyPath(property))
{
}
public PropertyChangeNotifier(DependencyObject propertySource, PropertyPath property)
{
if (null == propertySource)
{
throw new ArgumentNullException(nameof(propertySource));
}
if (null == property)
{
throw new ArgumentNullException(nameof(property));
}
this._propertySource = new WeakReference(propertySource);
var binding = new Binding();
binding.Path = property;
binding.Mode = BindingMode.OneWay;
binding.Source = propertySource;
BindingOperations.SetBinding(this, ValueProperty, binding);
}
public DependencyObject PropertySource
{
get
{
try
{
// note, it is possible that accessing the target property
// will result in an exception so ive wrapped this check
// in a try catch
return this._propertySource.IsAlive
? this._propertySource.Target as DependencyObject
: null;
}
catch
{
return null;
}
}
}
/// <summary>
/// Identifies the <see cref="Value"/> dependency property
/// </summary>
public static readonly DependencyProperty ValueProperty
= DependencyProperty.Register("Value", typeof(object), typeof(PropertyChangeNotifier),
new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnPropertyChanged)));
/// <summary>
/// Returns/sets the value of the property
/// </summary>
/// <seealso cref="ValueProperty"/>
[Description("Returns/sets the value of the property")]
[Category("Behavior")]
[Bindable(true)]
public object Value
{
get { return (object)this.GetValue(ValueProperty); }
set { this.SetValue(ValueProperty, value); }
}
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var notifier = (PropertyChangeNotifier)d;
if (notifier.RaiseValueChanged)
{
notifier.ValueChanged?.Invoke(notifier.PropertySource, EventArgs.Empty);
}
}
public event EventHandler ValueChanged;
public bool RaiseValueChanged { get; set; } = true;
public void Dispose()
{
BindingOperations.ClearBinding(this, ValueProperty);
}
}
}