다음을 통해 공유


GetBindingExpression – a good way to iterate over all dependency properties in visual tree in Silverlight 3?

Recently I’ve tried to iterate over all dependency properties in the subset of visual tree. Despite it can be fragile, it is useful in advanced scenarios when certain actions needs to be trigged basing on binding’s path, converter or converter parameters. As a lazy programmer, I tried to find something over the Internet. All I was able to find was specific to WPF due to Silverlight API limitations (more later in the post). It looked as an easy task due to a new method in Silverlight 3 in FrameworkElement – GetBindingExpression. Here is my first try:

 

        private void BuildBindingList(FrameworkElement element)

        {

            FieldInfo[] infos = element.GetType().GetFields(BindingFlags.Public | BindingFlags.FlattenHierarchy |

                BindingFlags.Instance | BindingFlags.Static);

            foreach (FieldInfo field in infos)

            {

                if (field.FieldType == typeof(DependencyProperty))

                {

      DependencyProperty dp = (DependencyProperty)field.GetValue(null);

                    BindingExpression ex = element.GetBindingExpression(dp);

                    if (ex != null)

                    {

                        System.Diagnostics.Debug.WriteLine("Binding found with path: " + ex.ParentBinding.Path.Path);

                    }

                }

            }

            int children = VisualTreeHelper.GetChildrenCount(element);

            for (int i = 0; i < children; i++)

            {

                FrameworkElement child = VisualTreeHelper.GetChild(element, i) as FrameworkElement;

                if (child != null)

                {

                    BuildBindingList(child);

                }

            }

}

 

The method consists of two for loops.

First one is responsible for iterating over all dependency properties. We need to use parameterized GetFields method to extract all fields, because if you check in reflector, the no-parameter version of this method is defined as

 

  public FieldInfo[]  GetFields (){
   return this.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
}

It lacks BindingFlags.FlattenHierarchy flag and as the result all inherited fields (majority of which are dependency properties) would be skipped.

The second “for” loop calls the method recursively in the visual tree using VisualTreeHelper.

Unfortunately we are not able to use the code above in Silverlight 2. The problem is not actually that it lacks GetBindingExpression, but that BindingExpression class is internal in the framework.

There is also one issue with the code above which. It does not iterate over attached properties. I found several ways to do it in WPF. Two of them are described in this thread, which involve:

· MarkupObject / MarkupWriter

· TypeDescriptor

No luck - they are not supported in Silverlight 3 and I didn’t find a graceful way to do it

I mentioned a gracefully way. There is one dirty way. It iterates over all types in assembly, lists their (also non-attached) dependency properties and tries to extract all possible dependency values from an object. For ex. For attached properties defined in your assembly it would be.

            Assembly a = Assembly.GetCallingAssembly();

            foreach (Type t in a.GetTypes())

            {

                foreach (FieldInfo fi in t.GetFields(BindingFlags.DeclaredOnly

                    | BindingFlags.Static | BindingFlags.Public))

                {

                    if (fi.FieldType == typeof(DependencyProperty))

                    {

                        DependencyProperty dp = (DependencyProperty)fi.GetValue(null);

                        BindingExpression ex = element.GetBindingExpression(dp);

                        if (ex != null)

                        {

                            System.Diagnostics.Debug.WriteLine("Binding found to element: " + ex.ParentBinding.Path.Path);

                        }

                    }

                }

            }

It is definitely not elegant or efficient way though. If I should miss something and it is possible to list all dependency properties in a more graceful ways, please let me know.

Comments