Winforms:在UserControl中添加一个关闭的“x”按钮

时间:2011-05-16 14:37:03

标签: .net winforms user-controls

我需要在UserControl的左上角添加一个关闭的“x”按钮。我希望该按钮具有与通常的Winforms关闭按钮(XP风格的蓝色等)类似的风格。

有可能做这样的事吗?

3 个答案:

答案 0 :(得分:4)

以下是如何制作模拟按钮的示例。它在表单本身上执行此操作,但在自定义控件上的工作方式相同。

它还展示了如果VisualStyleRenderer样式太“旧”,如何使用ControlPaint获取“花哨”按钮样式。

已更新经过六次修改后,我觉得我很满意。

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);
    }
}

扩展评论

扩展操作是指用户按下鼠标按钮(可选)移动鼠标,然后释放该按钮。单击UI按钮始终是扩展操作。捕获鼠标的目的是防止它在该操作期间与其他窗口交互。

你现在可以看到这个:

首先注意当您将鼠标移到此页面上的元素(如注释)时,它们会如何更改背景颜色。

现在打开Windows运行对话框(Win + R)。

单击取消或浏览...按钮并按住按钮。

将鼠标移动到浏览器窗口,注意它不再记录鼠标移动。

这就是所有按钮按设计工作的方式。虽然几乎没有人注意到它,因为你通常点击并释放而不移动鼠标(很多)。

将鼠标从按钮上移开并释放是一种提供给用户的机制,允许他们改变主意而不是“点击”按钮。这就是为什么你在按钮启动事件中再次测试,以确保它们仍然在按钮上,然后才能确定用户是否已执行点击。

因此捕获鼠标有三个原因:

首先,您不希望其他窗口执行突出显示等操作,因为用户忙于与您的窗口进行交互,在操作期间让其他窗口处理操作会分散注意力并且可能会造成混淆。

其次,您不希望将用户释放的鼠标按钮发送到另一个窗口,因为他们将鼠标一直移动到另一个窗口。没有附带按键的虚假鼠标释放可能会混淆写得不好的应用程序。

第三,对于某些扩展操作,如绘画或“争吵”,即使它们位于窗口边界之外,您也希望知道鼠标的坐标。您可以通过单击并按住桌面并移动鼠标来查看争吵的示例。请注意,即使将鼠标移到其他窗口上,矩形也会发生变化。

答案 1 :(得分:3)

更新答案
我刚刚在.net中找到ControlPaint类,它与DrawFrameControl函数做同样的事情。请改用它!

您可以使用ControlPaint类来实现一个按钮控件,该控件将自身绘制成Windows“关闭”按钮(覆盖OnPaint)。根据所需的复杂程度,您可以实现鼠标悬停事件和鼠标按下事件,以便为当前状态提供可视反馈。如果您将该控件放在UserControl上,那么您应该走在正确的轨道上。

原创,陈旧的答案
您可能希望查看Windows中的DrawFrameControl API函数。

DFC_CAPTIONDFCS_CAPTIONCLOSE的组合可以做你想要的。

答案 2 :(得分:0)

不确定是否可能,但如果您将托管此UserContorl的主控件将具有这3个按钮(min,Max / close)和UserControl,那么这肯定会使用户感到困惑。这背后的理性是什么?