NET User Interfaces in Csharp Windows Forms and Custom Controls [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

NET User Interfaces in Csharp Windows Forms and Custom Controls [Electronic resources] - نسخه متنی

Matthew MacDonald

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید




































Menus



Applications use two kinds of menus: main and context. Main menus provide a comprehensive set of options just below a window's title bar, and use standard headings like File, Edit, and Help. Context menus are floating or "pop up" menus that provide additional options. Typically, context menus appear when the user right-clicks a user interface element (like an object, or a system tray icon). Like main menus, context menus can contain nested layers of menus, but they tend to be much simpler.


Consider the partial menu shown in Figure 4-11. In .NET, this menu is modeled as a collection of objects, as shown in Figure 4-12. MenuItem objects make up every part of a menu except the top-level container.




Figure 4-11: A sample menu




Figure 4-12: Menu objects in .NET


The next code snippet shows how to create the first part of this menu (all the entries contained under the File heading). Generally, you use the Visual Studio .NET IDE to create the menu and configure items automatically through the custom menu designer.



// Build the menu starting with the deepest nested level
// (in this case, New, Open, and Save).
MenuItem mnuNew = new MenuItem();
mnuNew.Text = "New";
MenuItem mnuOpen = new MenuItem();
mnuOpen.Text = "Open";
MenuItem mnuSave = new MenuItem();
mnuSave.Text = "Save";
' Create the top-level File menu.
MenuItem mnuFile = new MenuItem();
mnuFile.Text = "File";
' Add the contained menu items to the File menu.
mnuFile.MenuItems.Add(mnuNew);
mnuFile.MenuItems.Add(mnuOpen);
mnuFile.MenuItems.Add(mnuSave);
// Create the main menu container.
MainMenu mnuMain = new MainMenu();
// Add the File menu to the main menu.
mnuMain.Add(mnuFile);
// Attach the main menu to the form.
this.Menu = mnuMain;


The next few sections dissect the .NET menu objects.



The Menu Class



In .NET, main menus, context menus, and menu items are all treated slightly differently. All of them inherit from the abstract Menu class (see Figure 4-13).




Figure 4-13: The menu object hierarchy


The Menu class contains a collection of MenuItem objects that corresponds to a single menu level, and provides functionality that lets you clone or merge these items into another menu, and find the top-level menu container. Table 4-11 lists the members of the Menu class.











Table 4-11: Members of the Abstract Menu Class






MemberPurpose















HandleThe internal operating system "handle" (number) that identifies this menu. It could be required for a low-level API call.










IsParentReturns true if this menu contains other menu items.










MenuItemsThe collection of MenuItem objects that represent the next level of the menu.










CloneMenu()Allows you to copy an array of MenuItem objects into the current menu. This method can be used to transfer items between a MainMenu and a ContextMenu, or vice versa.










GetContextMenu() and GetMainMenu()Returns the ContextMenu or MainMenu object that contains this menu, even if this menu is nested several layers deep in the hierarchy.









The MainMenu and ContextMenu Classes



At the top level, menu items are always contained in a MainMenu or ContextMenu class. These classes, which inherit from Menu, add a few additional frills. For example, the MainMenu class allows you to determine the form that owns the menu, while the ContextMenu class allows you to find the associated control and react to the Popup event when the menu is displayed (Tables 4-12 and 4-13). As you'll see, MainMenu and ContextMenu can't be treated equivalently in your code. This means that you can't display part of a main menu as a context menu, as you would in a Visual Basic 6 application.











Table 4-12: ContextMenu Members






MemberPurpose















SourceControlIndicates the control that "owns" this context menu (or null).










Popup eventAllows you to customize the menu when the menu is about to appear but has not yet been displayed. You can use this event to configure the context menu appropriately by hiding, adding, or disabling some items depending on the current state of your application.










Show()Displays the context menu on the screen, at the indicated position.














Table 4-13: MainMenu Members






MemberPurpose















CloneMenu()This new (alternative) CloneMenu() method can be called without arguments to create a duplicate copy of the MainMenu object.










GetForm()Returns the Form object that contains the menu.






Caution


Watch out—the MainMenu class adds a new CloneMenu() method for duplicating the menu. If you're transferring a few menu items from one menu to another, you'll want to use the CloneMenu() method inherited from the Menu class instead.






The MenuItem Class



Each command and submenu in a menu is represented by a MenuItem object. The MenuItem class is the most full-featured menu object, with several properties for configuring appearance and reacting to menu events, as described in Table 4-14.











Table 4-14: MenuItem Members






MemberPurpose















BarBreak and BreakThese properties allow you to create the unusual menus shown in Figure 4-14. Break, when set to true, instructs .NET to place the menu item in a separate column. For example, if you create an entire submenu of menu items with Break set to true, they appear as a horizontal menu arranged from left to right. BarBreak works identically, except it displays a vertical line as a column separator between menu items. These properties must be set in code—they aren't available in the Properties window.










Checked and RadioCheckIf set to true, a check mark is displayed next to the highlighted menu item. If RadioCheck is set to true, the menu item displays a bullet instead of a check mark when its Checked property is true. Typically the check mark style is used for a value that can be toggled on and off, while the radio button style is used when you provide a mutually exclusive list of menu selections, from which the user can choose only one.










DefaultItemThe default menu item for a menu is displayed in bold type. If the user double-clicks a submenu that contains a default item, the default item is selected automatically, and the submenu is closed. DefaultItem is usually provided as a guide for the user, and indicates the most common choice.










EnabledSets whether the menu should be selectable or disabled and displayed in "greyed out" text.










IndexThe numeric index of a menu item in its parent's MenuItems collection. You can modify this index to reposition the menu item.










MergeOrder and MergeTypeThis specifies the behavior for this menu when being merged with another menu, either programmatically through the MergeMenu() method, or automatically with MDI forms. The MergeOrder is a number representing relative position (0, the default, is first in the menu). MergeType defines the merge behavior using one of the values from the MenuMerge enumeration.










OwnerDraw and the DrawItem and MeasureItem eventsWhen OwnerDrawn is set to true, the default menu user interface is not provided for you. Instead, you need to handle the DrawItem and MeasureItem events.










Shortcut and ShowShortcutShortcut sets a hotkey from the Shortcut enumeration (like Ctrl+N or F8). ShowShortcut determines whether the shortcut is displayed with the text for the menu item.










TextSets the text for menu item. Use the ampersand character to precede the access key (as in E&xit to make "x" the access key).










VisibleIf set to false, the menu item is not be shown at all.










CloneMenu()Duplicates a menu. This method keeps all event handlers intact, and is useful when you need to reuse the same entries in a context menu as in a main menu.










MergeMenu()Combines the children of two menus into a single list. You could use this technique to create a context menu that contains the entries from two different submenus in the main menu. Just remember to use CloneMenu() first, and then merge the duplicate copy of the menus.










PerformClick() and PerformSelect()Triggers the appropriate Click or Select event for the MenuItem. These methods are rarely used, except if you are creating wizards or other tools that drive an application by "remote control."










Popup eventOccurs when the menu item's submenu is just about to be displayed. You can use this to dynamically tailor a menu just in time when it is selected. The Popup event is not raised for menus that do not contain submenu items.










Select eventOccurs when a menu item is highlighted, but not selected. This can occur when the menu item is scrolled over by using the keyboard controls, or hovered over with the mouse.










Click eventOccurs when the menu item is clicked with the mouse or triggered with the keyboard.









Figure 4-14: Menu variations





The Visual Studio .NET Menu Designer



Visual Studio.NET includes built-in support for creating any type of menus. To start, drag a main menu or context menu onto your form. It will appear in the component tray. To design a menu, click once to select it, and then type text in the onscreen "type here" areas (see Figure 4-15).




Figure 4-15: The Visual Studio .NET menu designer


Every time you add a new menu item, a "type here" area appears for a submenu and a new menu item. You can also drag and drop menu items in any order you want, and configure each menu item's name and properties in the Properties window when it is selected. Right-click a menu item and select Insert Separator to add a horizontal dividing line between menu entries.





Attaching a Menu



Unlike other controls, main menus are not added to the Controls collection of the hosting form. Instead, they are specifically set using the Menu property of the form. Once this link is set, the menu automatically appears at the top of the form.



this.Menu = mnuMain; // Attach the mnuMain MainMenu object.


Context menus use a similar technique with the ContextMenu property.



ctrl.ContextMenu = mnuContext; // Attach the mnuContext ContextMenu object.


Context menus, however, are not automatically shown. Instead, you need to show them manually using the ContextMenu.Show() method. Typically, you perform this task in the MouseUp event for the linked control.


The example that follows displays the control's context menu. It uses the sender parameter, making it completely generic. You could use this event handler for every control with a context menu in your application.



private void lbl_MouseUp(Object sender, System.Windows.Forms.MouseEventArgs e)
{
// Convert the sender parameter into a valid control reference.
Control ctrl = (Control)sender;
// If the right mouse button was pressed, show the menu.
if (e.Button == MouseButtons.Right)
{
ctrl.ContextMenu.Show(ctrl, new Point(e.X, e.Y));
}
}


Using a control's ContextMenu property is really just a convenience. You can display a context menu at any time, in response to any event, even if you haven't set the ContextMenu property of a nearby control. However, using the ContextMenu property allows you to write a generic method that can handle the MouseUp event for multiple controls. Your code simply needs to retrieve the ContextMenu property of the control that fired the event.





Menu Events



There are two ways you can handle menu selection events. You can write an individual event handler for the Click event of every MenuItem. This is ideal if you are writing all the menu code inside the current form.




private void mnuOpen_Click(System.Object sender, System.EventArgs e)
{
// (Do something here.)
}
private void mnuNew_Click(System.Object sender, System.EventArgs e)
{
// (Do something here.)
}
private void mnuSave_Click(System.Object sender, System.EventArgs e)
{
// (Do something here.)
}


However, if your menu just hands the task off to another class, it probably makes sense to handle all menu events in the same event handler. (In this case, your event handler acts as a generic switchboard). You can then determine which MenuItem fired the event by converting the sender parameter into a MenuItem object and examining its Text, or just by comparing object references (which is preferred, because the compiler alerts you if you refer to a nonexisting MenuItem, but it doesn't alert you if you enter incorrect menu text). The following code snippet handles the Click event of three MenuItem objects, and compares the event sender to the appropriate form-level variables to determine which item was clicked.



private void mnu_Click(System.Object sender, System.EventArgs e)
if (sender == mnuOpen)
{
MyApp.DoNew();
}
else if (sender == mnuNew)
{
MyApp.DoSave();
}
else if (sender == mnuSave)
{
MyApp.DoOpen();
}
}


This approach of handling all menu clicks in one method also provides an easy way to implement the standard MFC logic, where a help string is displayed in another control (typically a status bar) whenever a menu item is highlighted.





Copying and Cloning a Menu



.NET imposes some restrictions on menus. Items cannot belong to more than one menu, and they cannot be shared between types of menus. In many applications, a context menu is actually a subset of a main menu. To set this up with .NET, you need to copy the appropriate branch of the menu.


You might attempt this with the logical-appearing code shown below:



// A flawed approach.
ContextMenu mnuContext = new ContextMenu();
// Attempt to copy the menu items from the File menu.
foreach (MenuItem mnuItem in mnuFile.MenuItems)
{
mnuContext.MenuItems.Add(mnuItem.Text);
}


Unfortunately, this will copy the items but lose the event handlers. To preserve the event handling logic, you need to use the CloneMenu() method, as shown here:



// A copy operation that preserves event handlers.
ContextMenu mnuContext = new ContextMenu();
// Copy the menu items from the File menu into a context menu.
foreach (MenuItem mnuItem in mnuFile.MenuItems)
{
mnuContext.MenuItems.Add(mnuItem.CloneMenu());
}





Merging a Menu



In some cases, you might want to create a context menu that contains the entries from two different submenus. While you could do this by duplicating and manipulating the individual MenuItem objects, the easiest way is by using the built-in MenuItem.MergeMenu() method.


The example below combines the menu items in the top-level File and Edit menus.



// Create a copy of the menus you want to merge.
MenuItem mnuMergeFile, mnuMergeEdit;
mnuMergeFile = mnuFile.CloneMenu();
mnuMergeEdit = mnuEdit.CloneMenu();
// Merge the duplicate copy of the menus.
mnuMergeFile.MergeMenu(mnuMergeEdit);
// Now add the merged menu to the appropriate control.
this.ContextMenu = mnuMergeFile;





/ 142