我正在使用反射来获取属性列表,并且我想使用该属性来获取给定属性所绑定到的 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>
我不清楚你原来的问题。所以我可以为您提供您所要求的解决方案。由于该解决方案的性能密集型性质,值得尝试以不同的方式解决您的原始问题,例如通过改进您的设计。我无法帮助你,因为我不明白你试图解决的原始问题。
重点是,您无法从绑定源获取绑定目标,因为完整的绑定元数据与绑定目标一起存储。
BindingExpression
(BindingExpression
对象的集合)。BindingExpression
对象集合中,逐一检查 BindingExpression
来检查绑定是否使用您提供的属性名称作为绑定源。您可以看到有很多嵌套循环(至少三个),这使得算法的成本非常高。此外,每一步都必须使用反射。这就是为什么你应该尝试更优雅地解决原始问题。
以下示例演示如何获取潜在绑定目标 (
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
}
}