Я пишу журнал отмены для моего экрана ввода данных WPF, который будет отслеживать изменения во всех элементах управления. Когда пользователь выбирает «Отменить», я хочу не только отменить последнее изменение, но и вернуть фокус элементу управления, значение которого возвращается. Я борюсь с лучшим способом вернуть этот фокус.
Моя ViewModel будет частью, которая обрабатывает ведение журнала отмены: установщики свойств ViewModel будут фиксировать некоторое состояние «до» перед обновлением DataModel. Так или иначе, это состояние «до» должно включать в себя достаточно информации, чтобы я мог вернуть фокус позже.
Для иллюстрации предположим, что есть два поля для ввода данных: Адрес и Город. ViewModel имеет свойство для каждого, а View имеет TextBox для каждого, привязанного к соответствующему свойству ViewModel.
Давайте рассмотрим пример, когда пользователь только что ввел значение в поле «Адрес», а затем щелкнул поле «Город». Я использую поведение UpdateSourceTrigger.LostFocus по умолчанию, поэтому изменение адреса сохраняется, когда текстовое поле адреса теряет фокус. На данный момент у меня есть три разных идеи о том, как подойти к этому, но я не знаю достаточно подробностей о WPF, чтобы знать, как заставить любую из них работать.
Я мог бы забыть о привязке данных в стиле MVVM и перехватить события LostFocus элементов управления редактированием (или добавить прикрепленное поведение, или создать собственный элемент управления, который обертывает TextBox, или...). В обработчике событий LostFocus я мог бы создать фрейм отмены, содержащий ссылку на отправителя события. Позже, после Undo, я просто фокусирую элемент управления, ссылку на который я сохранил. Это, вероятно, то, что я сделал бы в WinForms, но в WPF я бы предпочел придерживаться шаблона ViewModel - я бы предпочел, чтобы логика журналирования жила в ViewModel, чем в представлении, для тестируемости, по крайней мере. Так что этот вариант не мой первый выбор.
В моих средствах установки свойств ViewModel я мог бы захватить имя устанавливаемого свойства ViewModel (в данном примере — «Адрес») и сохранить это имя в кадре отмены. Позже, в Undo, я мог пройтись по всем элементам управления в представлении, ища первый, который я смог найти, у которого есть что-то, связанное со свойством с именем Address. Как только я нахожу один такой элемент управления, я уделяю ему внимание. Этого было бы достаточно для того, что мне нужно, поскольку я не ожидаю, что более одного элемента управления будет привязано к одному и тому же свойству ViewModel. Проблема в том, что это потребует копаться в выражениях привязки, чего я не знаю, как это сделать. (Это также приведет к большему количеству поздних привязок на основе имен, которые могут сломаться, если я рефакторинг.)
Когда моя ViewModel добавляет изменение в стек отмены, она может попросить слой View (через интерфейс) создать Memento, который знает, какой элемент управления имеет фокус. При отмене журнал попросит представление восстановить этот сувенир. Проблема здесь в том, что к тому времени, когда свойство моей ViewModel устанавливается и я добавляю рамку отмены, фокус клавиатуры уже переместился на текстовое поле City, поэтому «создать памятку» должно быть сложнее, чем просто «где находится». текущий фокус клавиатуры прямо сейчас», и я не уверен, как выполнить этот трюк.
У кого-нибудь есть какие-либо предложения по выполнению любой из вышеперечисленных работ или альтернативные подходы, которые могут работать лучше?