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