windows winapi custom-draw

windows - Win32: ¿Cómo dibujar un control de edición personalizado?



winapi custom-draw (5)

Subclase el control de edición. Maneje WM_PAINT llamando primero al procedimiento de ventana original y luego, si está vacío y no enfocado, dibuje el texto de referencia. Pase cualquier otro mensaje al procedimiento de ventana original.

He hecho esto, funciona. El problema que tenía la persona CodeGuru no parece aplicarse a su situación. Creo que está tratando de hacer más con la apariencia. Para el rendimiento, parece que el control de edición está realizando algunas actualizaciones fuera del proceso WM_PAINT (probablemente por el rendimiento). Eso hará que sea casi imposible tomar el control completo de la apariencia. Pero PUEDE dibujar el indicador de cue.

Necesito implementar la funcionalidad de EM_SETCUEBANNER , donde aparece una sugerencia de texto dentro de un control de Edición:

El problema es que no puedo usar la versión 6 de los controles comunes, que es lo que se requiere para obtener la implementación provista por Microsoft de un banner de referencia.

lo he visto simplemente cambiando el texto del control de edición, y el formato de fuente para

Dark Gray Italic Text

pero arrojará Eventos de cambio ( envoltorio de componente proporcionado por la biblioteca de componentes superior ) que no puedo encontrar una manera de evitar.

Así que, en cambio, iba a dibujar el texto personalizado, dibujar el texto de la señal de entrada cuando el control está desenfocado y vacío, y confiar en la pintura predeterminada de lo contrario.

El control de edición no expone agradablemente un mecanismo de dibujo personalizado, como ListView, TreeView y otros proporcionan .

Otras personas lo han investigado , pero parece ser una tarea casi imposible:

Por la forma en que se ven las cosas, tendré que manejar los siguientes mensajes:

  • WM_ERASEBKGND, WM_PAINT (por razones obvias)
  • WM_SETFOCUS, WM_KILLFOCUS (para evitar que la barra blanca se muestre, como se describió anteriormente)
  • WM_CHAR (para procesar y actualizar el texto en el control)

Y también necesito encontrar una manera de mostrar el cursor en el control, ya que no he encontrado la manera de permitir que Windows lo haga por mí sin también pintar la barra blanca que mencioné.

Esto va a ser divertido. :Rodar los ojos:

Dado que el control de edición de Windows nunca se diseñó para ser dibujado a mano: ¿alguien sabe cómo dibujar a medida un control de edición de Windows?

Nota : también aceptaré respuestas que resuelvan mi problema, en lugar de responder mi pregunta. Pero a cualquier otra persona que desee dibujar un control de Edición, encontrando esta pregunta, probablemente le gustaría una respuesta.


Cree una clase de ventana propia que se parezca y un control de edición vacío, que dibuje el texto de referencia y muestre un símbolo de intercalación y enfoque. Crea el control de edición también, pero colócalo detrás de tu ventana. (o déjalo oculto)

Luego, cuando obtiene el primer mensaje WM_CHAR (o WM_KEYDOWN?). Coloque su ventana detrás del conrol de edición, enfoque la edición y pase el mensaje WM_CHAR. A partir de ese momento, el control de edición se hará cargo.

Puede escuchar las notificaciones EN_CHANGE desde el control de edición si necesita volver a mostrar el texto de referencia cuando la edición se vacía. Pero creo que estaría bien volver al texto de referencia solo cuando la edición pierde el foco Y está vacía.


El dibujo personalizado y el control de edición son esencialmente imposibles. Hay algunos casos especializados en los que está haciendo tan poco que puede salirse con la suya, pero corre el riesgo de romperse gravemente en la próxima revisión de Windows (o cuando alguien ejecuta su aplicación en una versión anterior, o a través de servicios de terminal, etc.).

Simplemente asumir el control WM_PAINT y WM_ERASEBKGROUND no son lo suficientemente buenos, ya que el control a veces se pintará en otros mensajes también.

Es mejor que solo escriba su propio control de edición. Sé que es una gran cantidad de trabajo, pero a la larga será menos trabajo que tratar de hackear el camino para hacerse cargo de todos los códigos de dibujo de los controles de edición.

Recuerdo que, en los viejos tiempos, todos usaban la subclase del botón de control para agregar color y gráficos, etc. El caso es que un día me senté y escribí mi propia clase de ventana de botones. y era MENOS CÓDIGO que lo que teníamos en nuestro árbol fuente para crear una subclase y crear un botón personalizado para Windows.


Y también necesito encontrar una manera de mostrar el cursor en el control, ya que no he encontrado la manera de permitir que Windows lo haga por mí sin también pintar la barra blanca que mencioné.

Si desea manejar WM_PAINT por su cuenta sin reenviar el mensaje al proceso de ventana original de su superclase, no debe olvidar llamar a DefWindowProc. Para que se dibuje el cursor. Para evitar la barra blanca, debe eliminar el pincel de clase con SetClassLongPtr. Y de alguna manera mantenga la región de recorte de su DC para recortar las salidas ExtTextOut de Edit controt. La barra blanca puede ser el resultado de la opción OPAQUE pasada a ExtTextOut por el control Editar.

Conclusión: Escribe tu propio control. Sin dolor no hay ganancia.


La creación de subclases del control EDIT funcionó bien para mí: era necesario mostrar cierta información de formato al usuario cuando se editaban los atributos del objeto (y algunos atributos podían ser líneas múltiples). Lo importante, como dijo Adrian en su respuesta, es llamar al procedimiento de control EDIT antes de tu propio dibujo. Llamarlo después o emitir tu propio BeginPaint / EndPaint (con return 0 o DefWindowProc) me causó problemas por el hecho de que el texto no se muestra, hasta que se muestra solo al cambiar el tamaño, pero no después de la edición, hasta dejar la basura de la herramienta restante. Con eso, no he tenido ningún problema, independientemente de los otros tiempos de repintado del control EDIT.

Alguna configuración:

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>");

La devolución de llamada:

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;

Escribir tu propio control EDIT (que en realidad he hecho más de una vez, en grados parciales de compleción en comparación con el incorporado) no es mucho trabajo si haces el mínimo (quizás solo en inglés con soporte de caret básico), pero es MUCHO trabajo corregir si desea navegar por secuencias de comandos complejas con clústeres de tamaño variable, selección por rangos, compatibilidad con IME, menús contextuales con copiar y pegar, modos de alto contraste y funciones de accesibilidad como texto a voz. Por lo tanto, a diferencia de muchas otras respuestas, le recomiendo que no implemente su propio control EDIT simplemente para el texto del anuncio de referencia.