Как получить все элементы управления пользовательским интерфейсом, заданное свойство ограничено WPF C#

Я использую отражение, чтобы получить список свойств, и я хотел бы использовать это свойство, чтобы получить экземпляры элемента управления 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 26.04.2024 19:10

@BionicCode Да, верно. Именно это.

Dev 26.04.2024 19:15

В этом случае вы должны понимать, как работает привязка данных. Привязка всегда устанавливается для цели привязки (которая всегда является свойством зависимости). Это означает, что единственная информация о привязке хранится в свойстве зависимости цели привязки. Невозможно получить эту информацию из источника привязки. Как следствие, вам придется получить все элементы управления и проверить все свойства зависимостей каждого элемента управления, чтобы собрать все выражения привязки.

BionicCode 26.04.2024 19:19

Затем вам нужно получить источник привязки выражения и сопоставить его с исходным объектом предиката, для которого вы используете отражение. Если вы найдете совпадение, попробуйте сопоставить путь привязки текущего свойства зависимостей. Если у вас есть совпадение, значит, вы нашли свой контроль. Затем перейдите к следующему свойству зависимости и после всех свойств текущего элемента управления перейдите к следующему элементу управления и повторите. Вам придется повторить эту полную процедуру для всех исходных свойств (объектов PropertyInfo). Это очень дорогостоящая задача.

BionicCode 26.04.2024 19:19

Возникает вопрос, почему вы хотите это сделать? Возможно, есть лучшее решение вашей первоначальной проблемы.

BionicCode 26.04.2024 19:24

@BionicCode Мне нужно создать функцию, которая получает все отчетные свойства и сохраняет/сопоставляет элементы пользовательского интерфейса со своим свойством. например Имя строки будет иметь ширину = 50. Данные о ширине могут быть жестко закодированы или привязаны к одному и тому же объекту, но мое отражение не улавливает их, потому что это не свойство, о котором можно сообщить. Он предназначен для отслеживания/аудита (больше похожего на снимок) отчетных элементов, которые добавляются/удаляются из окна().

Dev 26.04.2024 19:43

«Имя строки будет иметь ширину = 50». Можете ли вы рассказать об этом подробнее? И в чем разница между свойствами, аннотированными атрибутом, и теми, которые не имеют атрибута?

BionicCode 26.04.2024 19:57

@BionicCode «Имя строки будет иметь ширину = 50» — я имел в виду, что ширина — это атрибут пользовательского интерфейса, а в объекте с именем «ReportResult» имя отчетного свойства будет храниться в «public string PropertyName { get; набор; }», а соответствующие атрибуты и значения пользовательского интерфейса, такие как «Ширина», будут сохранены в « public List<Dictionary<string, string>> SelectedUIAttributesandValues ​​{ get; набор; }'

Dev 26.04.2024 20:11

Давайте продолжим обсуждение в чате.

Dev 26.04.2024 20:11

@BionicCode «Тогда вам нужно получить источник привязки выражения и сопоставить его с исходным объектом предиката». Это тот бит, для которого я могу найти код.

Dev 26.04.2024 21:13
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
10
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваша первоначальная проблема мне не ясна. Поэтому я могу предоставить вам решение того, о чем вы просили. Из-за высокой производительности решения стоит попытаться решить исходную проблему по-другому, например. улучшая свой дизайн. Я не могу вам с этим помочь, так как не понимаю исходную проблему, которую вы пытаетесь решить.

Дело в том, что вы не можете получить цели привязки из источника привязки, поскольку полные метаданные привязки хранятся вместе с целью привязки.

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

Другие вопросы по теме