介绍 本文演示了如何有效地创建自己的下拉控件,其灵活性要比捕获Windows标准ComboBox的Paint事件大得多。我所见过的大多数免费自定义下拉控件都不是它们所吹捧的那样。它们中的许多在没有正确关闭的情况下冻结在屏幕上,而且有些代码非常臃肿。 我终于咬紧牙关,决定从头创建自己的控件。由于我的时间表上的时间限制,我没有花很多时间开发设计时支持,但是目前的方法工作得很好。我们将介绍一个创建QuickColor的示例,即您在上面看到的控件。对于本文的范围,我假设您熟悉继承的概念。 使用的代码 我为继承的控件设计了一个基类,恰当地称为DropDownControl。这个类处理所有下拉相关的函数,因此我们可以将注意力集中在自定义控件的外观上。 创建新控件的第一步是向项目中添加一个新UserControl,切换到Code视图,并将其设置为继承DropDownControl。 隐藏,复制Code
public partial class QuickColor : UserControl //<-- replace with DropDownControl { ... }
切换回设计视图后,您将注意到的第一件事是,控件现在在顶部绘制了一个组合框。下面剩下的区域是设计下拉内容的地方。 图1 让我们来看看如何让它工作。为了使下拉控件在运行时工作,我们必须调用InitializeDropDown()方法。这应该在InitializeComponent()方法之后立即执行。InitializeDropDown方法接受一个类型为Control的参数,该参数决定下拉窗口的外观和大小。例如,如果您传入一个宽为150像素、高为50像素的标签控件,那么DropDownControl的窗口大小将被设置为150 x 50,并且Label控件将被加载到下拉窗口中,如图2所示。 隐藏,复制Code
public partial class QuickColor : DropDownControl { public QuickColor() { InitializeComponent(); InitializeDropDown(label1); } }
图2 显然,这个简单的示例在现实场景中没有太大用处,但它应该清楚地演示了如何初始化下拉窗口。以QuickColor为例,我们需要三个控件加载到下拉窗口: “无色”按钮颜色网格“更纯色”按钮 多个下拉项 如果下拉窗口只接受一个参数,我们怎么能有多个项目呢?对于多个控件,应该将面板控件用作容器项。这是用来包含QuickColor下拉窗口中的三个控件的。如您所知,Panel类扩展了控制类,因此它被作为InitializeDropDown()方法中的参数接受。 绘画锚 让用户知道在拖放窗口中选择了什么总是一个好主意。对于QuickColor,用户需要知道选择了什么颜色。有一个名为AnchorClientBounds的DropDownControl属性,它包含在锚内绘制内容的坐标。如果使用了标准的ClientRectangle属性,那么combobox呈现将被覆盖。使用AnchorClientBounds可以确保锚内容将直接呈现在组合框中。对于QuickColor,我设计了一个名为ColorPanel的控件,用于锚点区域。ColorPanel检查颜色的alpha组件(透明度),并且(如果必要的话)在背景中绘制一个PhotoShop中的网格,这样alpha组件会更明显。ColorPanel的边界是由AnchorClientBounds属性设置的,正如您在第一张图片中所看到的,这个属性与锚非常匹配。 表演下 当用户单击锚点上的任何位置时,窗口会自动下拉。但是,如果您有一个位于锚点上的控件(如ColorPanel),则必须捕获其鼠标事件并以编程方式强制窗口拖放。在QuickColor的情况下,我们需要覆盖ColorPanel的单击事件来启动拖放。 隐藏,复制Code
private void colorPanel1_Click(object sender, EventArgs e) { OpenDropDown(); }
关闭放下窗口 如果用户单击了下拉窗口边界之外的任何地方,该窗口将自动关闭。但是,如果鼠标单击位于下拉窗口的边界内,则必须确定应该采取什么操作来关闭窗口,并调用CloseDropDown()方法。在QuickColor的情况下,从颜色网格中选择一种颜色将关闭下拉窗口。 隐藏,复制Code
private void colorGrid1_SelectedIndexChange(object sender, EventArgs e) { this.Color = colorGrid1.Color; this.CloseDropDown(); ... }
冻结下降窗口 有些时候,需要将拖放窗口冻结在其已拖放状态。还记得QuickColor下拉窗口中的按钮“More Solid Colors”吗?这个按钮会启动一个新的对话框,允许用户在创建自定义颜色时有更多的选项。让我们来看看代码: 隐藏,复制Code
private void btnMoreSolidColors_Click(object sender, EventArgs e) { this.FreezeDropDown(false); // false=drop visibility ColorChooser frm = new ColorChooser(this.Color); if (frm.ShowDialog() != DialogResult.Cancel && !frm.Color.Equals(this.Color)) { this.Color = frm.Color; this.CloseDropDown(); ... } this.UnFreezeDropDown(); }
请注意FreezeDropDown()方法。这到底是怎么回事?如果从删除窗口打开新表单时没有调用FreezeDropDown(),则删除窗口将关闭并杀死由它发起的任何形态。如果您是一个技术爱好者,并且想了解为什么必须调用此方法的细节,请阅读下面的部分。其他人可以跳过这个(无聊的)部分。 拖放窗口实现了IMessageFilter,并侦听在拖放时发送的所有应用程序消息。创建消息时,下拉窗口使用静态表单方法Form. activeform . equals()来确保它是活动表单。当另一个窗体成为活动时,下拉窗口关闭并删除作为Windows消息侦听器的自身。调用FreezeDropDown()方法会导致拖放窗口挂起活动的表单检查,并强制其保持打开状态。(请注意,我不建议将IMessageFilter用于比下拉窗口寿命更长的控件中。) DropState属性 在设计复杂的下拉控件时,您可能会遇到需要知道下拉窗口状态的情况。有一个称为DropState的属性,您可以在任何给定的时间检查它,以确定下拉窗口的状态。注意:DropState是只读的,所以你不能用这个属性改变下拉窗口的状态。 下面是演示项目中更多自定义下拉控件的一些缩略图。DropState属性在关闭这两个拖放控件时非常方便。EmployeePicker(图3)在ListView的SelectedIndexChange事件触发时关闭。但是,当下拉窗口关闭并从它的控制列表中删除ListView时,出现了一个问题。SelectedIndexChange事件再次触发!这又会导致递归地调用CloseDropDown()方法,从而引发异常。为了避免这种情况,我们只需检查SelectedIndexChange处理程序中的DropState属性。如果窗口正在下降或关闭,我们退出处理程序。 隐藏,复制Code
private void listView1_SelectedIndexChanged(object sender, EventArgs e) { if (this.DropState == eDropState.Dropping || this.DropState == eDropState.Closing) return; this.CloseDropDown(); ... }
图3 上面的控件已经作为演示快速创建。它们的唯一目的就是激发你对各种可能性的想象力。 创建属性更改事件 Windows ComboBox控件触发selectedindexchangeevent,通知侦听器选择已经更改。我们还需要触发某种事件,以通知侦听器选择的更改。DropDownControl附带了一个通用事件PropertyChanged,但我强烈建议您为自定义控件创建一个更有意义的事件。对于QuickColor,我创建了一个ColorChanged事件。如果其他开发人员通过查看事件来捕捉QuickColor,他们会比PropertyChanged事件更快地发现ColorChanged事件。但是,如果您决定坚持使用PropertyChanged事件,则可以通过受保护的方法OnPropertyChanged()触发它。 最后的评论 我相信本文将帮助您创建自己的下拉控件。如果你看到任何功能,可以改进,请让我知道。我还希望听到您根据本文创建的控件。 PhotoShop中的色彩对话框的概念来自Danny Blanchard。我对原始代码做了几次修改,使其更紧凑、更高效。伟大的工作丹尼! 历史 7/19/2010:将受保护的方法“GetDropDownLocation()”更改为“GetDropDownBounds()”。这个方法现在通过检查其挂起的屏幕边界来验证下拉窗口的位置。特别感谢Marco Mastropaolo的建议。如果ParentForm被移动,下拉窗口现在会自动更新它的位置。伟大的建议丹·伦道夫2009年11月6日:修正了当操作系统不支持视觉样式时组合框呈现的问题。感谢Mathiyazhagan让我们注意到这一点。 本文转载于:http://www.diyabc.com/frontweb/news228.html