views:

1085

answers:

1

I've implemented a DeskBand Taskbar Toolbar using the BandObjectLib (Extending Explorer with Band Objects using .NET and Windows Forms) from Codeproject, modified with support for the IDeskBand2 interface which allows the Start Menu in Windows Vista to retain transparency when my DeskBand taskbar toolbar is enabled. However, the text displayed in a combobox or textbox display the color of the underlying desktop background blended with the original color of the text.

A label does not have this problem as it is normally drawn using GDI(+) which ignores DWMComposition on the rendered text (not the background of the label).

I figured the problem is because the way DWM works on Vista regarding certain text elements which is explained on the following pages:

Using Vista Controls on Aero Glass
Windows Vista Aero Pt. 1 - Adding Glass to a Windows Forms Application
Adding or Retrofitting Aero Glass into Legacy Windows Applications

I am only using a combobox on my DeskBand toolbar so I would only need to know how to force the combobox to not display using DWM even though DWM is enabled on the system and is enabled on the DeskBand through the implementation of the IDeskBand2 interface.

Update: I've looked into it further and the C++ code at Adding or Retrofitting Aero Glass into Legacy Windows Applications seems the most likely bet on getting this working so the combobox display text isn't transparent. If anyone can look at that code pertaining just to the combobox and help me get it working for a C# combobox, it would make my month! I have started a bounty to hopefully get an answer.

Below is the EditProc.cpp class from the above mentioned project that should provide a quick sneak peek at what I'm looking at as a solution. To get the full picture, you'd need to look at the full project:

/*
*
* $RCSfile: aeroedit.cpp,v $
* $Source: /cvs/common/aeroedit.cpp,v $
* $Author: cvs $
* $Revision: 1.12 $
* $Date: 2007/05/20 10:38:25 $
* $State: Exp $
* Copyright (c) Stefan Kuhr
*/

#include <windows.h>
#include <tchar.h>
#include "safassrt.h"
#include "aaeroint.h"
#include "aerosubc.h"
#include "aeroglss.h"
#include <windowsx.h>
#include <gdiplus.h>
using namespace Gdiplus;

static void UpdateIfSelChanged(HWND hWnd, PAERO_SUBCLASS_WND_DATA pWndData)
{
    DWORD dwFirst, dwLast;
    SendMessage(hWnd, EM_GETSEL, (WPARAM)&dwFirst, (LPARAM)&dwLast);
    if(dwFirst!=pWndData->m_dwSelFirst || dwLast!=pWndData->m_dwSelLast)
    {
        pWndData->m_dwSelFirst = dwFirst;
        pWndData->m_dwSelLast = dwLast;
        VERIFY(InvalidateRect(hWnd, NULL, TRUE));
        VERIFY(UpdateWindow(hWnd));
    }
}


static LRESULT CALLBACK EditProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    PAERO_SUBCLASS_WND_DATA pWndData = (PAERO_SUBCLASS_WND_DATA)GetProp(hWnd,         WINDOW_DATA_STRING);
    ASSERT(pWndData);
    ASSERT(pWndData->m_pDwmApiImpl);
    WNDPROC pOldProc = pWndData->m_oldWndProc;
    ASSERT(pOldProc);
    PAERO_SUBCLASS_WND_DATA pWndDataParent =     (PAERO_SUBCLASS_WND_DATA)GetProp(GetParent(hWnd), WINDOW_DATA_STRING);

    /// 
    /// if aero glass is turned off and if we are not in destruction code, 
    /// just call the original wnd proc we had prior to subclassing:
    /// 
    if(WM_DESTROY!=uMsg && WM_NCDESTROY!=uMsg && WM_DWMCOMPOSITIONCHANGED!=uMsg &&     pWndDataParent && !pWndData->m_pDwmApiImpl->IsDwmCompositionEnabled())
        return CallWindowProc(pOldProc, hWnd, uMsg, wParam, lParam);



    if(pWndData->m_uiRedrawMsg==uMsg && pWndData->m_dwFlags & WD_IN_PAINT_CONTROL)
    {
        HDC hdc = GetDC(hWnd);
        hdc = GetDC(hWnd);
        if(hdc)
        {
            RECT rcClient;
            GetClientRect(hWnd, &rcClient);

            BP_PAINTPARAMS params = { sizeof(BP_PAINTPARAMS) };
            params.dwFlags        = 0L;//BPPF_ERASE;
            HDC hdcPaint = NULL;
            HPAINTBUFFER hBufferedPaint = pWndData->m_pUxTheme->BeginBufferedPaint(hdc,     &rcClient, BPBF_TOPDOWNDIB, &params,     &hdcPaint);
            if (hdcPaint)
            {
                LONG_PTR dwStyle = GetWindowLongPtr(hWnd, GWL_STYLE);
                DWORD_PTR dwSyscolorIdx = (dwStyle&WS_DISABLED ||         dwStyle&ES_READONLY)?COLOR_3DFACE:COLOR_WINDOW;
                VERIFY(FillRect(hdcPaint, &rcClient, (HBRUSH)(dwSyscolorIdx+1)));

                SendMessage(hWnd, WM_PRINTCLIENT, (WPARAM) hdcPaint,     PRF_CLIENT|PRF_CHECKVISIBLE);

                /// Make every pixel opaque
                    VERIFY(S_OK==pWndData->m_pUxTheme->BufferedPaintMakeOpaque_(hBufferedPaint, &rcClient));
                VERIFY(S_OK==pWndData->m_pUxTheme->EndBufferedPaint(hBufferedPaint, TRUE));    
        }

        VERIFY(1==ReleaseDC(hWnd, hdc));
        pWndData->m_dwFlags &= ~WD_IN_PAINT_CONTROL;
    }

    return 1;
}

switch(uMsg)
{
    case WM_KEYDOWN:
    {    
        LONG_PTR dwStyle = GetWindowLongPtr(hWnd, GWL_STYLE);
        if(dwStyle&WS_VSCROLL || dwStyle&ES_MULTILINE)
        {
            if(!(pWndData->m_dwFlags&WD_CARET_HIDDEN))
            {
                HideCaret(hWnd);
                pWndData->m_dwFlags|=WD_CARET_HIDDEN;
            }
        }
    }
        break;
    case WM_KEYUP:
    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
    case WM_MOUSELEAVE:
    {
        LONG_PTR dwStyle = GetWindowLongPtr(hWnd, GWL_STYLE);
        if(dwStyle&WS_VSCROLL || dwStyle&ES_MULTILINE)
        {
            if(pWndData->m_dwFlags&WD_CARET_HIDDEN)
            {
                ShowCaret(hWnd);
                pWndData->m_dwFlags&=~WD_CARET_HIDDEN;
            }

            UpdateIfSelChanged(hWnd, pWndData);
        }
    }
        break;
    case WM_NCPAINT:
        {
            LRESULT lRes = 0;
            lRes = CallWindowProc(pOldProc, hWnd, uMsg, wParam, lParam);
            DrawEditBorder(hWnd, pWndData);
            return lRes;
        }
    case WM_NCDESTROY:
    case WM_DESTROY:
        VERIFY(UnsubclassControl(hWnd, EditProc, pWndData));
        break;
}

return CallWindowProc(pOldProc, hWnd, uMsg, wParam, lParam);
}

BOOL AeroSubClassEdit(HWND hwnd)
{
    return AeroSubClassControl(hwnd, EditProc, WD_IN_PAINT_CONTROL);
}

Thank you,

John Rennemeyer
MuvEnum, LLC

+1  A: 

This is a total pain, I don't know why Microsoft didn't have the WinForms controls work with the DWMManager. The TextBox is easy to do, you can repaint a bitmap of it overtop of it. The DropDown is trickier because it has a native control 'Edit' box inside of it. Since it's not a .Net control (the edit piece) then you can't use the .Net classes to easily redraw it (I'm still trying to figure out how to do this).

I have however figured out how to get a DropDown to render on glass but only if the textbox part is disabled (which done by changing the style). Not exactly ideal. Mine's in VB.Net and I'm still working on it. However, I was inspired by this project where this guy has pulled it off in C# and may be of great assistance to you:

http://dwmwinform.codeplex.com/

As a side note, WPF support the Aero Glass effect with all it's controls. It's way more powerful but also way more time consuming to use (IMO... and, WPF doesn't do you any good if you're retro fitting a WinForms app). I just tend to like WinForms since I write business applications and don't really care or have time to write animations (WPF is cool, don't me wrong, I prefer WinForms).

This is my start where I inherited a ComboBox (In Vb.Net). This site isn't posting the whole class correctly so I'll just include the insides of the class:

    ''' <summary>
''' Enum of Windows messages that will trigger the redraw of the control
''' </summary>
''' <remarks></remarks>
Public Enum WindowsMessage
    WM_CHAR = &H100
    WM_KEYDOWN = &H102
    WM_MOUSEMOVE = &H200
    WM_PAINT = 15
    WM_PRINT = &H314
End Enum

''' <summary>
''' Constructor
''' </summary>
''' <remarks></remarks>
Sub New()
    Me.DropDownStyle = ComboBoxStyle.DropDownList
End Sub

''' <summary>
''' Processing of incoming messages.  We're going to get a bitmap of the control and then
''' redraw it onto the form when a few specified windows messages come through.
''' </summary>
''' <param name="m"></param>    
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    MyBase.WndProc(m)

    Select Case m.Msg
        Case WindowsMessage.WM_PAINT, WindowsMessage.WM_CHAR, WindowsMessage.WM_KEYDOWN, _
             WindowsMessage.WM_MOUSEMOVE, WindowsMessage.WM_PRINT
            RedrawControlAsBitmap(Me.Handle)
    End Select

End Sub

''' <summary>
''' Redraws a given control as a bitmap ontop of itself.
''' </summary>
''' <param name="hwnd"></param>
''' <remarks></remarks>
Public Sub RedrawControlAsBitmap(ByVal hwnd As IntPtr)

    Dim c As Control = Control.FromHandle(hwnd)

    If c IsNot Nothing Then
        Using bm As New Bitmap(c.Width, c.Height)
            c.DrawToBitmap(bm, c.ClientRectangle)

            Using g As Graphics = c.CreateGraphics
                g.DrawImage(bm, New Point(0, 0))
            End Using

        End Using
    End If

    c = Nothing

End Sub
John