Я использую отражение, чтобы получить список свойств, и я хотел бы использовать это свойство, чтобы получить экземпляры элемента управления WPF, к которому привязано данное свойство.
Итак, в коде я хотел бы получить доступ к элементу управления в коде, используя только имя свойства. В списке свойств у меня может быть свойство «Имя строки», которое привязано к «Текстовому полю», к которому я хотел бы получить доступ. и в приведенном ниже примере мне нужен доступ к списку.
С#:
private ObservableCollection<Show> _Shows;
[ReportableProperty]
public ObservableCollection<Show> Shows
{
get { return _Shows; }
set
{
if (value != _Shows)
{
_Shows = value;
OnPropertyChanged("Shows");
}
}
}
КСАМЛ:
<ListBox ItemsSource = "{Binding Shows, UpdateSourceTrigger=PropertyChanged}" ToolTip = "Test" SelectedItem = "{Binding SelectedShows, UpdateSourceTrigger=PropertyChanged}" Name = "lstShows" DisplayMemberPath = "name" SelectedValuePath = "id" SelectionChanged = "lstShows_SelectionChanged"></ListBox>
@BionicCode Да, верно. Именно это.
В этом случае вы должны понимать, как работает привязка данных. Привязка всегда устанавливается для цели привязки (которая всегда является свойством зависимости). Это означает, что единственная информация о привязке хранится в свойстве зависимости цели привязки. Невозможно получить эту информацию из источника привязки. Как следствие, вам придется получить все элементы управления и проверить все свойства зависимостей каждого элемента управления, чтобы собрать все выражения привязки.
Затем вам нужно получить источник привязки выражения и сопоставить его с исходным объектом предиката, для которого вы используете отражение. Если вы найдете совпадение, попробуйте сопоставить путь привязки текущего свойства зависимостей. Если у вас есть совпадение, значит, вы нашли свой контроль. Затем перейдите к следующему свойству зависимости и после всех свойств текущего элемента управления перейдите к следующему элементу управления и повторите. Вам придется повторить эту полную процедуру для всех исходных свойств (объектов PropertyInfo). Это очень дорогостоящая задача.
Возникает вопрос, почему вы хотите это сделать? Возможно, есть лучшее решение вашей первоначальной проблемы.
@BionicCode Мне нужно создать функцию, которая получает все отчетные свойства и сохраняет/сопоставляет элементы пользовательского интерфейса со своим свойством. например Имя строки будет иметь ширину = 50. Данные о ширине могут быть жестко закодированы или привязаны к одному и тому же объекту, но мое отражение не улавливает их, потому что это не свойство, о котором можно сообщить. Он предназначен для отслеживания/аудита (больше похожего на снимок) отчетных элементов, которые добавляются/удаляются из окна().
«Имя строки будет иметь ширину = 50». Можете ли вы рассказать об этом подробнее? И в чем разница между свойствами, аннотированными атрибутом, и теми, которые не имеют атрибута?
@BionicCode «Имя строки будет иметь ширину = 50» — я имел в виду, что ширина — это атрибут пользовательского интерфейса, а в объекте с именем «ReportResult» имя отчетного свойства будет храниться в «public string PropertyName { get; набор; }», а соответствующие атрибуты и значения пользовательского интерфейса, такие как «Ширина», будут сохранены в « public List<Dictionary<string, string>> SelectedUIAttributesandValues { get; набор; }'
Давайте продолжим обсуждение в чате.
@BionicCode «Тогда вам нужно получить источник привязки выражения и сопоставить его с исходным объектом предиката». Это тот бит, для которого я могу найти код.
Ваша первоначальная проблема мне не ясна. Поэтому я могу предоставить вам решение того, о чем вы просили. Из-за высокой производительности решения стоит попытаться решить исходную проблему по-другому, например. улучшая свой дизайн. Я не могу вам с этим помочь, так как не понимаю исходную проблему, которую вы пытаетесь решить.
Дело в том, что вы не можете получить цели привязки из источника привязки, поскольку полные метаданные привязки хранятся вместе с целью привязки.
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
}
}
Просто чтобы прояснить ваш трудный для чтения вопрос: у вас есть объект, который служит источником привязки. Теперь вы хотите использовать отражение, чтобы получить все свойства этого источника привязки. Далее, для каждого полученного имени свойства вы хотите получить все элементы управления, которые в данный момент привязаны к этому свойству? Это верно?