如何在窗體標題欄內的最小化,最大化和關閉按鈕旁邊繪製自定義按鈕?如何使用Windows窗體在Window Titlebar中繪製自定義按鈕?
我知道你需要使用Win32 API調用並重寫WndProc過程,但我一直無法找出一個正確的解決方案。
有誰知道如何做到這一點?更具體地說,有沒有人知道一種方法來做到這一點在Vista中的作品?
如何在窗體標題欄內的最小化,最大化和關閉按鈕旁邊繪製自定義按鈕?如何使用Windows窗體在Window Titlebar中繪製自定義按鈕?
我知道你需要使用Win32 API調用並重寫WndProc過程,但我一直無法找出一個正確的解決方案。
有誰知道如何做到這一點?更具體地說,有沒有人知道一種方法來做到這一點在Vista中的作品?
以下將在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;
}
繪畫似乎是比較容易的部分,下面將做到這一點:
[編輯:代碼刪除,請參閱我的其他答案]
真正的問題是不斷變化的狀態,按鈕上檢測點擊...因爲你需要鉤入程序的全局消息處理程序,.NET似乎隱藏了表單的鼠標事件,而不是在實際的容器區域(即鼠標移動並點擊標題欄) 。我正在尋找有關這方面的信息,現在發現它,我正在研究它,不應該太難......如果我們能夠弄清楚這些信息實際上傳遞了什麼。
我知道它已經很長時間以來的最後一個答案,但這真的幫了我最近,我喜歡更新克里斯提供的代碼與我的意見和修改。 該版本在Win XP和Win 2003上完美運行。在Win 2008上,有一個小錯誤,在調整窗口大小時我無法識別。也適用於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;
}
該代碼演示了要處理的消息以及如何處理它們。 該代碼使用自定義TitleBarButton對象的集合。這個班級太大而不能包含在這裏,但是如果需要的話,我可以提供這個班級和一個例子。
此代碼在Vista中似乎不起作用。 – 2008-09-20 05:21:56
你能定義「不起作用」嗎?它不識別這些信息,它不是在繪製?它甚至不會編譯? – 2008-09-20 05:47:23
對不起,它運行但繪製的矩形不顯示。 – 2008-09-20 05:54:43