【问题标题】:WPF. Find control that binds to specific propertyWPF。查找绑定到特定属性的控件
【发布时间】:2011-04-26 22:37:35
【问题描述】:

关于如何实现给定属性名的方法,找到绑定到给定属性的控件(可能来自可视化树)的任何想法?

【问题讨论】:

    标签: wpf data-binding binding


    【解决方案1】:

    试试这个。首先,将这个 DependencyObjectHelper 类复制粘贴到您的项目中。它有一个功能,可以让您获取给定对象中的所有 BindingObjects。

    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;
        }
    }
    

    然后,创建这个 GetBindingSourcesRecursive 函数。它递归地收集可视化树中的 DependencyObjects,其中至少有一个以给定属性名称为目标的 Binding 对象。

    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);
            }
        }
    }
    

    然后,要使用它,只需调用 GetBindingsRecursive 并传入属性名称、根视觉对象(例如窗口)和包含结果的对象列表。

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

    希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      我根据接受的 ASanch 答案创建了代码。此代码使用 LogicalTreeHelper,使其速度提高 6 倍(在简单窗口上查找具有特定绑定的控件时为 130 毫秒与 20 毫秒)。

      另外我修复了 ASanch 代码中的一些错误(查看原始 "else if (bindingBase is MultiBinding)""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);
              }
          }
      }
      

      【讨论】:

      • 这里的所有ToList 调用都是完全没有必要的。
      • 不应该将论点 propertyName 真正称为 bindingPath 或类似的东西吗?只是略读代码,并没有真正运行它,但是按属性名称搜索应该只需要检查dependencyProperties 列表。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-09
      • 2011-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-14
      • 1970-01-01
      相关资源
      最近更新 更多