C# 中的信号强度控件
一个类似手机信号强度的控件。

引言
这是一个相对简单的控件,它演示了开发自定义控件所使用的一些技术。我们将研究如何隐藏我们不希望通过设计器暴露的属性,如何正确列出我们的自定义属性,以及其他一些技巧。
背景
自定义控件和设计时支持有点令人畏惧,MSDN 文档难以通读,除非有人向我们展示如何操作,否则并非总是显而易见的。我开发自定义控件已有几年了,但我仍在学习自定义控件开发和丰富的设计时支持的 intricacies。希望这篇文章能为控件开发的黑暗世界带来一些光明。
隐藏属性
在真正深入 SignalStrengthMeter
的细节之前,我想先探讨一些控件开发的主题。首先是隐藏设计时属性。
当你通常在 Visual Studio 中创建一个新的自定义控件时,你会继承 UserControl
类。你可以选择继承几乎任何控件,甚至包括按钮、面板、单选按钮等。最好选择一个最能代表你想要开发的控件。问题是,无论你选择继承哪个控件,你都会在属性窗口中看到一大堆对你正在开发的控件没有用或没有意义的属性。
这就是属性隐藏的作用。每个属性都有大量与其关联的设计时元数据,你只需要更改元数据,使其不再显示在属性窗口中。唯一的方法(至少据我所发现的)是创建一个新的“隐藏”属性,并为其分配正确的元数据,例如:
[Browsable(false), DesignerSerializationVisibility
(DesignerSerializationVisibility.Hidden),
EditorBrowsable(EditorBrowsableState.Never)]
public new Image BackgroundImage
{
get { return base.BackgroundImage; }
set { base.BackgroundImage = value; }
}
有几个有趣的注意事项,第一点是属性前面带有“new
”关键字。这告诉编译器你打算“隐藏”基类对该属性的实现(可以尝试删除它并编译,查看错误)。接下来是 Browsable
和 DesignerSerializationVisibility
。将 Browsable
属性设置为 false
会告诉 Visual Studio 设计器,你不希望在设计时显示该属性。将 DesignerSerializationVisibility
设置为“Hidden
”会告诉设计器,你不希望在设计时序列化(保存)该属性的设置。
不幸的是,这并不能隐藏代码中的属性,它仍然显而易见。即使将访问级别设置为 private
或 protected
也无法从代码中移除该成员。
描述你的属性
这是你在 CodeProject 等地方找到的自制控件中经常遇到的一个大问题,没有人花时间对控件进行分类或描述,以提供丰富的运行时体验。想想不得不使用你代码的可怜开发者吧!
有 3 个重要的设置用于描述你的数据:
Browsable
(true)Category
("Category Name")Description
("Description")
显然还有很多,但这些是基础。你还可以考虑使用 DefaultValue
属性,你可以用它来标识默认值(这样该值在属性窗口中就不会显示为粗体)。
下面是一个应用三个主要属性(以及我将在下面描述的另外两个属性)的示例:
[Browsable(true), EditorBrowsable( EditorBrowsableState.Always),
Bindable( BindableSupport.Yes),
Category("Appearance"), Description("Number of bars in the signal monitor")]
public int NumberOfBars
{
get { return numberOfBars; }
set { numberOfBars = value; this.Invalidate(); }
}
你可以看到,我应用了 Browsable
属性,使其显示在属性窗口中;Category
属性,将其放置在属性窗口的“Appearance”类别中;以及 Description
,以便当用户选择该属性时,属性窗口底部的描述框会显示该属性的友好描述。
我还使用了另外两个属性,我不会详细介绍,但你可以将控件的某些属性设置为“高级”属性,我总是设置为始终显示,尽管我在 Visual Studio 中找不到隐藏高级属性的地方。第二个属性将该属性描述为“可绑定”,意味着我允许应用程序将其用于数据驱动的应用程序(在我的例子中,我用它们进行皮肤化,但在本项目中没有)。
关于元数据的最后一点:有一个名为 DisplayName
的元数据属性,你可以将其设置为任何你想要的属性名称,这样你就可以将“NoSignalThreshold
”变成“No Signal Threshold”。我强烈建议**避免**使用此属性。为什么?因为如果某个开发人员以后想扩展你的控件,或者基于你的控件创建一个新控件,那么如果他必须通过 Intellisense 来搜索它,而实际属性名称与属性窗口中的名称不匹配,那么隐藏该属性就会变得非常困难。
自定义控件区域
我一直很讨厌“透明”背景实际上并不透明的事实,它只是吸取其父控件(是的,窗体也是一个控件)的背景色。我在这款控件中加入了通过自定义控件区域实现真正透明的能力。你可以查看 CalculateRegion()
函数来了解我的实现方式,但基本上你只需要绘制一个包含所需区域的 GraphicsPath
,然后排除其他所有区域。
这可能会对性能产生一些影响,因为 Windows 现在必须对复杂的区域进行一些精细的剪裁,但我认为视觉效果是值得的。唯一的问题是,如果你将此控件放在另一个控件(如按钮)之上,用户可以在区域之间单击并影响其下方的控件。
Using the Code
我不会深入介绍实现细节,因为代码中确实没有什么革命性的东西。它只是绘制了许多条形图,计算它们是否已填充,填充样式(实心或渐变),以及颜色应是什么,具体取决于信号的“质量”。使用该控件是自明的,只需将控件拖放到窗体上,设置 MinValue
和 MaxValue
属性,然后设置 value 属性。
你可以将方向设置为从右到左、从左到右、从上到下和从下到上。我忽略了一件事是让它们顶部、底部、左侧或右侧对齐,这是未来可能进行的增强。
关注点
UI 开发更多的是艺术而非技巧,不幸的是,我不太擅长艺术,这个控件的外观和感觉还有很多改进的空间,尽情发挥吧!
历史
- 版本 1.0 - 初始发布
- 版本 1.1 - Bug 修复:
BottomToTop
布局样式在设置为 Transparent 时未计算区域