22.1.14 响应上下文菜单(1)
右击某个按钮将弹出只有Choose这一个菜单项的上下文菜单。当用户单击该菜单项时,程序应该显示允许输入适当数值的对话框。在Form1类的Design选项卡上单击该上下文菜单的名称,然后双击Choose菜单项,创建Click事件的处理程序。
第一个问题是确定引发该事件的是哪一组按钮。每一组按钮都在其自己的GroupBox控件内,而GroupBox类的Controls属性可返回对Control::ControlCollection类型的对象的引用,此对象表示本组合框内控件的集合。如果传递的实参控件在该集合内,则Control::Control Collection类定义的Contains()函数将返回true,否则返回false。因此,我们有办法确定触发Click事件的按钮属于哪一个按钮组。Choose菜单项Click事件处理程序的轮廓实现如下所示:
- System::Void chooseva lue_Click(System::Object^ sender, System::EventArgs^ e)
- {
- // Get the button that was clicked for the context menu, then...
-
- if(lottoValues->Controls->Contains(theButton))
- {
- // the button is from the lotto group...
- }
- else if(euroValues->Controls->Contains(theButton))
- {
- // The button is in the Values group...
- }
- else if(euroStars->Controls->Contains(theButton))
- {
- // The button is in the Stars group...
- }
- }
至少从原理上来说,上面的代码可以确定涉及的是哪一组按钮。但仍然有一点疑问,如何知道被右击而使上下文菜单打开的是哪一个按钮?
chooseva lue_Click()处理程序是在Choose菜单项被单击时调用的,因此该处理程序的sender参数标识的是Choose菜单项,而非被右击的按钮。需要有响应按钮上原来的右击事件的处理程序,在Form1的Design窗格中右击buttonContextMenu,可以创建所需的处理程序。如下所示,可以完成刚刚创建的处理程序函数的代码:
- System::Void buttonContextMenu_Opening(System::Object^ sender,
- System::ComponentModel::CancelEventArgs^ e)
- {
- contextButton = safe_cast<Button^>(buttonContextMenu->SourceControl);
- }
该函数将sender句柄强制转换为Button^类型,并将其存入Form1类的contextButton成员。因为该函数处理的事件属于上下文菜单,所以形参sender标识的是使该菜单显示出来的按钮。当然,还必须给Form1类添加私有成员变量contextButton:
- private:
- Button^ contextButton; // Button that was
right-clicked for context menu
现在需要做的就是考虑下一步做什么。
1. 处理Choose菜单项的逻辑
无论涉及的是哪一组按钮,响应Choose菜单项单击事件的处理程序可以相同,其工作过程大致如下:
(1) 显示一个允许输入数值的对话框。
(2) 检查输入值是否有效,即是否不超出范围,并且与其他按钮不同。
(3) 如果输入值无效,则显示相应的消息框,并允许重新输入或取消对话框操作。
(4) 如果输入值有效,则使用新值更新被右击的按钮。
实现该过程的第一步是创建一个新的对话框窗体。
2. 创建对话框窗体
按下Ctrl+Shift+A组合键,显示出Add New Item对话框,然后选择UI类别和Windows Form模板。输入窗体名称UserValueDialog,并单击Add按钮。现在,可以按下F4键,打开该窗体的Properties窗口,设置属性值,使其成为对话框。把ControlBox、MinimizeBox和MaximizeBox属性值设置为False,将Text属性设置为"User Value Input"。也应该将FromBorderStyle属性值设置为FixedDialog。
如图22-24所示,给对话框窗体添加OK和Cancel按钮以及Label和TextBox控件。
|
| (点击查看大图)图 22-24 |
把OK按钮的Text和(Name)属性值设置为OK,其DialogResult属性值也应该被设置为OK。Cancel按钮的Text、(Name)和DialogResult属性值应该是Cancel。将TextBox控件的(Name)属性值和TextAlign属性值分别设置为textBox和Center。Label控件的(Name)属性值可以是label,其Text属性可以是任何我们喜欢的内容,因为在代码中要根据具体情况修改该属性。
现在,可以再次显示出对话框窗体的属性,将AcceptButton和CancelButton属性的值分别设置为OK和Cancel。
3. 开发对话框类
在TextBox控件中输入的数值必须能够被Form1对象使用,因此给UserValueDialog类添加一个存储该数值的属性:
- public:
- property int Value;
这是一个常见标量属性的示例,因此其get()和set()函数是由编译器默认提供的。
对话框对象需要知道输入值的极限是什么,因为对话框类中OK按钮的处理程序要验证输入值是否合法。出于同样的原因,对话框对象需要知道按钮上的当前值是什么,以确保它们不重复。可以给UserValueDialog类再添加3个属性成员来存储相应的数据:
- public:
- property int LowerLimit;
- property int UpperLimit;
- property array<int>^ Values; // Current button values
使用该文本框后,不需要在其中放置虚假的值,因此在UserValueDialog类中添加如下的公有函数:
- void ClearTextBox()
- {
- textBox->Text = "";
- }
Form1对象必须能够根据显示该对话框时实际有效的极限值,修改label控件的Text属性。这可以通过给UserValidDialog类添加一个成员函数来实现:
- public:
- void SetLabelText(int lower, int upper)
- {
- label->Text = L"Enter your value between " + lower +L" and " + upper;
- }