如何获取给定属性绑定到 WPF C# 的所有 UI 控件

问题描述 投票:0回答:1

我正在使用反射来获取属性列表,并且我想使用该属性来获取给定属性所绑定到的 WPF 控件的实例。

因此,在后面的代码中,我想仅使用属性名称来访问后面的代码中的控件。在属性列表中,我可以将属性“字符串名称”绑定到“文本框”,我想访问该文本框。在下面的示例中,我想访问列表框。

C#:

private ObservableCollection<Show> _Shows;
[ReportableProperty]
public ObservableCollection<Show> Shows
{
    get { return _Shows; }
    set
    {
        if (value != _Shows)
        {

            _Shows = value;
            OnPropertyChanged("Shows");
        }
    }
}

XAML:

<ListBox ItemsSource="{Binding Shows, UpdateSourceTrigger=PropertyChanged}" ToolTip="Test" SelectedItem="{Binding SelectedShows, UpdateSourceTrigger=PropertyChanged}" Name="lstShows" DisplayMemberPath="name" SelectedValuePath="id" SelectionChanged="lstShows_SelectionChanged"></ListBox>
c# wpf
1个回答
0
投票

我不清楚你原来的问题。所以我可以为您提供您所要求的解决方案。由于该解决方案的性能密集型性质,值得尝试以不同的方式解决您的原始问题,例如通过改进您的设计。我无法帮助你,因为我不明白你试图解决的原始问题。

重点是,您无法从绑定源获取绑定目标,因为完整的绑定元数据与绑定目标一起存储。

  1. 您必须获取所有可能成为绑定目标的控件。
  2. 从潜在绑定目标的集合中,您可以逐一选择一个对象。
  3. 获取对象的所有依赖属性。
  4. 获取每个获取的依赖属性的
    BindingExpression
    BindingExpression
    对象的集合)。
  5. BindingExpression
    对象集合中,逐一检查
    BindingExpression
    来检查绑定是否使用您提供的属性名称作为绑定源。
  6. 如果您有属性名称列表,则对所有属性重复 5)(嵌套循环)
  7. 接下来,如果您有源对象列表,则从 5) 开始重复(嵌套循环)
  8. 对所有潜在的结合目标重复 3)(嵌套循环)

您可以看到有很多嵌套循环(至少三个),这使得算法的成本非常高。此外,每一步都必须使用反射。这就是为什么你应该尝试更优雅地解决原始问题。

以下示例演示如何获取潜在绑定目标 (

DependecyObject
) 的依赖属性并检查每个属性的绑定。收集与所提供的源对象绑定的每个绑定目标(可以有多个目标使用同一源)。

您必须使用生成要检查的潜在控件集合和源对象集合的代码来扩展帮助器方法,每个源对象都具有属性名称列表。

private readonly Dictionary<DependencyObject, IEnumerable<BindingExpression>> bindingExpressionsMap = new();

public bool TryGetBindingTargets(
  Type bindingSourceType, 
  string sourcePropertyName, 
  IEnumerable<DependencyObject> candidates, 
  out IEnumerable<DependencyObject> bindingTargets)
{
  var identifiedBindingTargets = new List<DependencyObject>();

  foreach (DependencyObject candidate in candidates)
  {
    if (this.bindingExpressionsMap.TryGetValue(candidate, out IEnumerable<BindingExpression> bindingExpressions))
    {
      foreach (BindingExpression bindingExpressionOfCandiate in bindingExpressions)
      {
        if (IsBindingValid(bindingSourceType, sourcePropertyName, bindingExpressionOfCandiate))
        {
          identifiedBindingTargets.Add(candidate);
        }
      }
    }
    else
    {
      bindingExpressions = new List<BindingExpression>();
      PropertyDescriptorCollection propertiesOfCandidate = TypeDescriptor.GetProperties(candidate);
      foreach (PropertyDescriptor propertyDescriptor in propertiesOfCandidate)
      {
        if (propertyDescriptor.IsReadOnly)
        {
          continue;
        }

        if (DependencyPropertyDescriptor.FromProperty(propertyDescriptor) is DependencyPropertyDescriptor dependencyPropertyDescriptor)
        {
          DependencyProperty dependencyProperty = dependencyPropertyDescriptor.DependencyProperty;
          if (BindingOperations.GetBindingExpression(candidate, dependencyProperty) is BindingExpression bindingExpressionOfCandiate)
          {
            ((IList<BindingExpression>)bindingExpressions).Add(bindingExpressionOfCandiate);

            if (IsBindingValid(bindingSourceType, sourcePropertyName, bindingExpressionOfCandiate))
            {
              identifiedBindingTargets.Add(candidate);
            }
          }
        }
      }

      this.bindingExpressionsMap.Add(candidate, bindingExpressions);
    }
  }

  bindingTargets = identifiedBindingTargets;

  return identifiedBindingTargets.Count > 0;
}

private static bool IsBindingValid(Type bindingSourceType, string sourcePropertyName, BindingExpression bindingExpression) 
  => bindingExpression.ResolvedSource.GetType() == bindingSourceType
    && bindingExpression.ResolvedSourcePropertyName.Equals(sourcePropertyName, StringComparison.Ordinal);

然后按如下方式使用:

// In this example RootPanel is a Grid, 
// the root element of the e.g. Window
IEnumerable<DependencyObject> bindingTargetCandidates = this.RootPanel.Children
  .Cast<DependencyObject>();
Type typeOfBindingSource = typeof(MyClass);
IEnumerable<string> propertyNamesOfBindingSource = typeOfBindingSource.GetProperties()
  .Select(propertyInfo => propertyInfo.Name);

foreach (string propertyName in propertyNamesOfBindingSource)
{
  if (TryGetBindingTargets(
    typeOfBindingSource, 
    propertyName, 
    bindingTargetCandidates, 
    out IEnumerable<DependencyObject> bindingTargets))
  {
    // Binding targets for the current binding source successfully found
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.