Winforms 如何使用Windows窗体在窗口标题栏中绘制自定义按钮?

Winforms 如何使用Windows窗体在窗口标题栏中绘制自定义按钮?,winforms,winapi,windows-vista,wndproc,titlebar,Winforms,Winapi,Windows Vista,Wndproc,Titlebar,如何在表单标题栏中的最小化、最大化和关闭按钮旁边绘制自定义按钮 我知道您需要使用Win32 API调用并重写WndProc过程,但我还没有找到一个正确的解决方案 有人知道怎么做吗?更具体地说,有人知道一种在Vista中工作的方法吗?绘图似乎是最简单的部分,以下几点可以做到: [编辑:代码已删除,请参阅我的其他答案] 真正的问题是改变状态和检测按钮的点击。。。为此,您需要挂接到程序的全局消息处理程序中,.NET似乎隐藏了表单的鼠标事件,而不在实际的容器区域中(即鼠标移动和单击标题栏)。我正在寻找关

如何在表单标题栏中的最小化、最大化和关闭按钮旁边绘制自定义按钮

我知道您需要使用Win32 API调用并重写WndProc过程,但我还没有找到一个正确的解决方案


有人知道怎么做吗?更具体地说,有人知道一种在Vista中工作的方法吗?

绘图似乎是最简单的部分,以下几点可以做到:

[编辑:代码已删除,请参阅我的其他答案]


真正的问题是改变状态和检测按钮的点击。。。为此,您需要挂接到程序的全局消息处理程序中,.NET似乎隐藏了表单的鼠标事件,而不在实际的容器区域中(即鼠标移动和单击标题栏)。我正在寻找关于它的信息,现在找到了,我正在努力,应该不会太难。。。如果我们能弄清楚这些消息实际上传递的是什么。

以下内容将在XP中运行,我手头没有Vista机器来测试它,但我认为您的问题不知何故来自错误的hWnd。不管怎样,请继续使用注释不好的代码

// The state of our little button
ButtonState _buttState = ButtonState.Normal;
Rectangle _buttPosition = new Rectangle();

[DllImport("user32.dll")]
private static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern int GetWindowRect(IntPtr hWnd, 
                                        ref Rectangle lpRect);
[DllImport("user32.dll")]
private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
protected override void WndProc(ref Message m)
{
    int x, y;
    Rectangle windowRect = new Rectangle();
    GetWindowRect(m.HWnd, ref windowRect);

    switch (m.Msg)
    {
        // WM_NCPAINT
        case 0x85:
        // WM_PAINT
        case 0x0A:
            base.WndProc(ref m);

            DrawButton(m.HWnd);

            m.Result = IntPtr.Zero;

            break;

        // WM_ACTIVATE
        case 0x86:
            base.WndProc(ref m);
            DrawButton(m.HWnd);

            break;

        // WM_NCMOUSEMOVE
        case 0xA0:
            // Extract the least significant 16 bits
            x = ((int)m.LParam << 16) >> 16;
            // Extract the most significant 16 bits
            y = (int)m.LParam >> 16;

            x -= windowRect.Left;
            y -= windowRect.Top;

            base.WndProc(ref m);

            if (!_buttPosition.Contains(new Point(x, y)) && 
                _buttState == ButtonState.Pushed)
            {
                _buttState = ButtonState.Normal;
                DrawButton(m.HWnd);
            }

            break;

        // WM_NCLBUTTONDOWN
        case 0xA1:
            // Extract the least significant 16 bits
            x = ((int)m.LParam << 16) >> 16;
            // Extract the most significant 16 bits
            y = (int)m.LParam >> 16;

            x -= windowRect.Left;
            y -= windowRect.Top;

            if (_buttPosition.Contains(new Point(x, y)))
            {
                _buttState = ButtonState.Pushed;
                DrawButton(m.HWnd);
            }
            else
                base.WndProc(ref m);

            break;

        // WM_NCLBUTTONUP
        case 0xA2:
            // Extract the least significant 16 bits
            x = ((int)m.LParam << 16) >> 16;
            // Extract the most significant 16 bits
            y = (int)m.LParam >> 16;

            x -= windowRect.Left;
            y -= windowRect.Top;

            if (_buttPosition.Contains(new Point(x, y)) &&
                _buttState == ButtonState.Pushed)
            {
                _buttState = ButtonState.Normal;
                // [[TODO]]: Fire a click event for your button 
                //           however you want to do it.
                DrawButton(m.HWnd);
            }
            else
                base.WndProc(ref m);

            break;

        // WM_NCHITTEST
        case 0x84:
            // Extract the least significant 16 bits
            x = ((int)m.LParam << 16) >> 16;
            // Extract the most significant 16 bits
            y = (int)m.LParam >> 16;

            x -= windowRect.Left;
            y -= windowRect.Top;

            if (_buttPosition.Contains(new Point(x, y)))
                m.Result = (IntPtr)18; // HTBORDER
            else
                base.WndProc(ref m);

            break;

        default:
            base.WndProc(ref m);
            break;
    }
}

private void DrawButton(IntPtr hwnd)
{
    IntPtr hDC = GetWindowDC(hwnd);
    int x, y;

    using (Graphics g = Graphics.FromHdc(hDC))
    {
        // Work out size and positioning
        int CaptionHeight = Bounds.Height - ClientRectangle.Height;
        Size ButtonSize = SystemInformation.CaptionButtonSize;
        x = Bounds.Width - 4 * ButtonSize.Width;
        y = (CaptionHeight - ButtonSize.Height) / 2;
        _buttPosition.Location = new Point(x, y);

        // Work out color
        Brush color;
        if (_buttState == ButtonState.Pushed)
            color = Brushes.LightGreen;
        else
            color = Brushes.Red;

        // Draw our "button"
        g.FillRectangle(color, x, y, ButtonSize.Width, ButtonSize.Height);
    }

    ReleaseDC(hwnd, hDC);
}

private void Form1_Load(object sender, EventArgs e)
{
    _buttPosition.Size = SystemInformation.CaptionButtonSize;
}
//我们的小按钮的状态
按钮状态_buttState=按钮状态。正常;
矩形_buttPosition=新矩形();
[DllImport(“user32.dll”)]
私有静态外部IntPtr GetWindowDC(IntPtr hWnd);
[DllImport(“user32.dll”)]
私有静态外部int GetWindowRect(IntPtr hWnd,
参考矩形(lpRect);
[DllImport(“user32.dll”)]
专用静态外部int ReleaseDC(IntPtr hWnd、IntPtr hDC);
受保护的覆盖无效WndProc(参考消息m)
{
int x,y;
矩形windowRect=新矩形();
GetWindowRect(m.HWnd,ref windowRect);
开关(m.Msg)
{
//WM_NCPAINT
案例0x85:
//WM_涂料
案例0x0A:
基准WndProc(参考m);
绘图按钮(m.HWnd);
m、 结果=IntPtr.0;
打破
//WM_激活
案例0x86:
基准WndProc(参考m);
绘图按钮(m.HWnd);
打破
//WM_NCMOUSEMOVE
案例0xA0:
//提取最低有效16位
x=((int)m.LParam>16;
//提取最高有效16位
y=(int)m.LParam>>16;
x-=windowRect.左;
y-=窗口矩形顶部;
基准WndProc(参考m);
如果(!_buttPosition.包含(新点(x,y))&&
_buttState==ButtonState.push)
{
_buttState=ButtonState.Normal;
绘图按钮(m.HWnd);
}
打破
//WM_nclu Buttondown
案例0xA1:
//提取最低有效16位
x=((int)m.LParam>16;
//提取最高有效16位
y=(int)m.LParam>>16;
x-=windowRect.左;
y-=窗口矩形顶部;
if(_buttPosition.Contains(新点(x,y)))
{
_buttState=按钮状态。按下;
绘图按钮(m.HWnd);
}
其他的
基准WndProc(参考m);
打破
//西蒙努普
案例0xA2:
//提取最低有效16位
x=((int)m.LParam>16;
//提取最高有效16位
y=(int)m.LParam>>16;
x-=windowRect.左;
y-=窗口矩形顶部;
if(_buttPosition.Contains)(新点(x,y))&&
_buttState==ButtonState.push)
{
_buttState=ButtonState.Normal;
//[[TODO]]:为按钮触发单击事件
//不管你想怎么做。
绘图按钮(m.HWnd);
}
其他的
基准WndProc(参考m);
打破
//WM_NCHITTEST
案例0x84:
//提取最低有效16位
x=((int)m.LParam>16;
//提取最高有效16位
y=(int)m.LParam>>16;
x-=windowRect.左;
y-=窗口矩形顶部;
if(_buttPosition.Contains(新点(x,y)))
m、 结果=(IntPtr)18;//HTBORDER
其他的
基准WndProc(参考m);
打破
违约:
基准WndProc(参考m);
打破
}
}
专用空白提取按钮(IntPtr hwnd)
{
IntPtr hDC=GetWindowDC(hwnd);
int x,y;
使用(Graphics g=Graphics.FromHdc(hDC))
{
//计算出尺寸和位置
int CaptionHeight=Bounds.Height-ClientRectangle.Height;
大小按钮大小=系统信息。标题按钮大小;
x=Bounds.Width-4*按钮化.Width;
y=(标题高度-按钮高度)/2;
_对接位置。位置=新点(x,y);
//计算颜色
画笔颜色;
如果(_buttState==ButtonState.push)
颜色=笔刷。浅绿色;
其他的
颜色=画笔。红色;
//画出我们的“按钮”
g、 圆角矩形(颜色、x、y、按钮大小、宽度、按钮大小、高度);
}
释放DC(hwnd、hDC);
}
私有void Form1\u加载(对象发送方、事件参数e)
{
_buttPosition.Size=SystemInformation.CaptionButtonSize;
}

我知道距离最后一个答案已经很久了,但最近这确实帮助了我,我喜欢用我的评论和修改更新Chris提供的代码。 该版本在Win XP和Win 2003上运行完美。在Win 2008上,ot有一个我在调整窗口大小时无法识别的小错误。同样适用于Vista(无Aero),但请注意标题栏按钮不是方形的,按钮尺寸应考虑到这一点

 switch (m.Msg)
            {
                // WM_NCPAINT / WM_PAINT        
                case 0x85:
                case 0x0A:
                    //Call base method
                    base.WndProc(ref m);
                    //we have 3 buttons in the corner of the window. So first's new button left coord is offseted by 4 widths
                    int crt = 4;
                    //navigate trough all titlebar buttons on the form
                    foreach (TitleBarImageButton crtBtn in titleBarButtons.Values)
                    {
                        //Calculate button coordinates
                        p.X = (Bounds.Width - crt * crtBtn.Size.Width);
                        p.Y = (Bounds.Height - ClientRectangle.Height - crtBtn.Size.Height) / 2;
                        //Initialize button and draw
                        crtBtn.Location = p;
                        crtBtn.ButtonState = ImageButtonState.NORMAL;
                        crtBtn.DrawButton(m.HWnd);
                        //increment button left coord location offset
                        crt++;
                    }
                    m.Result = IntPtr.Zero;
                    break;
                // WM_ACTIVATE      
                case 0x86:
                    //Call base method
                    base.WndProc(ref m);
                    //Draw each button
                    foreach (TitleBarImageButton crtBtn in titleBarButtons.Values)
                    {
                        crtBtn.ButtonState = ImageButtonState.NORMAL;
                        crtBtn.DrawButton(m.HWnd);
                    }
                    break;
                // WM_NCMOUSEMOVE        
                case 0xA0:
                    //Get current mouse position
                    p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits            
                    p.Y = (int)m.LParam >> 16;        // Extract the most significant 16 bits          
                    p.X -= windowRect.Left;
                    p.Y -= windowRect.Top;

                    //Call base method
                    base.WndProc(ref m);

                    ImageButtonState newButtonState;
                    foreach (TitleBarImageButton crtBtn in titleBarButtons.Values)
                    {
                        if (crtBtn.HitTest(p))
                        {//mouse is over the current button
                            if (crtBtn.MouseButtonState == MouseButtonState.PRESSED)
                                //button is pressed - set pressed state
                                newButtonState = ImageButtonState.PRESSED;
                            else
                                //button not pressed - set hoover state
                                newButtonState = ImageButtonState.HOOVER;
                        }
                        else
                        {
                            //mouse not over the current button - set normal state
                            newButtonState = ImageButtonState.NORMAL;
                        }

                        //if button state not modified, do not repaint it.
                        if (newButtonState != crtBtn.ButtonState)
                        {
                            crtBtn.ButtonState = newButtonState;
                            crtBtn.DrawButton(m.HWnd);
                        }
                    }
                    break;
                // WM_NCLBUTTONDOWN     
                case 0xA1:
                    //Get current mouse position
                    p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits
                    p.Y = (int)m.LParam >> 16;        // Extract the most significant 16 bits      
                    p.X -= windowRect.Left;
                    p.Y -= windowRect.Top;

                    //Call base method
                    base.WndProc(ref m);

                    foreach (TitleBarImageButton crtBtn in titleBarButtons.Values)
                    {
                        if (crtBtn.HitTest(p))
                        {
                            crtBtn.MouseButtonState = MouseButtonState.PRESSED;
                            crtBtn.ButtonState = ImageButtonState.PRESSED;
                            crtBtn.DrawButton(m.HWnd);
                        }
                    }
                    break;
                // WM_NCLBUTTONUP   
                case 0xA2:
                case 0x202:
                    //Get current mouse position
                    p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits   
                    p.Y = (int)m.LParam >> 16;        // Extract the most significant 16 bits 
                    p.X -= windowRect.Left;
                    p.Y -= windowRect.Top;

                    //Call base method
                    base.WndProc(ref m);
                    foreach (TitleBarImageButton crtBtn in titleBarButtons.Values)
                    {
                        //if button is press
                        if (crtBtn.ButtonState == ImageButtonState.PRESSED)
                        {
                            //Rasie button's click event
                            crtBtn.OnClick(EventArgs.Empty);

                            if (crtBtn.HitTest(p))
                                crtBtn.ButtonState = ImageButtonState.HOOVER;
                            else
                                crtBtn.ButtonState = ImageButtonState.NORMAL;
                        }

                        crtBtn.MouseButtonState = MouseButtonState.NOTPESSED;
                        crtBtn.DrawButton(m.HWnd);
                    }
                    break;
                // WM_NCHITTEST    
                case 0x84:
                    //Get current mouse position
                    p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits
                    p.Y = (int)m.LParam >> 16;        // Extract the most significant 16 bits
                    p.X -= windowRect.Left;
                    p.Y -= windowRect.Top;

                    bool isAnyButtonHit = false;
                    foreach (TitleBarImageButton crtBtn in titleBarButtons.Values)
                    {
                        //if mouse is over the button, or mouse is pressed 
                        //(do not process messages when mouse was pressed on a button)
                        if (crtBtn.HitTest(p) || crtBtn.MouseButtonState == MouseButtonState.PRESSED)
                        {
                            //return 18 (do not process further)
                            m.Result = (IntPtr)18;
                            //we have a hit
                            isAnyButtonHit = true;
                            //return 
                            break;
                        }
                        else
                        {//mouse is not pressed and not over the button, redraw button if needed  
                            if (crtBtn.ButtonState != ImageButtonState.NORMAL)
                            {
                                crtBtn.ButtonState = ImageButtonState.NORMAL;
                                crtBtn.DrawButton(m.HWnd);
                            }
                        }
                    }
                    //if we have a hit, do not process further
                    if (!isAnyButtonHit)
                        //Call base method
                        base.WndProc(ref m);
                    break;
                default:
                    //Call base method
                    base.WndProc(ref m);
                    //Console.WriteLine(m.Msg + "(0x" + m.Msg.ToString("x") + ")");
                    break;
            }
开关(m.Msg)
{
//WM\U NCPAINT/WM\U PAINT
案例0x85:
案例0x0A: