Reputation: 28586
I need to add a close "x" button in the upper left corner of a UserControl. I'd like that button have similar style as a usual Winforms close button (Blue in XP style, etc).
Is it possible to do such a thing?
Upvotes: 2
Views: 3448
Reputation: 14517
Here's an example of how to make a simulated button. It does so on the form itself, but works the same on a custom control.
It also shows how to get the "fancy" button style using a VisualStyleRenderer
in case the ControlPaint
style is too "old".
Updated After six edits, I think I'm happy with it.
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
class Form1 : Form
{
[STAThread]
static void Main()
{
//Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
public Form1()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.Opaque | ControlStyles.AllPaintingInWmPaint, true);
}
Rectangle buttonRect = new Rectangle(10, 10, 50, 20);
ButtonState buttonState = ButtonState.Normal;
protected override void OnMouseMove(MouseEventArgs e)
{
if (buttonRect.Contains(e.Location))
buttonState = Capture ? ButtonState.Pushed : ButtonState.Checked;
else
buttonState = ButtonState.Normal;
Invalidate(buttonRect);
base.OnMouseMove(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && buttonRect.Contains(e.Location))
{
Capture = true;
buttonState = ButtonState.Pushed;
Invalidate(buttonRect);
}
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (Capture)
{
Capture = false;
Invalidate(buttonRect);
if (buttonRect.Contains(e.Location))
{
// The button was clicked
MessageBox.Show("You clicked the button.");
}
}
base.OnMouseUp(e);
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.FillRectangle(SystemBrushes.Window, e.ClipRectangle);
VisualStyleRenderer renderer = null;
if (Application.RenderWithVisualStyles)
{
switch (buttonState)
{
case ButtonState.Checked:
if (VisualStyleRenderer.IsElementDefined(VisualStyleElement.Window.CloseButton.Hot))
renderer = new VisualStyleRenderer(VisualStyleElement.Window.CloseButton.Hot);
break;
case ButtonState.Pushed:
if (VisualStyleRenderer.IsElementDefined(VisualStyleElement.Window.CloseButton.Pressed))
renderer = new VisualStyleRenderer(VisualStyleElement.Window.CloseButton.Pressed);
break;
default:
if (VisualStyleRenderer.IsElementDefined(VisualStyleElement.Window.CloseButton.Normal))
renderer = new VisualStyleRenderer(VisualStyleElement.Window.CloseButton.Normal);
break;
}
}
if (renderer != null)
renderer.DrawBackground(e.Graphics, buttonRect);
else
ControlPaint.DrawCaptionButton(e.Graphics, buttonRect, CaptionButton.Close, buttonState);
base.OnPaint(e);
}
}
Extended Comment
An extended operation is one that begins with the user pressing a mouse button, (optionally) moving the mouse, and then releasing that button. Clicking on a UI button is always an extended operation. The purpose of capturing the mouse is to prevent it from interacting with other windows during that operation.
You can see this demonstrated right now:
First notice how when you move the mouse over elements on this page (like comments), they change background color.
Now open the Windows Run dialog (Win+R).
Click on the Cancel or Browse... button and keep holding the button.
Move the mouse around to the browser window and notice that it no longer registers your mouse movement.
This is how all buttons work by design. While hardly anyone notices it because you generally click and release without moving the mouse (much).
Moving the mouse off of the button and releasing is a mechanism provided to the user to allow them to change their mind and not "click" the button. That's why you test again in the button up event to make sure they are still on the button before you determine that the user has performed a click.
So there are three reasons to capture the mouse:
First you don't want other windows performing things like highlighting because the user is busy interacting with your window, having other windows doing things during the operation is distracting and potentially confusing.
Second you don't want the user's release of the mouse button to be sent to another window on the off chance that they move the mouse all the way onto another window. A spurious mouse release without an accompanying press can confuse poorly written applications.
Third, for certain extended operations like painting or "wrangling", you want to know the coordinates of the mouse even when they are outside the boundaries of your window. You can see an example of wrangling by clicking and holding on the desktop and moving the mouse around. Notice how the rectangle changes even when you move the mouse over other windows.
Upvotes: 5
Reputation: 56697
Updated Answer
I just found out there the ControlPaint class in .net, which does the same thing as the DrawFrameControl
function. Use that instead!
You could use the ControlPaint
class to implement a button control that paints itself to look like the Windows "Close" button (override OnPaint
). Depending on the required level of complexity, you might implement the mouse-over-events and mouse-down-events, too, to provide visual feedback for the current state. If you place that control on your UserControl
you should be on the right track.
Original, obsolete Answer
You might want to look at the DrawFrameControl API function in windows.
The combination of DFC_CAPTION
and DFCS_CAPTIONCLOSE
should do what you want.
Upvotes: 3
Reputation: 3777
not sure it is possible but that would definately confuse the users if your main Control that will host this UserContorl will have these 3 buttons (min,Max/close) and your UserControl as well. what's the rational behind this?
Upvotes: 0