RadComboBox PropertyChanged не отображается в тексте

У меня xaml вот так:

<controls:RadComboBoxEx ItemsSource="{Binding Projects}" 
                        SelectedItem="{Binding SelectedProject}" 
                        DisplayMemberPath="Title"/>

<TextBox Text="{Binding SelectedProject.Title}" />

RadComboBoxEx - это локальный класс, который расширяет RadComboBox от Telerik и отменяет значение по умолчанию свойства со значениями по умолчанию для всей компании:

this.IsEditable = true;
this.IsReadOnly = false;
this.OpenDropDownOnFocus = true;
this.CanAutocompleteSelectItems = false;
this.StaysOpenOnEdit = true;
this.IsFilteringEnabled = true;
this.TextSearchMode = TextSearchMode.Contains;

... и выполняет дополнительную обработку событий.

Когда мое приложение запускается и заполняются проекты, это работает как шарм. Пользователь может начать вводить текст в поле, увидеть, что он набирает, и получить отфильтрованный список элементов для выбора. Они выбирают элемент, и отображается полный заголовок, даже если они набрали только часть заголовка.

Теперь ... пользователь выбирает "Обновить" (элемент пользовательского интерфейса и метод не показаны). Фоновый код переходит в базу данных и подбирает новое название проекта. Он сохраняет тот же объект, просто обновляет свойства объекта, которые изменились. Срабатывают события PropertyChanged, TextBox отображает обновления, элементы в раскрывающемся списке со списком отображают обновления, но выбранный текст элемента не обновляется.

Сначала я попытался добавить свойство Text и привязать его к SelectedProject.Title. Двусторонняя привязка позволяла обновлять текст, но вызывала всевозможные проблемы, поскольку значения объектов изменялись непреднамеренно. Можно было бы ожидать, что OneWay привязка будет работать, но никаких обновлений из DataContext не потребовалось, за исключением UpdateSourceTrigger=LostFocus, чего на самом деле недостаточно.

Затем я обнаружил, что если я установлю IsEditable="False", отображение выбранного элемента будет обновляться без привязки к свойству Text. Но тогда пользователь не мог видеть, что он набирает, при попытке отфильтровать список. Я подумал об изменении TextSearchMode.Contains на TextSearchMode.StartsWith, но это подойдет только для некоторых комбинированных полей.

Я попытался принудительно привязать SelectedItem к TwoWay и вызвать событие PropertyChanged, сообщающее, что SelectedProject изменилось, даже если оно не изменилось, и не было изменений в поведении.

Очевидно, что TextBox, который отображается, когда IsEditable имеет значение true, не привязан (по умолчанию, если вы тупо не привязываете два способа к свойству Text ^) непосредственно к выбранному элементу, поскольку вы хотите, чтобы пользователь, вводящий текст, подключался к фильтру, а не к выбранному элементу . Однако при выборе элемента кажется, что он правильно заполняет всю строку, поэтому должен быть какой-то способ вызвать событие при изменении выбора. (Несмотря на мою предыдущую неудачную попытку форсировать это с помощью события PropertyChanged, это должно быть так.)

Поэтому я перебрал его в своем методе обновления.

Project temp = this.SelectedProject;
this.SelectedProject = null;
this.SelectedProject = temp;

Это сработало! Итак, где-то в цепочке событий внутри элемента управления не было никаких изменений - ›в цепочке не было прерывания действия, и он никогда не смотрел на изменение вложенных свойств для обновления свойства Text.

Итак, у меня есть хак, который работает в этом конкретном случае, но я должен сделать это в нескольких местах, и я бы не хотел оставлять эту ловушку для следующего разработчика в нашей команде. Могу ли я что-нибудь сделать в нашем RadComboBoxEx, чтобы получить правильный поток событий для этого типа изменений?


person Denise Skidmore    schedule 29.07.2020    source источник
comment
К вашему сведению, TextBox существует только для целей отладки, чтобы доказать правильность привязки данных на стороне ViewModel.   -  person Denise Skidmore    schedule 29.07.2020
comment
Попробуйте добавить UpdateSourceTrigger как PropertyChanged и Mode в TwoWay в привязке.   -  person Rufw91    schedule 06.08.2020


Ответы (1)


Официальное решение вопроса

Об этой проблеме сообщалось на форумах Telerik раньше. Официальное решение, чтобы заставить это работать, - привязать свойство Text ComboBox в режиме TwoWay. Вы сделали почти то же самое, но использовали одно и то же свойство для привязки отображаемого элемента и свойства Text. Это не будет соответствовать вашим требованиям, вам нужно создать отдельные свойства.

private Project _selectedProject;
private string _selectedProjectTitle;

public Project SelectedProject
{
   get => _selectedProject;
   set
   {
      if (_selectedProject != value)
      {
         _selectedProject = value;
         OnPropertyChanged();
      }
   }
}

public string SelectedProjectTitle
{
   get => _selectedProjectTitle;
   set
   {
      if (_selectedProjectTitle!= value)
      {
         _selectedProjectTitle= value;
         OnPropertyChanged();
      }
   }
}

В коде XAML свяжите SelectedItem и Text и установите DisplayMemberPath.

<ComboBox ItemsSource="{Binding Projects}" 
          SelectedItem="{Binding SelectedProject}" 
          DisplayMemberPath="Title"
          Text="{Binding  SelectedProjectTitle, Mode=TwoWay}"
          .../>

Каждый раз, когда вы выбираете новые данные из базы данных и ваш выбранный элемент или одно из его свойств изменяется, присвойте новый Title свойству SelectedProjectTitle. Это свойство используется только для того, чтобы сделать обновления возможными, оно не меняет никаких данных в ваших Project моделях представления, поэтому вы не столкнетесь с проблемой непреднамеренных изменений.

Основная причина проблемы

Основная причина этой проблемы - свойство IsEditable. Внутри есть такой Update метод, который ведет себя по-разному в зависимости от состояния IsEditable.

private void Update()
{
   if (this.IsEditable)
      this.UpdateEditableTextBox();
   else
      this.UpdateSelectionBoxItem();
}

Элемент отображения выбранного элемента отображается с помощью TextBox. Если ComboBox не редактируется, в текстовом поле отображается свойство, определенное DisplayMemberPath на SelectedItem. Он обновляется методом UpdateSelectionBoxItem, но не в UpdateEditableTextBox.

Следовательно, если ComboBox доступен для редактирования, DisplayMemberPath не учитывается при обновлениях, поэтому текст останется прежним, не будет никаких обновлений из свойств. Однако официальное решение, конечно же, работает, так как свойство Text привязано TwoWay, которое запускает обновления.

Также есть причина, по которой сброс SelectedItem работает для обновления текста. Есть несколько триггеров, которые вызывают обновление SelectedItem, например, назначение другого элемента или null. Это вызовет метод, который явно обновит свойство Text, а затем вызовет Update.

Могу ли я отменить шаблон «нет изменений -› нет событий »в дочернем классе RadComboBox?

Все методы, связанные с обновлением: private или internal, но не virtual. Вы не сможете переопределить поведение в подклассе, если не начнете с обработчиков изменения свойств зависимостей, которые сами по себе содержат много логики, поэтому эта оценка рано или поздно обречена на неудачу.

Я думаю, что лучший способ уйти отсюда - использовать официальное решение, поскольку оно задокументировано и соответствует поведению собственного ComboBox WPF. В конечном итоге это, скорее всего, сэкономит вам массу средств на обслуживание и исправление ошибок. Ключ в том, что вы открыто сообщаете об этом предполагаемом поведении и документируете его для собственного производного элемента управления, чтобы все ваши клиенты знали об этом.

person thatguy    schedule 10.08.2020
comment
Хорошо получить официальный ответ от Telerik, но это все еще ловушка, которую нужно распространять в каждом проекте, а не решать в дочернем классе ... - person Denise Skidmore; 11.08.2020
comment
Присуждение награды в любом случае, и это заслуживает ответа Telerik. - person Denise Skidmore; 11.08.2020