Модераторы: Partizan, gambit
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Реализация Undo, Redo в графическом редакторе, Нужен совет 
:(
    Опции темы
Anyone
Дата 7.11.2009, 13:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 218
Регистрация: 1.6.2008

Репутация: нет
Всего: нет



Необходимо реализовать команды Undo, Redo в графическом редакторе.
Существует несколько вариантов решения задачи: паттерны  CommandMemento.
Первый вариант мне больше нравится, потому что я так понимаю, что он более производительный, меньше занимает памяти, но в то же время, реализация является затруднительной, потому что многие действия не являются командами (к примеру, изменения положения и размеров элементов редактора), кроме того необходимо полностью самостоятельно определить логику команд.
Потому решил использовать паттерн Memento. Но и в этом случае столкнулся с проблемами:
1. Нужно правильно определить когда следует и когда не следует сохранять состояние
2. Проблема производительности. При сохранении состояния, я сохраняю программу в формате XML (структуру определяю сам, использую LINQ, Xaml сериализация не может сохранить полностью состояние, ввиду ограниченности), при восстановлении - выполняется синтаксический разбор XML-документа  (опять же использую LINQ).

Если первая проблема не крититична, решаема, то вторая - более серьезная. Если в редакторе около 100 объектов, то десериализация занимает около 3.5 сек, что при выполнении комманды Undo слишком много.

Есть какие-нить советы по поводу этого?
У меня есть мысль, что можно в фоновом потоке выполнять десериализацию нескольких элементов стека, где хранятся состояния, но при этом возникнут другие проблемы, а если часто выполнять команду, то производительность может только ухудшиться.
PM MAIL   Вверх
jonie
Дата 7.11.2009, 15:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 5613
Регистрация: 21.8.2005
Где: Владимир

Репутация: 22
Всего: 118



Anyone, а что насчет сериализации НЕ в xml ?


--------------------
Что-то не поняли? -> Напейтесь до зеленых человечков... эта сверхцивилизация Вам поможет...
PM MAIL Jabber   Вверх
Anyone
Дата 7.11.2009, 16:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 218
Регистрация: 1.6.2008

Репутация: нет
Всего: нет



Цитата(jonie @  7.11.2009,  15:53 Найти цитируемый пост)
Anyone, а что насчет сериализации НЕ в xml ?

Да, пробывал бинарную, но не все внутренние классы моего контрола сериализуемые, потому сохранить его как целый объект не удалось, хотя появилась идея сериализировать не сам контрол, а создать свой класс, с полями наподобии моей структуры XML файла. Может и глупо, но должно быстрее работать.

Забыл упомянуть что использую WPF.

Это сообщение отредактировал(а) Anyone - 7.11.2009, 16:38
PM MAIL   Вверх
tol05
Дата 7.11.2009, 20:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1632
Регистрация: 21.12.2006
Где: Харьков

Репутация: 63
Всего: 170



что-то я не все понял. Как паттерн Хранитель может заменить паттерн Команда? Что вообще между ними общего? Если хранитель - работает с состоянием, а команда - с поведением?

Да, конечно, историю комманд нужно хранить. Если она не помещается в память, то ее нужно хранить, используя паттерн Хранитель. Но в этом случае паттерн Хранитель выступает в роли дополнительного инструмента, но никак - не как альтернатива самой Команде. 
Допустим нам нужна возможность быстрого прохода по истории комманд... Но мы же не будем говорить, что паттерн Итератор тоже является альтернативой Комманде? )

Одним словом... я считаю что альтернативе паттерну Команда в данном случае нет.

Что же касается механизма... То я бы не стал вообще хранить состояние ни в каком виде. Это избыточно. В конце концов нам не требуется восстанавливать всю графику с нуля при каждом Undo\Redo. А всего-лишь отменить\восстановить последнее действие. Поэтому хранить нужно параметры и имена комманд.
Даже если нужно будет восстанавливать всю графику, сохраненную с предыдущей сессии, при старте новой сессии.... То, во первых, это не Undo\Redo будет... А во вторых - все равно рисование будет последовательным, т.е. это будет представлять собой последовательное выполнение восстановленных из хранилища комманд.

В самом общем случае (без привязки к технологиям) : есть Представление, Модель и какой-угодно Контроллер. Как они связаны - не важно вообще. В результате действия пользователя Представление инициирует команду Контроллера, который работает с Моделью и отправляет результыты Представлению обратно.
Каждая команда должна, кроме своих основных функций, иметь возможность отката всех произведенных ею действий. Она может хранить в себе параметры, которые ей были переданы. И на основе них производить свой откат.
Список комманд хранится в хранилище комманд (с которым, кстати, может работать как раз Хранитель). 

У контроллера также есть комманды Undo\Redo, которые просто перемещают указатель комманд хранилища (назначают текущей новую команду) и вызывают:
- или эту новую текущую команду Контроллера
- или откат прежней команды (которая была текущей до моменты вызова Undo)

Т.е. все отличие только в том, что команды Контроллера использует не Представление, а сам Контроллер (поскольку Представление может формировать только текущие параметры для комманд, но уж никак не для Redo или Undo)

Это сообщение отредактировал(а) tol05 - 7.11.2009, 20:29


--------------------
На хорошей работе и сны хорошие снятся.
PM MAIL   Вверх
Anyone
Дата 7.11.2009, 21:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 218
Регистрация: 1.6.2008

Репутация: нет
Всего: нет



Цитата(tol05 @  7.11.2009,  20:28 Найти цитируемый пост)
что-то я не все понял. Как паттерн Хранитель может заменить паттерн Команда? Что вообще между ними общего? Если хранитель - работает с состоянием, а команда - с поведением?

Я сохраняю не саму команду, а состояние графического редактора, а точнее его элементов.

Цитата(tol05 @  7.11.2009,  20:28 Найти цитируемый пост)
Что же касается механизма... То я бы не стал вообще хранить состояние ни в каком виде. Это избыточно.

Но в то же время и проще. Причем намного.

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

Потому попытаюсь заменить XML-сериализацию на бинарную (буду сохранять такую же структуру, что и в  XML, не целый объект), и если производительность будет удовлитворительной (в чем я сомневаюсь), так и оставлю. Если нет, буду думать по поводу  патерна Команда.

tol05
По поводу остальных паттернов - спасибо, ознакомлюсь для общего развития, но в рамках текущего проекта это избыточно.

Спасибо всем за идеи.

PM MAIL   Вверх
tol05
Дата 7.11.2009, 23:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1632
Регистрация: 21.12.2006
Где: Харьков

Репутация: 63
Всего: 170



Цитата(Anyone @  7.11.2009,  20:56 Найти цитируемый пост)
tol05, 
По поводу остальных паттернов - спасибо, ознакомлюсь для общего развития, но в рамках текущего проекта это избыточно.
 зря... не хотел обидеть...

все таки я думаю что сохранение состояния - это крайне неправильная идея.... Особенно при испольовании WPF ! С его-то возможностями...

что я имею в виду:
Цитата(Anyone @  7.11.2009,  20:56 Найти цитируемый пост)
Я сохраняю не саму команду, а состояние графического редактора, а точнее его элементов.
[всех конечно?]
Т.к. команды Undo\Redo могут вызываться пользователем каким угодно образом. Например
Shape89-Shape90-Undo-Undo-Redo-Redo-Undo-Undo-Redo-Shape50_1-Undo-Undo... и так - до бесконечности

то, как я понимаю, Вы, Anyone, собираетесь каждый раз обнулять графический контейнер, поднимать файл, считывать его в память и перерисовывать все объекты?! (после чего все объекты заново сохранять в файл, конечно же)

А не проще ли просто в случае Undo при помощи команды обратиться к контейнеру (Canvas-у например) с просьбой удалить из своих Children объект Shape89 (к примеру) и не трогать остальные 999 тысяч других Shapes???
А в случае Redo добавить в тот же контейнер только один Shape, а не стирать все 999 тысяч Shapes чтобы сразу же их нарисовать обратно + тот несчастный Shape89...

P.S. пара комментариев еще

Цитата(Anyone @  7.11.2009,  20:56 Найти цитируемый пост)
в моем случае реализация затруднена, потому что многие действия в редакторе не являются коммандами (то же перемещение, вращение элементов, изменения размеров)
 сделайте эти действия коммандами вот и все. Как мне кажется, логически они под этот термин подпадают (вспоминаю Corel и Photoshop )  )
Цитата(Anyone @  7.11.2009,  20:56 Найти цитируемый пост)
сами элементы достаточно сложные, откат команды может забрать не меньше ресурсов, чем восстановление состояния (к примеру, после перемещения или удаления большого ко-ва элементов).
а что, опросить сложный объект, узнать его сложное внутреннее состояние (которое как-бы уже и не очень внутренним становится), подготовить эти данные к сохранению, сохранить, считать, подготовить рисование по этим данным - это все не сложно? Ну а про удаление большого кол-ва объектов... Это будет самой популярной операцией в Вашем редакторе.

Это сообщение отредактировал(а) tol05 - 7.11.2009, 23:12


--------------------
На хорошей работе и сны хорошие снятся.
PM MAIL   Вверх
Anyone
Дата 8.11.2009, 09:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 218
Регистрация: 1.6.2008

Репутация: нет
Всего: нет



Цитата(tol05 @  7.11.2009,  23:11 Найти цитируемый пост)
сделайте эти действия коммандами вот и все. Как мне кажется, логически они под этот термин подпадают (вспоминаю Corel и Photoshop )  )

Я бы давно это сделал, если бы за прорисовку отвечал я. Но WPF сам за меня это делает - я лишь определяю внешний вид.
На самом деле вся сложность паттерна Комманда заключается в том, что я изначально не подготовил для него фундамент, придется много чего переделывать\доделывать. Но если производительность паттерна Хранитель меня не устроит (пользователь будет оценивать не способ реализации, а производительность, так зачем тратить кучу времени, если будет работать не плохо?), придется все таки это сделать.


Цитата(tol05 @  7.11.2009,  23:11 Найти цитируемый пост)
tol05, По поводу остальных паттернов - спасибо, ознакомлюсь для общего развития, но в рамках текущего проекта это избыточно. зря... не хотел обидеть...

Почему я должен обижаться, наоборот, спасибо за идеи.
PM MAIL   Вверх
math64
Дата 9.11.2009, 12:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: нет
Всего: 72



Можно добавить в окно скрытый (Rich)TextBox - пусть он занимается сохранением состояний для Undo/Redo, нужно только научиться синхронизировать состояение твоей канвы с текстом в TextBox
PM   Вверх
Anyone
Дата 9.11.2009, 16:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 218
Регистрация: 1.6.2008

Репутация: нет
Всего: нет



Цитата(math64 @  9.11.2009,  12:36 Найти цитируемый пост)
Можно добавить в окно скрытый (Rich)TextBox - пусть он занимается сохранением состояний для Undo/Redo, нужно только научиться синхронизировать состояение твоей канвы с текстом в TextBox

Не совсем понятно зачем текст бокс, ведь можно использовать, для этого свой класс-одиночку? Что в нем хранить, ведь редактор то графический? Список комманд чтоле?
PM MAIL   Вверх
math64
Дата 10.11.2009, 09:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: нет
Всего: 72



Наверно, у тебя есть команда Save где ты сохраняешь графику в виде xml? Вот этот xml и можно хранить в TextBox, а Undo/Redo для него уже реализованы. Но нужно уметь быстро синхронизировть этот xml с графикой, что может оказаться непростой задачей.
PM   Вверх
Anyone
Дата 10.11.2009, 10:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 218
Регистрация: 1.6.2008

Репутация: нет
Всего: нет



Цитата(math64 @  10.11.2009,  09:27 Найти цитируемый пост)
Но нужно уметь быстро синхронизировть этот xml с графикой, что может оказаться непростой задачей.

Так именно в этом вся проблема, процесс десериализации XML-документа достаточно медленный, если много элементов. Еще хочу попробывать реализовать интерфейс  ISerializable и использовать бинарную сериализацию, а потом, если не устроит, буду думать по поводу паттерна Команда.
PM MAIL   Вверх
math64
Дата 11.11.2009, 09:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: нет
Всего: 72



Бинарная сериализация ускорит дело, но для большого документа всё равно будет медленно. Нужно сериализовать только те части, которые изменились. Возможно, RichTextBox будет удобнее - каждому графическому объекту задать в соответстие параграф с помощью присоединённого свойства или Map, хранящейся где-то отдельно. Сам объект может сохраняться не в xml.
Создание нового объекта - в RichTextBox появляется новый параграф, который сопоствавляется с этим объектом.
Изменение объекта - вносятся изменения в параграф
Удаление - параграф удаляется.
Undo/Redo - вносятся изменения в объекты, чьи параграфы изменились, удаляются объекты, чьи параграфы удалились, создаются объекты для новых параграфов.

Добавлено через 8 минут и 24 секунды
Ещё можно сохранять редактируемые объекты в базу данных - так придётся делать для ОЧЕНЬ больщого документа, RichTextBox будет требовать слишком много памяти.
PM   Вверх
Anyone
Дата 11.11.2009, 09:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 218
Регистрация: 1.6.2008

Репутация: нет
Всего: нет



math64
Интересная идея, нужно попробывать.
По поводу базы данных. А что если ее хранить в датасете, один объект - одна запись, скажем айдишка и свойства, сохранение программы - это сериализация датасета. А синхронизировать БД с редактором в отдельном потоке. Я правильно мыслю?

PM MAIL   Вверх
Anyone
Дата 14.11.2009, 21:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 218
Регистрация: 1.6.2008

Репутация: нет
Всего: нет



Цитата(math64 @  11.11.2009,  09:25 Найти цитируемый пост)
RichTextBox будет удобнее - каждому графическому объекту задать в соответстие параграф с помощью присоединённого свойства или Map, хранящейся где-то отдельно. Сам объект может сохраняться не в xml.Создание нового объекта - в RichTextBox появляется новый параграф, который сопоствавляется с этим объектом.Изменение объекта - вносятся изменения в параграфУдаление - параграф удаляется.

Есть стандартные механизмы для реализации такой возможности, или нужно все самому продумать? Может есть какой-нить пример?
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Прежде чем создать тему, посмотрите сюда:
mr.DUDA
THandle

Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов.
Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :)
Так же не забывайте отмечать свой вопрос решенным, если он таковым является :)


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Общие вопросы по .NET и C# | Следующая тема »


 




[ Время генерации скрипта: 0.0973 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.