How to find PropertyInfo about a DependencyProperty (on a DependencyObject) in WinUI 3

316 Views Asked by At

Sometimes it's useful to have more information about a DependencyProperty than metadata supplies. For example, in generalized tracing methods and event handlers, it's helpful to be able to find the name (and other characteristics) of a DependencyProperty on a DependencyObject.

In WinUI 3, System.Reflection reports DependencyProperties as MemberType=Properties so we use GetProperties() to get a PropertyInfo[] containing information on all properties on an object or type (WPF reports DependencyProperties as MemberType=Fields so we'd use GetFields() instead).

DependencyProperties are actually declared as static fields and the Get... methods in Reflection typically default to not listing static members. When calling GetProperties, be sure to include the BindingFlags.Static flag, as well as BindingFlags.Public to include public DependencyProperties in your list.

Next, we check each of the returned PropertyInfo[] items to see if they are actually about the DependencyProperty identifier of interest. If dp holds the identifier of your DependencyProperty of interest and pi is the PropertyInfo item you're checking, use the object.ReferenceEquals(pi.GetValue(sender), dp) method to see if they both refer to the same identifier (Note: this will be false if pi and dp are on different objects or identify different properties). There should be one, and only one, match in the returned array.

Here is the beginning of a PropertyChanged handler that demonstrates:

    private static void BoundPropertyChanged(DependencyObject sender, DependencyProperty dp)
    {
        // sender is DependencyObject upon which the dp changed

        PropertyInfo dpInfo = sender.GetType().GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
            .Where(pi => pi.PropertyType == typeof(DependencyProperty) && object.ReferenceEquals(pi.GetValue(sender), dp)).Single();

        // Trace logic, etc.
    }

The first statement has four parts:

  1. sender.GetType().GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy) fetches information on all properties on sender's type. The BindingFlags.FlattenHierarchy flag means, essentially, include inherited properties, too.
  2. .Where(pi => pi.PropertyType == typeof(DependencyProperty) selects only members that have a PropertyType of DependencyProperty (routed events might be in the list from 1), too).
  3. && object.ReferenceEquals(pi.GetValue(sender), dp)) further limits the selected items to those that refer to the same identifiers.
  4. the .Single(); clause selects the sole member of the resulting list and throws an error if there is more (or less) than one.

One caveat is that this approach relies heavily on reflection, which is relatively slow.

I don't know of a better way to do this, but I'd welcome suggestions and improvements!

0

There are 0 best solutions below