Zone de liste déroulante en arrière-plan dans la fenêtre parent du dégradé?

J’ai une fenêtre avec un fond dégradé. La Combobox possède sa propre brosse d’arrière-plan. Comment puis-je supprimer un coin blanc dans une liste déroulante? Comment puis-je changer de pinceau ou d’une autre manière. Sur le coin blanc de l’image marqué par un cadre rouge.

combobox avec coin blanc

Je crée une combobox en tant que:

DWORD dwStyle = WS_CHILD | CBS_DROPDOWNLIST; if (m_bVisible) dwStyle |= WS_VISIBLE; m_hWnd = CreateWindow(WC_COMBOBOX, NULL, dwStyle, m_posX, m_posY, m_width, m_height, m_hParent, (HMENU)m_id, m_hInstance, NULL); 

J’ai essayé de changer la brosse de fond avec le message WM_CTLCOLOREDIT , mais sans effet:

 case WM_CTLCOLOREDIT: if ((HWND)lParam == m_hSrcListBox) { return (LRESULT)m_hBrush; } break; 

=== RESOLU. VERSION DE TRAVAIL ===

Première façon.

Dans le parent WndProc:

 case WM_CTLCOLORSTATIC: if ((HWND)lParam == m_hSrcListBox) { return (LRESULT)m_pSrcListBox->GetHbrush(); } break; 

Dans ma classe:

 // // CListBox::GetHbrush(). // // Get brush. // HBRUSH CListBox::GetHbrush() { if (!m_hBrush) { m_hBrush = CreateTransparentBackgroundBrush(m_hParent, m_hWnd); } return m_hBrush; } 

Créer un fond transparent:

 // // CListBox::CreateTransparentBackgroundBrush(). // // Create transparent background for element. // HBRUSH CListBox::CreateTransparentBackgroundBrush(HWND parent, HWND client) { RECT rct; POINT p1; POINT p2; GetWindowRect(client, &rct); p1.x = rct.left; p1.y = rct.top; ScreenToClient(parent, &p1); p2.x = rct.right; p2.y = rct.bottom; ScreenToClient(parent, &p2); HDC hdcParent = GetDC(parent); HDC hdcClient = GetDC(client); HDC hdcmem = CreateCompatibleDC(hdcClient); HBITMAP hbitmap = CreateCompatibleBitmap(hdcClient, p2.x - p1.x, p2.y - p1.y); SelectObject(hdcmem, hbitmap); BitBlt(hdcmem, 0, 0, p2.x - p1.x, p2.y - p1.y, hdcParent, p1.x, p1.y, SRCCOPY); HBRUSH pattern = CreatePatternBrush(hbitmap); DeleteDC(hdcmem); DeleteObject(hbitmap); ReleaseDC(client, hdcClient); ReleaseDC(parent, hdcParent); return pattern; } 

Deuxième façon.

Dans WndProc parent, dessinez l’arrière-plan du message WM_ERASEBKGND, alors les coins ne le seront pas.

 case WM_ERASEBKGND: m_hdc = (HDC)wParam; // draw background. return TRUE; break; 

Le résultat des deux méthodes:

combobox sans coin blanc

Pour les boîtes de dialog, gérez WM_CTLCOLORDLG et retournez une brosse d’arrière-plan pour la liste déroulante.

Si vous affichez cette liste déroulante dans une boîte de dialog, l’astuce consiste à gérer le message WM_CTLCOLORDLG dans la procédure de fenêtre de votre boîte de dialog. En réponse à ce message, vous renvoyez une poignée à un pinceau que la boîte de dialog utilisera pour peindre son arrière-plan.

 case WM_CTLCOLORDLG: { // NOTE: This code is wrong because it creates a new brush object each time it processes // the message, which it promptly leaks. It is merely for demonstration purposes. // Normally, you would create the brush once, in response to WM_INITDIALOG, // cache it away, and return that same cached handle each time, finally destroying // the brush in response to WM_NCDESTROY. HBRUSH hBrush = CreateSolidBrush(RGB(255, 120, 0)); return reinterpret_cast(hBrush); } 

Il s’agit de la méthode documentée standard permettant de modifier la couleur d’arrière-plan d’une boîte de dialog. Elle résout également le problème de la liste déroulante. Apparemment, pour une raison quelconque, les commandes de liste déroulante utilisent également ce pinceau pour peindre leur arrière-plan. Je suppose qu’ils envoient un message WM_CTLCOLORDLG à leur parent lorsqu’ils se peignent eux-mêmes.

Bien entendu, cela vous limite aux capacités graphiques d’un pinceau GDI. Vous pouvez dessiner la couleur système ou unie de votre choix, ou même utiliser un pinceau hachuré ou modèle / bitmap, mais il n’existe pas de moyen simple de créer un pinceau dégradé. (GDI + en a un, mais pas GDI.) Normalement, cela n’aurait pas d’importance – vous appelez simplement la fonction GradientFill dans votre gestionnaire de messages WM_PAINT (ou même WM_ERASEBKGND ). Cela fonctionne bien pour l’arrière-plan de la boîte de dialog, mais la liste déroulante dessine toujours son arrière-plan avec le pinceau renvoyé par WM_CTLCOLORDLG . Par conséquent, les quatre points sur ses angles sont toujours dessinés dans COLOR_3DFACE (le pinceau renvoyé par la procédure de dialog par défaut).

Renvoyer un pinceau null ( NULL_BRUSH / HOLLOW_BRUSH ) à partir de WM_CTLCOLORDLG ne fonctionne pas non plus. L’aspect change légèrement, de sorte que les pixels des coins supérieur droit et inférieur gauche sont maintenant remplis avec quelque chose qui ressemble à COLOR_3DSKSHADOW , mais ils sont toujours remplis de manière visible avec une couleur autre que le dégradé d’arrière-plan.

Donc, si vous voulez vraiment que ça ait l’air sympa, il ne vous rest qu’une seule option: retourner une poignée à un pinceau GDI. Et bien sûr, il doit s’agir du même pinceau que celui utilisé pour dessiner l’arrière-plan du dialog.

Si vous voulez un remplissage en dégradé, la seule solution à laquelle je peux penser consiste à utiliser un pinceau motif / bitmap, où le bitmap (DDB ou DIB) est votre dégradé. Pas génial, mais au moins l’époque de Windows 9x nous limitant à 8 × 8 modèles est révolue. Peut-être qu’une personne plus inventive que moi peut utiliser cette information pour trouver une meilleure solution de contournement?


Pour les autres fenêtres, gérez WM_CTLCOLORSTATIC et retournez une brosse d’arrière-plan pour la liste déroulante.

Tout cela pour une boîte de dialog. Mais qu’en est-il si vous affichez la liste déroulante dans une fenêtre standard ( c’est -à- dire autre chose qu’une boîte de dialog)? Le message WM_CTLCOLORDLG n’est jamais envoyé dans ce cas.

Au lieu de cela, la liste déroulante envoie un message WM_CTLCOLORSTATIC à sa fenêtre parente, puis utilise le handle de pinceau renvoyé en réponse à ce message pour peindre son arrière-plan.

C’est bizarre, je sais. Je ne l’ai découvert que par des tests empiriques et je ne suis pas certain de la logique. Si je devais deviner, je dirais que le style CBS_DROPDOWNLIST rend la liste déroulante non éditable ( c’est -à- dire que ce n’est pas une vraie liste déroulante car il n’y a pas de contrôle Edit), donc au lieu de WM_CTLCOLOREDIT , il utilise WM_CTLCOLORSTATIC . Une boîte d’édition désactivée envoie également WM_CTLCOLORSTATIC , ainsi qu’une liste déroulante désactivée avec les styles “normaux” CBS_SIMPLE et CBS_DROPDOWN .

Bien plus étrange, cela ne se produit que lorsque le thème Aero est activé (Vista et 7). Cela ne se produit pas sous Windows 10, ni avec le thème Luna (Styles visuels sous XP), ni avec le thème Classique. (Je n’ai pas testé Windows 8 ou 8.1.) Cela n’a pas d’importance, je suppose, car tous ces autres thèmes dessinent une simple liste déroulante rectangular, ne laissant aucun coin de pixel à l’arrière-plan.

Quelle que soit la logique, la solution rest de gérer le message WM_CTLCOLORSTATIC et de renvoyer le pinceau que vous souhaitez que la liste déroulante utilise pour peindre son arrière-plan.

Les mêmes considérations s’appliquent ici que celles discutées ci-dessus pour la boîte de dialog. Si votre fenêtre utilise un arrière-plan de couleur unie ou une couleur système, vous n’êtes plus chez vous. Renvoyez simplement une poignée au même pinceau que celui que vous avez défini comme pinceau d’arrière-plan de la classe window. Si vous souhaitez utiliser un dégradé, vous devez trouver un moyen de représenter ce dégradé sous la forme d’un pinceau GDI.

 WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDR_APPLICATION)); wcex.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDR_APPLICATION_SMALL)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = reinterpret_cast(COLOR_3DDKSHADOW + 1); // background brush wcex.lpszMenuName = NULL; wcex.lpszClassName = TEXT("My Colored Window Class"); RegisterClassEx(&wcex); 
 case WM_CTLCOLORSTATIC: { // NOTE: No leak here because we're using a system brush in this example. return reinterpret_cast(GetSysColorBrush(COLOR_3DDKSHADOW)); // background brush } 

Comment puis-je supprimer un coin blanc dans une liste déroulante?

Je ne sais pas s’il existe un moyen plus officiel de s’en débarrasser, mais une option serait de créer une région avec des angles arrondis à l’aide de CreateRoundRectRgn() , puis de l’appliquer à la ComboBox à l’aide de SetWindowRgn() . Cela masquer les coins.