How to Implement a DependencyPropertyWatcher

Dependency properties are widely used in WPF controls. However there is no buid-in way to listen to changes of such properties.

The following class is a generic watcher for dependecy properties:

public class DependencyPropertyWatcher<T> : DependencyObject, IDisposable
{
  /// <summary>
  /// Value of Value Property
  /// </summary>
  public static readonly DependencyProperty ValueProperty =
      DependencyProperty.Register(
          "Value",
          typeof(object),
          typeof(DependencyPropertyWatcher<T>),
          new PropertyMetadata(null, OnPropertyChanged));

  /// <summary>
  /// Called when Property Changes.
  /// </summary>
  public event EventHandler PropertyChanged;

  /// <summary>
  /// Initializes a new instance of the <see cref="DependencyPropertyWatcher{T}"/> class.
  /// </summary>
  /// <param name="target">Target Dependency Object</param>
  /// <param name="propertyPath">Path of Property</param>
  public DependencyPropertyWatcher(DependencyObject target, string propertyPath)
  {
    Target = target;
    BindingOperations.SetBinding(
        this,
        ValueProperty,
        new Binding() { Source = target, Path = new PropertyPath(propertyPath), Mode = BindingMode.OneWay });
  }

  /// <summary>
  /// Gets the target Dependency Object
  /// </summary>
  public DependencyObject Target { get; private set; }

  /// <summary>
  /// Gets the current Value
  /// </summary>
  public T Value
  {
    get { return (T)GetValue(ValueProperty); }
  }

  /// <summary>
  /// Called when the Property is updated
  /// </summary>
  /// <param name="sender">Source</param>
  /// <param name="args">Args</param>
  public static void OnPropertyChanged(object sender, DependencyPropertyChangedEventArgs args)
  {
    var source = (DependencyPropertyWatcher<T>)sender;
    source.PropertyChanged?.Invoke(source, EventArgs.Empty);
  }

  // Dispose() calls Dispose(true)
  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }

  /// <summary>
  /// Called when object is being disposed.
  /// </summary>
  protected virtual void Dispose(bool disposing)
  {
    if (disposing)
    {
      ClearValue(ValueProperty);
    }
    // free native resources if there are any.
  }
}

The target object and a property path are provided in the constructor. This information is used to create a binding to the DependencyPropertyWatcher’s Value property. This contains the current value of the dependency property at runtime.

The DependencyPropertyWatcher casts an event when the property changes. This is done by using the PropertyChangedCallback of the ValueProperty.

Please remember to keep an instance of the DependencyPropertyWatcher. Otherwise it will be disposed by the garbage collection.

List<DependencyPropertyWatcher<Visibility>> stateWatchers = new List<DependencyPropertyWatcher<Visibility>>();     

public void Register()
{
  var watcher = new DependencyPropertyWatcher<Visibility>(Application.Current.MainWindow, nameof(Window.Visibility));
 
  // We need to keep a reference to the watcher. Otherwise it will be destroyed
  stateWatchers.Add(watcher);
  watcher.PropertyChanged += (sender, args) => UpdateChildSizes(sender as Window);
}

private static void UpdateChildSizes(Window window)
{
  // Do something!
}

As you can see in the example the T of the generic DependencyPropertyWatcher contains the type of the property to watch. The first argument of the constructor is the actual object and the second is the name of the property to watch.

Das könnte Dich auch interessieren …

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.