Отменить ведение журнала: выделение элемента управления, отменяемого

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

Моя ViewModel будет частью, которая обрабатывает ведение журнала отмены: установщики свойств ViewModel будут фиксировать некоторое состояние «до» перед обновлением DataModel. Так или иначе, это состояние «до» должно включать в себя достаточно информации, чтобы я мог вернуть фокус позже.

Для иллюстрации предположим, что есть два поля для ввода данных: Адрес и Город. ViewModel имеет свойство для каждого, а View имеет TextBox для каждого, привязанного к соответствующему свойству ViewModel.

Давайте рассмотрим пример, когда пользователь только что ввел значение в поле «Адрес», а затем щелкнул поле «Город». Я использую поведение UpdateSourceTrigger.LostFocus по умолчанию, поэтому изменение адреса сохраняется, когда текстовое поле адреса теряет фокус. На данный момент у меня есть три разных идеи о том, как подойти к этому, но я не знаю достаточно подробностей о WPF, чтобы знать, как заставить любую из них работать.

  1. Я мог бы забыть о привязке данных в стиле MVVM и перехватить события LostFocus элементов управления редактированием (или добавить прикрепленное поведение, или создать собственный элемент управления, который обертывает TextBox, или...). В обработчике событий LostFocus я мог бы создать фрейм отмены, содержащий ссылку на отправителя события. Позже, после Undo, я просто фокусирую элемент управления, ссылку на который я сохранил. Это, вероятно, то, что я сделал бы в WinForms, но в WPF я бы предпочел придерживаться шаблона ViewModel - я бы предпочел, чтобы логика журналирования жила в ViewModel, чем в представлении, для тестируемости, по крайней мере. Так что этот вариант не мой первый выбор.

  2. В моих средствах установки свойств ViewModel я мог бы захватить имя устанавливаемого свойства ViewModel (в данном примере — «Адрес») и сохранить это имя в кадре отмены. Позже, в Undo, я мог пройтись по всем элементам управления в представлении, ища первый, который я смог найти, у которого есть что-то, связанное со свойством с именем Address. Как только я нахожу один такой элемент управления, я уделяю ему внимание. Этого было бы достаточно для того, что мне нужно, поскольку я не ожидаю, что более одного элемента управления будет привязано к одному и тому же свойству ViewModel. Проблема в том, что это потребует копаться в выражениях привязки, чего я не знаю, как это сделать. (Это также приведет к большему количеству поздних привязок на основе имен, которые могут сломаться, если я рефакторинг.)

  3. Когда моя ViewModel добавляет изменение в стек отмены, она может попросить слой View (через интерфейс) создать Memento, который знает, какой элемент управления имеет фокус. При отмене журнал попросит представление восстановить этот сувенир. Проблема здесь в том, что к тому времени, когда свойство моей ViewModel устанавливается и я добавляю рамку отмены, фокус клавиатуры уже переместился на текстовое поле City, поэтому «создать памятку» должно быть сложнее, чем просто «где находится». текущий фокус клавиатуры прямо сейчас», и я не уверен, как выполнить этот трюк.

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


person Joe White    schedule 12.05.2009    source источник
comment
Я думаю, что в конце концов вам придется сломать шаблон MVVM, потому что ваша ViewModel тесно связана с вашим View.   -  person treehouse    schedule 16.05.2009
comment
Есть ли у вас какие-либо предложения о способах достижения этого, которые больше подходят для MVVM? Я просто не хочу, чтобы пользователь нажимал «Отменить» и понятия не имел, что только что изменилось (поскольку элемент управления, который только что был отменен, может быть прокручен за пределы экрана).   -  person Joe White    schedule 16.05.2009


Ответы (1)


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

Например, это моя виртуальная машина:

public class VM
{
    public double Price { get; set; }
    public bool PriceHighlighted { get; set; }
}

Затем привяжите свойство Price к TextBox, а фон TextBox — к PriceHighlighted (с преобразователем значений). Теперь VM имеет полный контроль над тем, как должно реагировать представление. Когда пользователь выполняет «Отменить», VM может установить для всех xxxHightlighted значение false, кроме того, который вы хотите выделить.

person treehouse    schedule 17.05.2009
comment
Интересная идея. Не отвечает на вопрос, как прокрутить это поле в поле зрения, но его, безусловно, можно проверить. - person Joe White; 17.05.2009
comment
Способ прокрутки представления зависит от того, как представление может потреблять информацию, предоставляемую виртуальной машиной. Например, если все текстовые поля находятся в коллекции, привязанной к списку, вы можете прокручивать представление, задав selectedIndex для collectionView. - person treehouse; 17.05.2009