65.9K
CodeProject 正在变化。 阅读更多。
Home

托管 C++ 按钮控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.70/5 (10投票s)

2008年4月12日

CPOL

5分钟阅读

viewsIcon

75868

downloadIcon

1821

这是一个自定义按钮控件的示例,完全用托管 C++ 编写。

mehButton

如果您喜欢这篇文章,请投票支持。如果您不喜欢这篇文章,请投票,并告诉我您不喜欢的原因。

引言

我开始这个项目是为了了解如何开发一个与标准 .NET Button 控件质量相当的托管自定义控件。像大多数 .NET 初学者一样,我最初是从 UserControl 类继承的,但很快就发现我想要实现的一些概念存在显著的限制。

在对 Google 和 MSDN 文档进行大量搜索后,我决定开发一个继承自 ButtonBaseIButtonControl 的按钮控件。继承自 ButtonBase 的一个问题是该类是抽象的。这意味着在设计器中无法直接获取按钮控件。但是,它可以添加到工具箱中,并且在窗体设计器中运行良好。

该控件设计灵活。它允许实现圆角或平整的正方形控件。此外,您可以指定哪些角是圆角,哪些是正方形。您还可以更改角的半径。我使许多颜色可配置。我还包含了为悬停颜色和正常颜色绘制渐变色的功能。我实现了标准 .NET 按钮控件可以响应的大多数事件。通过实现 IButtonControl 接口,还暴露了几个标准的按钮行为。

背景

CodeProject 上有很多 .NET 自定义控件。为什么我们需要另一个?部分原因是我是一名 C++ 程序员。我想看看将我的 WTL 和 ATL 技能转移到 .NET 需要付出什么。我想了解为 WTL 使用的控件与为 VB.NET 或 C# 使用的控件之间的架构差异。我大量使用属性来描述属性并分配默认值。我还添加了 XML 文档标签,以便在您的窗体中使用该控件时,Intellisense 更有意义。

我的控件设计是受到 WiBAlan Zhao 工作的影响。他们的文章都提供了极大的启发和教育。

编译和使用源代码

下载并解压演示项目。请务必保留源文件的目录结构。

首次加载解决方案时,您可能会看到有关缺少引用的多条消息。这是因为程序集尚未在您的机器上生成。(我在源分发版中没有包含任何二进制文件,因此会出现引用丢失的情况。)只需选择发布或调试模式,然后重新编译。程序集将被生成,所有引用都将得到验证。此时,您可以通过将程序集拖到工具箱表面,或从上下文菜单中选择“添加项...”,然后导航到 <根目录>\mehControls\release 并选择 mehButton.dll 来将控件添加到工具箱。

使用组件

将控件添加到工具箱后,您可以将其拖到任何窗体上。我已经实现了一个设计器组件,因此所有属性都将在“属性”视图中正确显示。您会注意到,我在设计器的 PostFilterProperties 方法中删除了一些最难实现的属性。其他属性,例如“Flat”属性,是冗余的。mehControl 的编程相当标准。以下代码说明了窗体设计器向导如何在 C# 项目中初始化控件。

// 
// mehButton1
// 
this.mehButton1.BackColor = System.Drawing.SystemColors.Control;
this.mehButton1.ButtonStyle = mehControls.mehButton.ButtonStyles.Rectangle;
this.mehButton1.CurveMode = mehControls.mehButton.CornerCurveStyle.None;
this.mehButton1.DialogResult = System.Windows.Forms.DialogResult.None;
this.mehButton1.GradientStyle = mehControls.mehButton.GradientStyles.None;
this.mehButton1.HoverBorderColor = System.Drawing.SystemColors.ControlDark;
this.mehButton1.HoverColorA = System.Drawing.SystemColors.ButtonFace;
this.mehButton1.HoverColorB = System.Drawing.SystemColors.ButtonHighlight;
this.mehButton1.Image = null;
this.mehButton1.Location = new System.Drawing.Point(12, 122);
this.mehButton1.Name = "mehButton1";
this.mehButton1.NormalBorderColor = System.Drawing.SystemColors.WindowFrame;
this.mehButton1.NormalColorA = System.Drawing.SystemColors.ButtonHighlight;
this.mehButton1.NormalColorB = System.Drawing.SystemColors.ButtonFace;
this.mehButton1.Radius = 0;
this.mehButton1.Size = new System.Drawing.Size(127, 38);
this.mehButton1.SmoothingQuality = mehControls.mehButton.SmoothingQualities.AntiAlias;
this.mehButton1.TabIndex = 4;
this.mehButton1.Text = "m&ehButton Flat";
this.mehButton1.UseVisualStyleBackColor = false;
this.mehButton1.MouseLeave += new System.EventHandler(this.mehButton1_MouseLeave);

...

// The control's events are implmented in a standard fashion.
// The C# design wizard is able to auto-fill the events
// properly from the "Properties" sheet.
private void mehButton1_MouseLeave(object sender, EventArgs e)
{
    this.lblEventText.Text = "Mouse has left mehButton1";
}

实现设计器

当您为 .NET 开发自定义可视化控件时,您会想要实现一个设计器。设计器可以很简单(就像我实现的那个),也可以根据您的需要做得非常复杂。以下代码说明了最小设计器的定义。

namespace mehControls
{
    ref class mehButtonDesigner : 
        public System::Windows::Forms::Design::ControlDesigner
    {
    public:
        mehButtonDesigner(void)
        {
        };

        ~mehButtonDesigner(void)
        {
        };

    protected:
        virtual void PostFilterProperties(IDictionary ^) override;
        virtual void OnPaintAdornments(PaintEventArgs ^e) override;

    };
}

属性的属性设置

您将希望正确地为您的属性设置属性。这将允许您控制属性在“属性”显示中的分组以及属性的默认值。

[DefaultValue(System::Drawing::Color::typeid, "System::Drawing::SystemColors::Control")]
[Description("Background Color for button"), Category("Appearance")]
// This is how you split a property definition
// This is useful if the property has many lines of
// code or you do not want to expose the
// inner workings of your code in the header.
property virtual Color BackColor
{
    // The property function prototypes are specified here
    Color get() override;
    void set(Color value) override;
}

处理控件的 OnPaint 事件

与原生模式编程一样,大部分工作都在控件的 OnPaint(或 OnPaintBackground)事件中处理。对于此控件,DrawRoundRectangle 完成了大部分繁重的工作。为了实现圆角,我大量使用了 gp->AddArc(arc, angle1, angle2),其中 GraphicsPath ^gp = gcnew GraphicsPath()

摘要

该按钮实现了 .NET 2.0 按钮的许多属性。花了很多时间弄清楚如何添加“热”边框的绘制。我不得不运用我高中的几何知识来计算绘制圆角时圆弧的正确中心。这是我用于开发圆角算法的基本图。

RoundedCorners1.JPG

我还能够实现背景的渐变绘制,这在按钮出现鼠标悬停或单击事件时可以产生一些非常酷的效果。下面的枚举说明了实现的渐变类型。

enum class GradientStyles
{
    None,
    Horizontal,
    Vertical,
    ForwardDiagonal,
    BackwardDiagonal
};

该控件远未完成。我已经为所有属性添加了属性。但是,XML 文档标签还有大量工作要做。在此版本的控件中,我也尚未实现主题。

您应该知道的事情

有几件事我发现用托管和混合模式 C++ 做起来容易得多。首先,您不必担心导入 API 调用。您可以包含 Platform SDK 的任何头文件,Visual C++ 会为您处理调用。这非常有帮助,可以节省大量处理 API 调用时的时间。在托管 C++ 项目中,在 wchar_tString 之间来回切换要简单得多。

VS 2005 没有像 C# 那样为 C++ 提供漂亮的向导。当您选择 Windows 控件项目时,您只会得到它。没有设计器,也没有锦上添花的功能。一开始您需要经常查阅文档。但是,我发现通过艰难的方式学习,我对我所处的环境有了更深入的了解。不知不觉中,我就很少查阅文档了。

在项目之间添加引用在 C++ 和 C# 之间是不同的。您可以使用项目属性页来添加引用。只需导航到“通用属性”,然后单击“添加新引用...”按钮。

指定命名空间和解析引用的名称:在托管 C++ 中,您将使用“::”而不是“.”来解析名称。

// You'll use this syntax in managed C++
using namespace System::Windows::Forms;
// Instead of the C# syntax
//using System.Windows.Forms;

历史

  • 版本 1.0 - 2008 年 4 月 11 日 - 控件的初始发布。
© . All rights reserved.