Страницы

Поиск по вопросам

понедельник, 15 октября 2018 г.

Редактирование свойств выбранного объекта

В программе есть возможность редактировать свойства выбранного объекта.
Это реализовано через свойство SelectedObject во ViewModel.
Проблема в следующем. Если я выбираю объект, редактирую его свойства и кликаю в пределах текущего TabItem, то свойство обновляется. Но если же я редактирую свойство и, не убирая курсор с текстового поля, переключаюсь на другую вкладку, то изменения в свойстве не сохраняются.
Установка UpdateSourceTrigger=PropertyChanged в моем случае не подходит. Нужен именно LostFocus
Как это пофиксить?

Набросал пример, который отражает проблему.

Code-behind:
public class PersonVm : BaseVm { private string _name; private string _lastname;
public string Name { get => _name; set { _name = value; OnPropertyChanged(); } }
public string Lastname { get => _lastname; set { _lastname = value; OnPropertyChanged(); } } }
public class MainVm : BaseVm { private PersonVm _selectedPerson;
public MainVm() { Class1.Add(new PersonVm { Name = "Алексей", Lastname = "Алексеев" }); Class1.Add(new PersonVm { Name = "Иван", Lastname = "Иванов" });
Class2.Add(new PersonVm { Name = "Петр ", Lastname = "Петров" }); Class2.Add(new PersonVm { Name = "Николай", Lastname = "Николаев" }); } public ObservableCollection Class1 { get; } = new ObservableCollection(); public ObservableCollection Class2 { get; } = new ObservableCollection();
public PersonVm SelectedPerson { get => _selectedPerson; set { _selectedPerson = value; OnPropertyChanged(); } } }
XAML:





Ответ

На данный момент получается, что при возникновении события LostFocus привязки уже нет, поэтому она не срабатывает. Из всех PreviewXXX-событий у TextBox есть только PreviewLostKeyboardFocus, если подписаться на него и посмотреть отладчиком что лежит в Text, то мы увидим, что в нем привязка пока еще есть, значит это событие нам подойдет.
Действительно, давайте попробуем в подписчике принудительно обновить привязку, добавьте в обработчик такой код:
(sender as TextBox).GetBindingExpression(TextBox.TextProperty).UpdateSource();
и вы увидите, что все работает как вы и хотели.
Что можно с этим сделать? Можно, например, добавить обработчик события для всех нужных TextBox, или реализовать наследника TextBox с таким поведением, ну или, наконец, сделать Attached Behavior
Первый вариант, я считаю, плох, т.к. нужно будет добавлять обработчики такие во всех окнах/контролах где нам нужен TextBox с таким поведением, ну и если вдруг для какого-то из TextBox нам понадобится еще один обработчик этого события - его придется вешать из кода.
Поэтому я покажу последние два варианта.
Наследник TextBox, все предельно просто, я создал такой класс:
public class MyTextBox : TextBox { protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e) { base.OnPreviewLostKeyboardFocus(e); GetBindingExpression(TextProperty).UpdateSource(); } }
Всё. Это уже работает, достаточно вместо стандартного TextBox использовать local:MyTextBox. Но у этого подхода есть минус - если вы стилизуете стандартные TextBox, то эти кастомные штатно не будут подхватывать стили и их придется стилизовать отдельно, хотя, по сути, у нас тот же самый TextBox
Поэтому наиболее оптимальным решением видится сделать прикрепляемое поведение.
Подключите к проекту сборку System.Windows.Interactivity и создайте класс:
class TextBoxLostFocusUpdateBindingBehavior : Behavior { protected override void OnAttached() { base.OnAttached(); AssociatedObject.PreviewLostKeyboardFocus += OnPreviewLostKeyboardFocus; }
protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.PreviewLostKeyboardFocus -= OnPreviewLostKeyboardFocus; }
private void OnPreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) { AssociatedObject.GetBindingExpression(TextBox.TextProperty).UpdateSource(); } }
Теперь в XAML добавьте пространство имен: xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
И у нужных TextBox укажите это поведение:

PS.: Что примечательно, технология Binding в WPF достаточно интеллектуальна для того, чтобы не обновлять одну и ту же привязку, если с момента прошлого обновления изменений не было. Поэтому нам не нужно предпринимать никаких дополнительных действий чтобы отключить обновление привязки по LostFocus, она просто не будет никогда там обновляться, обновление теперь будет происходить всегда по PreviewLostKeyboardFocus

Комментариев нет:

Отправить комментарий