Win32: Как настроить рисование элемента управления Edit? - PullRequest
11 голосов
/ 24 декабря 2009

Мне нужно реализовать функциональность EM_SETCUEBANNER , где текстовая подсказка появляется внутри элемента управления Edit:

Example of cue banner in edit control

Суть в том, что я не могу использовать версию 6 Common Controls, которая необходима для того, чтобы получить поставляемую Microsoft реализацию баннера cue.

Я рассмотрел простое изменение текста элемента управления для редактирования и формата шрифта на

Dark Gray Italic Text

но он выдаст Изменить события ( обертка компонента, предоставленная библиотекой более высокого уровня ), которую я не могу найти способ избежать.

Так что вместо этого я собирался рисовать текст по собственному усмотрению, рисовать текст Cue Banner, когда элемент управления не сфокусирован и пуст, и в противном случае полагаться на рисование по умолчанию.

Элемент управления «Правка» не предоставляет удобный механизм рисования, , такие как ListView, TreeView и другие, предоставляют .

Другие люди изучили это , но это кажется почти невозможной задачей:

Судя по всему, я должны справиться со следующим Сообщения:

  • WM_ERASEBKGND, WM_PAINT (по понятным причинам)
  • WM_SETFOCUS, WM_KILLFOCUS (для предотвращения белая полоса от отображения - описано выше)
  • WM_CHAR (для обработки и обновления текст в элементе управления)

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

Это будет весело. : Rolleyes:

Учитывая, что элемент управления Windows Edit никогда не предназначался для пользовательского рисования: кто-нибудь знает, как создать пользовательский элемент управления Windows Edit?


Примечание : я также приму ответы, которые решают мою проблему, вместо того, чтобы отвечать на мой вопрос. Но любой, кто хочет сделать пользовательский элемент управления «Правка», сталкиваясь с этим вопросом, вероятно, хотел бы получить ответ.

Ответы [ 5 ]

9 голосов
/ 24 декабря 2009

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

Просто захватить WM_PAINT и WM_ERASEBKGROUND недостаточно, потому что элемент управления иногда будет отображать и другие сообщения.

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

Я помню в старые добрые времена, когда все использовали для создания подкласса элемент управления кнопками для добавления цвета, графики и т. Д. Дело в том, что однажды я сел и написал свой собственный класс окна кнопок. и это был МЕНЬШЕ КОДА, чем то, что у нас было в нашем исходном дереве для подкласса и пользовательского рисования кнопки Windows.

5 голосов
/ 18 июля 2015

Подклассы элемента управления EDIT работали хорошо для меня - нужно было отображать некоторую информацию о форматировании пользователю при редактировании атрибутов объекта (и некоторые атрибуты могут быть несколькими строками). Важная вещь, как сказал Адриан в своем ответе, также заключается в том, чтобы вызвать процедуру управления EDIT до вашего собственного чертежа. Вызов его позже или выдача собственного BeginPaint / EndPaint (с возвратом 0 или DefWindowProc) вызвали у меня проблемы с текстом, не отображаемым вообще, с отображением только при изменении размера, но не после редактирования, до оставления помета на экране остатка каретки. С этим у меня не было никаких проблем, независимо от других раз перекраски элемента управления EDIT.

Некоторые настройки:

SetWindowSubclass(attributeValuesEdit, &AttributeValueEditProcedure, 0, reinterpret_cast<DWORD_PTR>(this));

// Not only do multiline edit controls fail to display the cue banner text,
// but they also ignore the Edit_SetCueBannerText call, meaning we can't
// just call GetCueBannerText in the subclassed function. So store it as
// a window property instead.
SetProp(attributeValuesEdit, L"CueBannerText", L"<attribute value>");

Обратный звонок:

LRESULT CALLBACK AttributeValueEditProcedure(
    HWND hwnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam,
    UINT_PTR subclassId,
    DWORD_PTR data
    )
{

...

case WM_PRINTCLIENT:
case WM_PAINT:
    {
        auto textLength = GetWindowTextLength(hwnd);
        if (textLength == 0 && GetFocus() != hwnd)
        {
            // Get the needed DC with DCX_INTERSECTUPDATE before the EDIT
            // control's WM_PAINT handler calls BeginPaint/EndPaint, which
            // validates the update rect and would otherwise lead to drawing
            // nothing later because the region is empty. Also, grab it from
            // the cache so we don't mess with the EDIT's DC.
            HDC hdc = (message == WM_PRINTCLIENT)
                ? reinterpret_cast<HDC>(wParam)
                : GetDCEx(hwnd, nullptr, DCX_INTERSECTUPDATE|DCX_CACHE|DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS);

            // Call the EDIT control so that the caret is properly handled,
            // no caret litter left on the screen after tabbing away.
            auto result = DefSubclassProc(hwnd, message, wParam, lParam);

            // Get the font and margin so the cue banner text has a
            // consistent appearance and placement with existing text.
            HFONT font = GetWindowFont(hwnd);
            RECT editRect;
            Edit_GetRect(hwnd, OUT &editRect);

            // Ideally we would call Edit_GetCueBannerText, but since that message
            // returns nothing when ES_MULTILINE, use a window property instead.
            auto* cueBannerText = reinterpret_cast<wchar_t*>(GetProp(hwnd, L"CueBannerText"));

            HFONT previousFont = SelectFont(hdc, font);
            SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
            SetBkMode(hdc, TRANSPARENT);
            DrawText(hdc, cueBannerText, int(wcslen(cueBannerText)), &editRect, DT_TOP|DT_LEFT|DT_NOPREFIX|DT_NOCLIP);
            SelectFont(hdc, previousFont);

            ReleaseDC(hwnd, hdc);

            // Return the EDIT's result (could probably safely just return zero here,
            // but seems safer to relay whatever value came from the edit).
            return result;
        }
    }
    break;

Написание собственного элемента управления EDIT (который я на самом деле делал более одного раза, с частичной степенью полноты по сравнению со встроенным) не так уж много работы, если вы выполняете минимум (возможно, только на английском языке с базовой поддержкой каретки) ), но это МНОГО работы, чтобы получить исправление, если вы хотите плавную навигацию по сложным сценариям с кластерами переменного размера, выделению по диапазонам, поддержке IME, контекстным меню с копированием и вставкой, высококонтрастным режимам и функциям доступности, таким как преобразование текста в речь , Поэтому, в отличие от многих других ответов, я рекомендую , а не реализовать свой собственный элемент управления EDIT только для текста баннера.

5 голосов
/ 24 декабря 2009

Создайте собственный класс окна, который выглядит как пустой элемент управления редактированием, который рисует текст подсказки, показывает каретку и имеет фокус. Создайте также элемент управления редактирования, но поместите его за окном. (или оставьте это скрытым)

Тогда когда вы получите первое сообщение WM_CHAR (или WM_KEYDOWN?). Вы помещаете свое окно за правкой редактирования, фокусируете на редактировании и передаете сообщение WM_CHAR. С этого момента контроль редактирования вступит во владение.

Вы можете прослушивать уведомления EN_CHANGE из элемента управления редактированием, если вам нужно вернуться к отображению текста подсказки, когда редактирование становится пустым. Но я думаю, что было бы хорошо вернуться к ключевому тексту только тогда, когда редактирование теряет фокус И пусто.

3 голосов
/ 24 декабря 2009

Подкласс управления редактированием. Обработайте WM_PAINT, сначала вызвав исходную оконную процедуру, а затем, если она пуста и не в фокусе, нарисуйте текст подсказки. Передайте каждое другое сообщение исходной оконной процедуре.

Я сделал это - это работает. Проблема, с которой столкнулся человек из CodeGuru, не имеет отношения к вашей ситуации. Я считаю, что он пытается сделать больше для появления. Что касается производительности, похоже, что элемент управления редактирования выполняет некоторые обновления вне обработки WM_PAINT (вероятно, для производительности). Это сделает практически невозможным полный контроль над внешним видом. Но вы МОЖЕТЕ нарисовать подсказку кия.

0 голосов
/ 06 октября 2012

И мне также нужно найти способ отображения каретки в элементе управления, так как я не нашел способ позволить Windows сделать это для меня без также рисую белую полосу, о которой я упоминал.

Если вы хотите обрабатывать WM_PAINT самостоятельно, без пересылки сообщения в исходный windowproc вашего суперкласса, вы не должны забывать вызывать DefWindowProc. Так что каретка будет нарисована. Чтобы избежать белой полосы, вы должны удалить кисть класса с помощью SetClassLongPtr. И каким-то образом сохраняйте область отсечения вашего DC, чтобы обрезать выходные данные Edit Controt ExtTextOut. Белая полоса может быть результатом опции OPAQUE, переданной ExtTextOut с помощью элемента управления Edit.

Вывод: напишите свой собственный контроль. Нет боли, нет выгоды.

...