У меня есть приятная работающая функция редактирования-отмены в моем приложении winforms. Он работает с использованием класса CommandStack
, который равен двум Stack<IStateCommand>
s (один для отмены, один для повторения). Каждая команда имеет метод Execute
и Undo
, а сам объект CommandStack
имеет событие, которое вызывается при изменении стеков.
CommandStack
также работает, если метод LogCommand вызывается из его собственной функции Undo и, следовательно, добавляет его в стек повторного выполнения, а не в стек отмены. Это можно сделать, просто добавив текущий ManagingThreadId
к объекту List<int>
, а затем удалив его после завершения команды отмены (в отличие от использования трассировки стека, которая, я считаю, будет намного медленнее и немного грязной).
В моем приложении много разных команд, так что эта формула как бы задуманна, так как мне потребуется несколько дней, чтобы переделать все эти реализации IStateCommand
.
Единственная проблема с этим, в настоящее время некоторые события пользовательского интерфейса также вызывают другие события пользовательского интерфейса, оба из которых записывают IStateCommand
в историю отмен. Есть ли способ в C #, который я могу обнаружить, если функция LogCommand уже была вызвана из того же события пользовательского интерфейса (Click, DragDrop, SelectedIndexChanged, TextChanged и т. Д.), Тогда я могу объединить команды в одну команду (используя мой CommandList
класс, который также наследует IStateCommand
)?
Я думал о том, чтобы сохранить текущее время при вызове события отмены, а затем, если следующая команда будет зарегистрирована менее, чем через x миллисекунд, объединить их в истории, но это выглядит немного неаккуратно. Я также рассмотрел поиск трассировки стека, но я не знаю, что искать, чтобы найти событие корневого интерфейса пользователя, и не знаю, скажу ли я разницу между одним нажатием кнопки, а затем другим щелчком по тому же кнопка.
Также может быть полезно знать, что все эти команды вызываются из потока пользовательского интерфейса из обработчиков событий (в основном из событий из пользовательских элементов управления). Единственная часть моего приложения, которая использует другой поток, запускается после большинства событий пользовательского интерфейса, после того, как журнал отмены зарегистрирован.
Спасибо!
Сортировка
Один и тот же метод вызывается дважды из одного и того же события пользовательского интерфейса (например, MouseUp, DragDrop). Во второй раз, когда вызывается этот метод, как я могу проверить, что он уже был вызван одним и тем же событием интерфейса?
Редактировать: решение (вроде)
Это немного грязно, так как у меня нет времени, чтобы полностью переписать эту систему. Однако я реализовал его таким образом, чтобы в будущем не было такой грязной работы.
Решение основано на одном из комментариев Эрно к его ответу (поэтому я отмечу его ответ как принятый), где он предлагает добавить параметр. Я добавил еще одну перегрузку к своему LogCommand(IStackCommand)
методу в классе CommandStack
, LogCommand(IStackCommand, string)
. Строка - это actionId, который сохраняется для каждой команды, и если эта строка совпадает с последней, команды объединяются. Это дает возможность пройти через каждое событие и дать уникальный идентификатор.
Однако, грязная часть - чтобы заставить его работать, прежде чем мы покажем клиенту, actionId
по умолчанию System.Windows.Forms.Cursor.Position.ToString()
, ой! Поскольку позиция курсора не изменяется во время выполнения потока пользовательского интерфейса, это объединяет каждую команду. На самом деле он даже объединяет команды TextChanged (если они не двигают мышью!)