如何在表格的标题栏中的最小化,最大化和关闭按钮旁边绘制自定义按钮?
我知道您需要使用Win32 API调用并覆盖WndProc过程,但我无法找到可行的解决方案。
有谁知道怎么做?更具体地说,有没有人知道在Vista中有效的方法?
答案 0 :(得分:6)
以下将在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;
}
答案 1 :(得分:2)
我知道自上次回答以来已经很久了但是这最近对我有所帮助,我想更新Chris提供的代码以及我的评论和修改。 该版本在Win XP和Win 2003上运行完美。在Win 2008上有一个小错误,在调整窗口大小时我无法识别。也适用于Vista(no-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;
}
该代码演示了我将要处理的消息以及如何对待它们。 该代码使用自定义TitleBarButton对象的集合。这个类太大了,不能包含在这里,但如果需要的话,我可以提供一个例子。
答案 2 :(得分:1)
绘图似乎很容易,下面会这样做:
[修改:代码已删除,请参阅我的其他答案]
真正的问题是改变状态并检测按钮上的点击...因为你需要挂钩到程序的全局消息处理程序,.NET似乎隐藏了表单的鼠标事件而不是实际的容器区域(即鼠标移动并点击标题栏)。我正在寻找有关的信息,现在发现它,我正在努力,不应该太难......如果我们能弄清楚这些消息实际上是通过了什么。