Рисование поверх CEdit

Модераторы: Romeo, Hawk, Absurd, WinMain, DeeJayC

Ответить
Albor
Сообщения: 482
Зарегистрирован: 06 сен 2004, 13:34
Откуда: Днепропетровск

11 окт 2007, 17:42

Задача такая: в поле редактирования текста (CEdit или RichEdit) нужно подчёркивать некоторые места в тексте цветом отличным от цвета текста (вроде как MSWord подчёркивает ошибки). Казалось бы, перехватить сообщение WM_PAINT и после его обработки дорисовать поверх текста всё что нужно, но оказалось что CEdit рисует текст каким-то другим способом. Даже при запуске приложения данный контрол не получает вышеуказанного сообщения. Я решил задачу так: унаследовал от CEdit класс, в котором определил свою ф-цию рисования, вызываемую по сообщению WM_USER+... При получении сообщений о перерисовке или изменении контрола или при выделении текста делаю PostMessage(WM_USER+...). То есть, рисую после того, как определённые сообщения уже обработаны. Всё, вроде бы, работает. Хотелось бы узнать: как было бы правильно подойти к решению такой задачи.
Albor
Сообщения: 482
Зарегистрирован: 06 сен 2004, 13:34
Откуда: Днепропетровск

20 ноя 2007, 17:39

Вобщем, вопрос не о рисовании, а о проблеме возникшей при релизе. Ввожу своё сообщение и обработчик в классе, унаследованном от RichEdit. Всё рисуется прекрасно, но в debug-режиме. Компилирую релиз и сразу сплошные отказы, в частности, как мне удалось выяснить, некорректно работают функции SetWindowText и GetWindowText. Причём, если убираю постановку в очередь своего сообщения, всё начинает работать корректно (но, естественно, то ради чего это задумывалось, отключается). Своё сообщение посылаю из PreTranslateMessage таким образом: if(pMsg->Message==WM_PAINT) PostMessage(WM_MYMESSAGE...); Что касается SetWindowText, то данная функция для RichEdit вызывается другим способом - через переменную CWnd m_pCtrlSite, но данный момент я обошёл вызовом одноимённой API функции, однако при закрытии приложения возникают исключительные ситуации, связанные всё с той же переменной из CWnd. Самое интересное в том, что все проблемы связяны только с постановкой в очередь моего сообщения (Даже если обработчик данного сообщения ничего не делает). Кто может прокоментировать?
Аватара пользователя
Romeo
Сообщения: 3091
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

23 ноя 2007, 10:51

Из PreTranslateMessage? Ого! Какой жёсткий хардкод. Советую отсабклассить контрол и все проблемы должны пропасть. Если не знаешь что такое сабклассинг, расскажу. Кстати, на чём пишешь?
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Albor
Сообщения: 482
Зарегистрирован: 06 сен 2004, 13:34
Откуда: Днепропетровск

24 ноя 2007, 14:06

Расскажи, Romeo, а то в моих книжках об этом пара слов и не совсем понятно. Хотелось бы, так же, узнать чем "жёсток хардкод". Из PreTranslateMessage нельзя посылать сообщения? Пишу в VS 6 с использованием MFC.
Albor
Сообщения: 482
Зарегистрирован: 06 сен 2004, 13:34
Откуда: Днепропетровск

25 ноя 2007, 15:54

Почитал в MSDN о субклассинге, из чего понял, что это - подмена WndProc. Где перехватываются, если можно так выразится, сообщения и выполняется одно из действий: либо передаётся сообщение базовому классу, либо блокируется, либо выполнение каких-то действий с дальнейшей передачей сообщения в базовый класс. На первый взгляд - не сильно отличается от PreTranslateMessage. Больше чем уверен, что я не прав, иначе зачем придумали этот субклассинг. Но первые опыты с подменой процедуры закончились с теми же граблями.
Аватара пользователя
Romeo
Сообщения: 3091
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

25 ноя 2007, 23:21

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

Реализуется саклассинг, как ты верно подметил, простой подменой WndProc при программировании на чистом Win32 API. Библиотечные надстройки (MFC, ATL), естественно, предоставляют специальные методы для сабклассинга, например SubcalssWindow, так что о прямой подмене WinProc в этом случае не идёт. Так же замечу, что в WinXP появились специализированные функции уровня ядра для забклассинга, которые позволяют избежать некоторых минусов, таких как невозможность произвести повторный сабклассин уже отсабклассинного контрола и некоторые другие, которые налагались на старый подход. Благо в эти ограничения конечный программист упирается крайне редко и вариант простой подмены WndProc функционален для подовляющего большинства случаев. Подробности о разнице сабклассинга до WinXP и после его выхода можно прочитать здесь:

http://msdn2.microsoft.com/en-us/library/bb773183.aspx

Теперь по поводу "жёсткого харкода". Я сгоряча подумал, что ты засунул такую проверку прямо в главный цикл обработки сообщений приложения. Потому и сильно удивился. Если ты это делаешь в PreTranslateMessage самого контрола, то ничего страшного в этом нет, хотя, я настаиваю на этом, сабклассинг является более академичным подходом.

Какая разница между if'ом в PreTranslateMessega и написанием обработчика WM_PAINT в отсабклашеном классе-контроле? Для твоего крайне простого случая - никакой. Но сабклассинг является более мощныи средством и об этом следует помнить. В помощью сабклассинга можно обрабатывать так называемый message reflection в то время, как втискивая код в PreTranslateMessage класса-контрола это будет сделать не то, что не возможно, но крайне затруднительно.

Если ты говоришь, что попробовал сабклассинг, и всё равно не работает, то нужно разбираться в чём дело более детально. Предлагая скинуть сюда имплементацию WM_PAINT для твоего класса-контрола и также участок кода, в котором происходит сабклассинг. Попробую тебе помочь.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Albor
Сообщения: 482
Зарегистрирован: 06 сен 2004, 13:34
Откуда: Днепропетровск

26 ноя 2007, 10:18

Спасибо, Romeo. В настоящий момент всё заработало как надо. Я перегрузил ф-цию WndProc и уже из неё произвёл дорисовку, но уже не посылкой сообщения, а просто вызовом рисующей ф-ции. Наверное перегрузка WndProc - это и есть субклассинг в MFC? Во всяком случае, принцип тот же. Конечно, нужно ещё разобраться подробнее, но это, как говорится, дело наживное.
Аватара пользователя
Romeo
Сообщения: 3091
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

26 ноя 2007, 10:44

Явная подмена WndProc используется при программировании на чистом Win32 API. Если используешь MFC, то всё-таки желательно использовать методы SubclassWindow/UnsubclassWindow класса CWnd, от которого порождаются все классы-окна. Дело даже не в том что это более правильно, просто это более удобно. После вызова SubclassWindow все сообщения контрола изменяют маршрут и проходят сначала через message map твоего класса, а только потом попадают в старую WndProc отсабклашенного контрола. Причём это всё скрыто библиотекой MFC и тебе лишь остаётся добавить обработчик ON_WM_PAINT в message map твоего класса.

Если всё работает, то, конечно, можешь ничего не трогать. Я просто пытаюсь тебя научить делать так, как это принято делать :)
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
BBB
Сообщения: 1272
Зарегистрирован: 27 дек 2005, 13:37

26 ноя 2007, 14:10

Romeo, а в чем практическая разница использования SubclassWindow от создания своего нового класса - наследника от CWnd. Точнее - наследника от наследника CWnd (напр., CButton, CDialog etc.) ? Ведь в этом случае тоже по своему желанию можно переопределить отбработку любого WIN-сообщения. А непереопределенный будут перенаправляться в обработчики базового класса.
Или это из серии "один и тот же результат можно получить разными способами"?
Аватара пользователя
Romeo
Сообщения: 3091
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

26 ноя 2007, 15:04

Как ты привязываешь объект твоего класса к контролу? Если ты используется DDX_Control, то внутри него вызывается SubclassWindow :)
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Ответить