Files
ShrlAlgoToolkit/AntdWpf/Controls/PropertyChangeNotifier.cs
ShrlAlgo 4d35cadb56 更新
2025-07-11 09:20:23 +08:00

112 lines
4.2 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 AntdWpf.Controls
{
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
/// <summary>
/// AddValueChanged of dependency property descriptor results in memory leak as you already know.
/// So, as described here, you can create custom class PropertyChangeNotifier to listen
/// to any dependency property changes.
///
/// This class takes advantage of the fact that bindings use weak references to manage associations
/// so the class will not root the object who property changes it is watching. It also uses a WeakReference
/// to maintain a reference to the object whose property it is watching without rooting that object.
/// In this way, you can maintain a collection of these objects so that you can unhook the property
/// change later without worrying about that collection rooting the object whose values you are watching.
///
/// Complete implementation can be found here: 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);
}
}
}