WPF. Find control that binds to specific property

前端 未结 2 1717
猫巷女王i
猫巷女王i 2020-12-06 03:09

Any ideas on how to implement a method that given a propertyname, finds a control (perhaps from a visualtree) which is bound to the given property?

相关标签:
2条回答
  • 2020-12-06 03:24

    I created code based on accepted ASanch answer. This code uses LogicalTreeHelper which makes it 6times faster (130ms vs 20ms when looking for control with specific binding on simple window).

    Plus I fix some errors in ASanch code (look at original "else if (bindingBase is MultiBinding)" or "else if (bindingBase is PriorityBinding)").

    public static class DependencyObjectHelper
    {
        /// <summary>
        /// Gets all dependency objects which has binding to specific property
        /// </summary>
        /// <param name="dependencyObject"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static IList<DependencyObject> GetDependencyObjectsWithBindingToProperty(DependencyObject dependencyObject, string propertyName)
        {
            var list = new List<DependencyObject>();
            GetDependencyObjectsWithBindingToPropertyRecursive(propertyName, dependencyObject, list);
    
            return list;
        }
    
        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="dependencyObject"></param>
        /// <param name="sources"></param>
        /// <remarks>
        /// Based on ASanch answer on http://stackoverflow.com/questions/3959421/wpf-find-control-that-binds-to-specific-property
        /// </remarks>>
        private static void GetDependencyObjectsWithBindingToPropertyRecursive(string propertyName, DependencyObject dependencyObject, ICollection<DependencyObject> sources)
        {
            var dependencyProperties = new List<DependencyProperty>();
            dependencyProperties.AddRange(MarkupWriter.GetMarkupObjectFor(dependencyObject).Properties.Where(x => x.DependencyProperty != null).Select(x => x.DependencyProperty).ToList());
            dependencyProperties.AddRange(
                MarkupWriter.GetMarkupObjectFor(dependencyObject).Properties.Where(x => x.IsAttached && x.DependencyProperty != null).Select(x => x.DependencyProperty).ToList());
    
            var bindings = dependencyProperties.Select(x => BindingOperations.GetBindingBase(dependencyObject, x)).Where(x => x != null).ToList();
    
            Predicate<Binding> condition = binding => binding != null && binding.Path.Path == propertyName && !sources.Contains(dependencyObject);
    
            foreach (var bindingBase in bindings)
            {
                if (bindingBase is Binding)
                {
                    if (condition(bindingBase as Binding))
                        sources.Add(dependencyObject);
                }
                else if (bindingBase is MultiBinding)
                {
                    if (((MultiBinding)bindingBase).Bindings.Any(bindingBase2 => condition(bindingBase2 as Binding)))
                    {
                        sources.Add(dependencyObject);
                    }
                }
                else if (bindingBase is PriorityBinding)
                {
                    if (((PriorityBinding)bindingBase).Bindings.Any(bindingBase2 => condition(bindingBase2 as Binding)))
                    {
                        sources.Add(dependencyObject);
                    }
                }
            }
    
            var children = LogicalTreeHelper.GetChildren(dependencyObject).OfType<DependencyObject>().ToList();
            if (children.Count == 0)
                return;
    
            foreach(var child in children)
            {
                GetDependencyObjectsWithBindingToPropertyRecursive(propertyName, child, sources);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-06 03:47

    Try this one. First, copy-paste this DependencyObjectHelper class in your project. It has a function that allows you to get all the BindingObjects in a given object.

    public static class DependencyObjectHelper
    {
        public static List<BindingBase> GetBindingObjects(Object element)
        {
            List<BindingBase> bindings = new List<BindingBase>();
            List<DependencyProperty> dpList = new List<DependencyProperty>();
            dpList.AddRange(DependencyObjectHelper.GetDependencyProperties(element));
            dpList.AddRange(DependencyObjectHelper.GetAttachedProperties(element));
    
            foreach (DependencyProperty dp in dpList)
            {
                BindingBase b = BindingOperations.GetBindingBase(element as DependencyObject, dp);
                if (b != null)
                {
                    bindings.Add(b);
                }
            }
    
            return bindings;
        }
    
        public static List<DependencyProperty> GetDependencyProperties(Object element)
        {
            List<DependencyProperty> properties = new List<DependencyProperty>();
            MarkupObject markupObject = MarkupWriter.GetMarkupObjectFor(element);
            if (markupObject != null)
            {
                foreach (MarkupProperty mp in markupObject.Properties)
                {
                    if (mp.DependencyProperty != null)
                    {
                        properties.Add(mp.DependencyProperty);
                    }
                }
            }
    
            return properties;
        }
    
        public static List<DependencyProperty> GetAttachedProperties(Object element)
        {
            List<DependencyProperty> attachedProperties = new List<DependencyProperty>();
            MarkupObject markupObject = MarkupWriter.GetMarkupObjectFor(element);
            if (markupObject != null)
            {
                foreach (MarkupProperty mp in markupObject.Properties)
                {
                    if (mp.IsAttached)
                    {
                        attachedProperties.Add(mp.DependencyProperty);
                    }
                }
            }
    
            return attachedProperties;
        }
    }
    

    Then, create this GetBindingSourcesRecursive function. It recursively collects the DependencyObjects in the visual tree that has at least one Binding object targetting a given property name.

    private void GetBindingSourcesRecursive(string propertyName, DependencyObject root, List<object> sources)
    {
        List<BindingBase> bindings = DependencyObjectHelper.GetBindingObjects(root);
        Predicate<Binding> condition =
            (b) =>
            {
                return (b.Path is PropertyPath) 
                    && (((PropertyPath)b.Path).Path == propertyName)
                    && (!sources.Contains(root));
            };
    
        foreach (BindingBase bindingBase in bindings)
        {
            if (bindingBase is Binding)
            {
                if (condition(bindingBase as Binding))
                    sources.Add(root);
            }
            else if (bindingBase is MultiBinding)
            {
                MultiBinding mb = bindingBase as MultiBinding;
                foreach (Binding b in mb.Bindings)
                {
                    if (condition(bindingBase as Binding))
                        sources.Add(root);
                }
            }
            else if (bindingBase is PriorityBinding)
            {
                PriorityBinding pb = bindingBase as PriorityBinding;
                foreach (Binding b in pb.Bindings)
                {
                    if (condition(bindingBase as Binding))
                        sources.Add(root);
                }
            }
        }
    
        int childrenCount = VisualTreeHelper.GetChildrenCount(root);
        if (childrenCount > 0)
        {
            for (int i = 0; i < childrenCount; i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(root, i);
                GetBindingSourcesRecursive(propertyName, child, sources);
            }
        }
    }
    

    Then, to use this, just call GetBindingsRecursive passing in the property name, the root visual (e.g. the Window), and an object list that will contain the results.

    List<object> sources = new List<object>();
    GetBindingSourcesRecursive("SomePropertyPath", this, sources);
    sources.ForEach((o) => Console.WriteLine(o.ToString()));
    

    Hope this helps.

    0 讨论(0)
提交回复
热议问题