更新
This commit is contained in:
111
AntdWpf/Controls/PropertyChangeNotifier.cs
Normal file
111
AntdWpf/Controls/PropertyChangeNotifier.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
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 i’ve 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user