В качестве примера расматриваю контрол Panel с нарисованными на нем фигурами. Вся картина представляет собой круг, квадрат и линию (круг и квадрат заполненные) разного цвета, фигуры не пересекаются. Мне нужно реализовать алгоритм перетаскивания элементов с сохранением их цвета и форм во время перетаскивания.
Кто уже исследовал подобную проблему, или кто уже знает решение, посоветуйте либо в каком направлении копать, либо само решение, пожалуйста?
Сейчас думаю о нескольких направлениях:
1) копирование изображения по форме фигуры и вывод копии по верх основного изображения в момент перетаскивания;
2) перерисовка фигуры в соответствии с положением мыши.
Так же думаю, что вероятно стандартных методов Graphics не достаточно для реализации задуманного...
В общем, хочется услышать советы тех, кто решал или решает подобные задачи.
Пока я обошелся перетаскиванием контура, но это очень уж простой способ.
Здравствуйте, Saruwatari, Вы писали:
S>В качестве примера расматриваю контрол Panel с нарисованными на нем фигурами. Вся картина представляет собой круг, квадрат и линию (круг и квадрат заполненные) разного цвета, фигуры не пересекаются. Мне нужно реализовать алгоритм перетаскивания элементов с сохранением их цвета и форм во время перетаскивания. S>Кто уже исследовал подобную проблему, или кто уже знает решение, посоветуйте либо в каком направлении копать, либо само решение, пожалуйста? S>Сейчас думаю о нескольких направлениях: S>1) копирование изображения по форме фигуры и вывод копии по верх основного изображения в момент перетаскивания; S>2) перерисовка фигуры в соответствии с положением мыши. S>Так же думаю, что вероятно стандартных методов Graphics не достаточно для реализации задуманного... S>В общем, хочется услышать советы тех, кто решал или решает подобные задачи. S>Пока я обошелся перетаскиванием контура, но это очень уж простой способ.
Наследуйся от Control и реагирй на события мыши. С самими изображениями возиться не надо, в итоге что-то типа Control получится.
Здравствуйте, Saruwatari, Вы писали:
S>В качестве примера расматриваю контрол Panel с нарисованными на нем фигурами. Вся картина представляет собой круг, квадрат и линию (круг и квадрат заполненные) разного цвета, фигуры не пересекаются. Мне нужно реализовать алгоритм перетаскивания элементов с сохранением их цвета и форм во время перетаскивания. S>Кто уже исследовал подобную проблему, или кто уже знает решение, посоветуйте либо в каком направлении копать, либо само решение, пожалуйста? S>Сейчас думаю о нескольких направлениях: S>1) копирование изображения по форме фигуры и вывод копии по верх основного изображения в момент перетаскивания; S>2) перерисовка фигуры в соответствии с положением мыши. S>Так же думаю, что вероятно стандартных методов Graphics не достаточно для реализации задуманного... S>В общем, хочется услышать советы тех, кто решал или решает подобные задачи. S>Пока я обошелся перетаскиванием контура, но это очень уж простой способ.
Вообще, по-хорошему, смотреть надо в сторону WPF — там эти квадраты и круги являются объектами и можно изменяя их свойства влиять на отображаемую картинку. Что-то в этом духе было еще в Builder — там был такой визуальный элемент Shape, который мог быть квадратом, кругом и т.д. и у которого можно было менять кое-какие параметры. WPF — значительно более продвинутое решение на эту тему. Для проверки нажатия там используется механизм HitTest.
Если речь идет всенепременно о Windows.Forms, то там я бы исходил из такого паттерна:
1) На форму добавляется PictureBox
2) Для PictureBox добавляется обработчик события перерисовки OnPaint
3) Создаётся своя система классов типа VisualObject => VisualRectangle, VisualEllipse и т.д. по необходимость
При этом в базовом классе определяется чистая виртуальная функция Draw(Graphics g), которая в наследных объектах реализуется с использованием стандартных методов рисования типа FillRectangle() и т.п. Кроме этого классы содержат такие вещи как координаты, цвет и т.п. по вкусу. Естественно, всё это распределяется по системе классов по мере появления.
4) Создаётся список объектов базового типа. Что-то вроде
[с#]
List<VisualObject> visualObjects = new List<VisualObject>();
[/с#]
и в этот список добавляются нужные объекты производных классов с нужными параметрами (интерактивно или из файла или руками). Например так:
[с#]
visualObjects.Add(new VisualEllipse(10, 10, 100, 100, Color.Red /*...и т.п. по вкусу, как это объявлено в конструкторе...*/);
visualObjects.Add(new VisualRectangle(100, 100, 150, 100, Color.Green /*...и т.п. по вкусу, как это объявлено в конструкторе...*/);
...
[/c#]
По сути такой список составляет основу документа, который можно легко сохранять на диск и читать с диска.
5) Функция OnPaint получится до безобразия простой:
...
// Здесь заливается белым цветом вся площадь рисункаforeach (VisualObject vo in visualObjects)
vo.Draw(e.Grapnics); // e <-> PaintEventArgs, передаваемый в функцию OnPaint, в том числе содержит графику
И всё!
Теперь насчёт перемещения.
Объект, который нужно перемещать изымается из списка и его прорисовка выполняется в той же функции OnPaint после вышеприведённого фрагмента, если такой объект имеется. Ещё лучше, реализовать такое изымание через Drag and Drop, но можно и без этого. При этом при отрисовке можно также использовать, например, полупрозрачное рисование с альфа-каналом. Ну в общем, простор есть
6) Ну и естественно, нужно прикрутить сюда работу с мышью
OnMouseDown — Найти объект, на котором совершен щелчок. Для этого в базовом классе для объектов нужно сделать ещё одну виртуальную функцию вроде bool HitTest(Point pt), которая вернет истину, если точка попадает в объект. В наследных классах она пишется в соответсвии со спецификой. Эта функция в момент щелчка вызывается также через foreach для каждого объекта и если объект найден он и изымается из списка и рисуется дальше отдельно.
OnMouseMove — делает Refresh()
OnMouseUp — помещает объект обратно в список с уже подкорректированными координатами.
В общем, это общий план действий. Если что не понятно, спрашивайте
Здравствуйте, DirectX, Вы писали:
... DX>Объект, который нужно перемещать изымается из списка и его прорисовка выполняется в той же функции OnPaint после вышеприведённого фрагмента, если такой объект имеется. Ещё лучше, реализовать такое изымание через Drag and Drop, но можно и без этого. При этом при отрисовке можно также использовать, например, полупрозрачное рисование с альфа-каналом. Ну в общем, простор есть
...
Меня интересует именно этот момент.
Я думал именно о таком подходе. Но мне кажется здесь происходит перерисовка объекта, т.е. при сдвиге элемента, приходится перерисовать все отображаемые элементы, а потом перетаскиваемый. Возможно, есть "более интеллектуальный" подход, а может и нет... Поэтому и возник вопрос.
Здравствуйте, Saruwatari, Вы писали:
S>Меня интересует именно этот момент. S>Я думал именно о таком подходе. Но мне кажется здесь происходит перерисовка объекта, т.е. при сдвиге элемента, приходится перерисовать все отображаемые элементы, а потом перетаскиваемый. Возможно, есть "более интеллектуальный" подход, а может и нет... Поэтому и возник вопрос.
Я так думаю, способы существуют, но принципиально способ именно такой — рисование списка с использованием виртуальных функций. Проще это в .NET 3.0 и WPF.
А из способов оптимизации такого подхода могу посоветовать следующий:
1) Вводится переменная, показывающая, что включен режим перетаскивания (она в любом случае нужна). Она == true после OnMouseDown и == false после OnMouseUp.
2) Функция рисования разделяется на 2 способа:
а. если эта переменная false, то рисуется всё обычным образом. Не стоит беспокоиться в этом случае за производительность — всё равно перерисовка будет идти не часто (если там анимаций тем более нет), кроме этого, перерисовывается всё равно участок окна, подвергшийся изменениям, что само по себе оптимизируется. Т.е. если окно не перекрывается в процессе работы, то вообще ничего перерисовываться не будет.
б. если включён режим перетаскавания, то в момент нажатия OnMouseDown имеет смысл нарисовать один раз весь список кроме объекта в Bitmap! После этого, else в функции OnPaint, отвечающий за рисование в случае перетаскивания foreach содержать не будет, а там будет тупо рисование битмапа + рисование поверх него объекта. При этом общее количество объектов уже будет без разницы.
Если ещё важен порядок объектов, то можно создать двa bitmap — один с объектами до, другой с объектами после (при этом нужно, конечно, соблюсти прозрачность фона). И рисутеся сначала группа нижних объектов, потом перемещаемый, потом вышележащие.
Здравствуйте, Saruwatari, Вы писали:
S>В качестве примера расматриваю контрол Panel с нарисованными на нем фигурами. Вся картина представляет собой круг, квадрат и линию (круг и квадрат заполненные) разного цвета, фигуры не пересекаются. Мне нужно реализовать алгоритм перетаскивания элементов с сохранением их цвета и форм во время перетаскивания. S>Кто уже исследовал подобную проблему, или кто уже знает решение, посоветуйте либо в каком направлении копать, либо само решение, пожалуйста? S>Сейчас думаю о нескольких направлениях: S>1) копирование изображения по форме фигуры и вывод копии по верх основного изображения в момент перетаскивания; S>2) перерисовка фигуры в соответствии с положением мыши. S>Так же думаю, что вероятно стандартных методов Graphics не достаточно для реализации задуманного... S>В общем, хочется услышать советы тех, кто решал или решает подобные задачи. S>Пока я обошелся перетаскиванием контура, но это очень уж простой способ.
А чем плох для этих целей Microsoft Visual Basic 2005 Power Packs — там контролами с простеньким градиентом, заливкой и реакйией на события выделены: линия, окружность, прямоугольник?
Здравствуйте, Acemore, Вы писали:
A>А чем плох для этих целей Microsoft Visual Basic 2005 Power Packs — там контролами с простеньким градиентом, заливкой и реакйией на события выделены: линия, окружность, прямоугольник?
Тем, что реально фигуры будут немного сложнее простых, если коротко.
После некоторых мытарств в указанной области решил написать собственный контролы, унаследовавшись от Control. Т.к. Control уже содержит функции работы с мышью, собственной отрисовкой, а кроме того, к нему можно привязать другой контрол, мне представилась заманчивой перспектива их использования. Остается только правильно отрисовать в OnPaint и аналогичных методах. Но здесь возникли проблемы. Во-первых, контролы могут быть отличными от прямоугольных и, как следствие, они должны правильно себя отрисовывать и правильно обрабатывать мышь. И во-вторых, при перетаскивании над таким контролом другого, на нижнем остается след от перетаскиваемого контрола. Пока не понял как это все побороть. Если у кого есть идеи поделитесь, пожалуйста.
На счет обработки мыши, думаю, в WindProc прерывания надо обрабатывать. Но пока понимание теоретическое. Надо пробовать.
Здравствуйте, Saruwatari, Вы писали:
S>После некоторых мытарств в указанной области решил написать собственный контролы, унаследовавшись от Control. Т.к. Control уже содержит функции работы с мышью, собственной отрисовкой, а кроме того, к нему можно привязать другой контрол, мне представилась заманчивой перспектива их использования. Остается только правильно отрисовать в OnPaint и аналогичных методах. Но здесь возникли проблемы. Во-первых, контролы могут быть отличными от прямоугольных и, как следствие, они должны правильно себя отрисовывать и правильно обрабатывать мышь. И во-вторых, при перетаскивании над таким контролом другого, на нижнем остается след от перетаскиваемого контрола. Пока не понял как это все побороть. Если у кого есть идеи поделитесь, пожалуйста. S>На счет обработки мыши, думаю, в WindProc прерывания надо обрабатывать. Но пока понимание теоретическое. Надо пробовать.
Думаю, что способов на самом деле больше одного, но всё-таки использование наследования от Control хотя и возможно, но видится ошибочным. Вы и сами отмечаете, что при отличной от прямоугольника форме уже есть проблемы. Естественно будут, поскольку любой контрол по своей сути является окном, имеющим hWnd. В этом можно легко убедиться, если посмотреть на готовую программу с помощью Spy++. Кроме этого, понятно, почему приходится WndProc использовать — ведь использовать MouseMove для контрола не получится, а в MouseMove контейнера он не сработает, когда мышь над другим контролом.
Всё же мне кажется особого велосипеда изобретать не стоит. Наивно предполагать, что, например, в CorelDraw картинка использует дескриптор окна для каждой кривульки и фигурки.
Т.е. использование контролов в понимании Win32 — это крайне непродуктивный подход — может быть он и возможен, но это всё равно, что для отображения фотографий использовать извращение типа такого:
Panel[,] picture = new Panel[320,240];
int k = 0;
foreach (Panel pnl in picture)
{
pnl = new Panel();
pnl.Width = 1;
pnl.Height = 1;
pnl.Left = k % 320;
pnl.Top = k / 320;
k++;
pnl.BackColor = ...;
}
Все жё посмотрите в сторону WPF. Там для такой задачи лучше всего подходит контейнер Canvas — для абсолютного позиционирования большого числа контролов. И контролы там безоконные, что очень важно, и рисование будет однозначно без мерцания, и возможностей по HitTest'ингу — масса.
Re[3]: Перетаскивание нарисованных объектов
От:
Аноним
Дата:
14.10.07 16:44
Оценка:
DX>Все жё посмотрите в сторону WPF. Там для такой задачи лучше всего подходит контейнер Canvas — для абсолютного позиционирования большого числа контролов. И контролы там безоконные, что очень важно, и рисование будет однозначно без мерцания, и возможностей по HitTest'ингу — масса.
Если не сложно, плиз, киньте в меня ссылкой для начинающих по WPF: что это такое, как его прикрутить к проекту, где взять доку, какой там рантайм и т.п.
Здравствуйте, Аноним, Вы писали:
DX>>Все жё посмотрите в сторону WPF. Там для такой задачи лучше всего подходит контейнер Canvas — для абсолютного позиционирования большого числа контролов. И контролы там безоконные, что очень важно, и рисование будет однозначно без мерцания, и возможностей по HitTest'ингу — масса.
А>Если не сложно, плиз, киньте в меня ссылкой для начинающих по WPF: что это такое, как его прикрутить к проекту, где взять доку, какой там рантайм и т.п.
Вот для первого знакомства книжка подойдёт. Там про .NET 3 только одна глава, но для начала самое оно. Еще на толковые книжки натыкался, но ссылки не помню.
Для использования этого дела нужен Vista Platform SDK — либо с MS скачивается, либо в осле должен быть. Но он большой — распространяется в виде образа (*.img или *.iso) и занимет больше гигабайта. Там почти все, что нужно есть. Плюс надо поставить для студии небольшое дополнение, чтобы в проектах появилось.
На счет перетаскивания таких контролов не могу согласиться. Я это делал.
Согласен с тем, что по использованию ресурсов и по скорости, это возможно менее оптимальный подход, но рисование элементов — это еще больший "велосипед", чем использование наследования от Control.
За совет по WPF большое спасибо. И за ссылку. В данном конкретном проекте я, к сожалению, не могу им воспользоваться из-за нехватки времени на изучение WPF.
PS: Человек, желающий написать векторный редактор, и я — разные люди. Мой проект ближе к Visio.
Здравствуйте, Saruwatari, Вы писали:
S>Мой проект ближе к Visio.
Тогда только рисование.
Примерно так:
Объекты полотна отрисовываются по OnPaint, при этом текущий (выделенный), ниже- и вышележащие элементы буферизуются по-отдельности.
Манипуляции совершаются с помощью т.н. режимов: каждый такой режим обрабатывает необходимые события таким образом, что режим по-умолчанию (перетаскивание и масштабирование элементов) обрабатывает соответствующим образом события мыши, а по OnPaint добавляет на полотно маркеры для выделенного объекта, а режим добавления нового объекта позволяет начертить мышью новый объект, уже по-своему обрабатывая события.
Здравствуйте, Марсель абый, Вы писали:
МА>Здравствуйте, Saruwatari, Вы писали:
S>>Мой проект ближе к Visio.
МА>Тогда только рисование.
МА>Примерно так: МА>
МА>Объекты полотна отрисовываются по OnPaint, при этом текущий (выделенный), ниже- и вышележащие элементы буферизуются по-отдельности.
МА>Манипуляции совершаются с помощью т.н. режимов: каждый такой режим обрабатывает необходимые события таким образом, что режим по-умолчанию (перетаскивание и масштабирование элементов) обрабатывает соответствующим образом события мыши, а по OnPaint добавляет на полотно маркеры для выделенного объекта, а режим добавления нового объекта позволяет начертить мышью новый объект, уже по-своему обрабатывая события. МА>
МА>Немного об этом здесь и здесь.
Я понимаю желание рисовать все в OnPaint, но унаследовавшись от Control, я зделал все необходимое за два часа, при этом получив возможность очень легко элементы сделать интерактивными, в перспективе. но у всего есть плюсы и минусы...
Здравствуйте, DirectX, Вы писали:
DX>Взял набросал небольшую программку, чтобы пояснить рисование через OnPaint. Пример далеко не полный, но основные моменты там есть:
DX>Пример
DX>
Спасибо, за пример, но мне нужно было не это.
Попробую объяснить еще раз (хотя, я уже разобрался как мне следует поступать).
В общем, проблем с постороением архитектуры системы рисования, написанием алгоритмов, реализующих эту систему у меня не было. Мне хотелось услышать описание следующего алгоритма в C# терминах:
1) Перед перетаскиванием фигуры, экран перерисовывается без неё и фотографировался в буфер
2) При перетаскивании совершаются шаги:
— копирование области из буфера в отображаемую память, соответствующей текущему положению фигуры (таким образом мы затрем ее без потерь)
— отрисовка фигуры в новом положении
Собственно, все. Мне кажется, что такой алгоритм даст выигрыш в скорости отрисовки. Но, возможно, он уже реализован системой, а возможно, есть уже метода его реализации. Так же вероятно, что я ошибаюсь в его эффективности.
Мне интересно это.
Но должен отметить, что если бы я пошел все-таки по пути "собственноручного рисования", то мне пришлось бы решать еще задачи взаимодействия фигур с мышью и пр. и пр. Унаследовавшись от контролов я рисую только формы и содержимое, ну еще рамки выделения, которые приходится рисовать на Desctop'е, к сожалению.