Windows Presentation Foundation

Hello, WPF

WPF from Scratch
Navigation Applications
Content Model
Layout
Controls
Data Binding
Dependency Properties
Resources
Styles and Control Templates
Graphics
Application Deployment
Where Are We?

Layout

Introduction
Layout Basics
DockPanel
StackPanel
Grid
Canvas
Viewbox
Text Layout
Common Layout Properties
When Content Doesn't Fit
Custom Layout
Where Are We?

Controls

Introduction
What Are Controls?
Handling Input
Built-In Controls
Where Are We?

Data Binding

Introduction
Without Data Binding
Data Binding
Binding to List Data
Data Sources
Master-Detail Binding
Where Are We?

Styles and Control Templates

Introduction
Without Styles
Inline Styles
Named Styles
Element-Typed Styles
Data Templates and Styles
Triggers
Control Templates
Where Are We?

Resources

Introduction
Creating and Using Resources
Resources and Styles
Binary Resources
Global Applications
Where Are We?

Graphics

Introduction
Graphics Fundamentals
Shapes
Brushes and Pens
Transformations
Visual-Layer Programming
Video and 3-D
Where Are We?

Animation

Animation Fundamentals
Timelines
Storyboards
Key Frame Animations
Creating Animations Procedurally
Where Are We?

Custom Controls

Introduction
Custom Control Basics
Choosing a Base Class
Custom Functionality
Templates
Default Visuals
Where Are We?

ClickOnce Deployment

A Brief History of Windows Deployment
ClickOnce: Local Install
The Pieces of ClickOnce
Publish Properties
Deploying Updates
ClickOnce: Express Applications
Choosing Local Install versus Express
Signing ClickOnce Applications
Programming for ClickOnce
Security Considerations
Where Are We?

Built-In Controls

3.3. Built-In Controls

WPF provides a range of built-in controls . Most of these correspond to standard Windows control types that you will already be familiar with. Note that none of these controls are wrappers around old Win32 controls. Although they look like their Win32 counterparts, they are all native WPF controls. This means that they offer full support for all of the WPF functionality described in this book, including styling, resolution independence, data binding, composition, and fully integrated support for WPF's graphical capabilities.

3.3.1. Buttons

Buttons are controls that a user can click. The result of the click is up to the application developer, but there are common expectations, depending on the type of button. For example, clicking on a CheckBox or RadioButton is used to express a choice and does not normally have any immediate effect beyond visually reflecting that choice. By contrast, clicking on a normal Button usually has some immediate effect.

Using buttons is straightforward. Example 3-11 shows markup for a Button element:

Example 3-11. Markup for a Button
<Button Click="ButtonClicked">Button</Button>

The contents of the element (the text "Button" in this case) are used as the button caption. A handler for the Click event has been specified with an attribute. This indicates that the code-behind for the XAML must contain a method with the name specified in the markup, such as that shown in Example 3-12. (Of course we could also attach the event handler by giving the button an x:Name and using normal C# event-handling syntax.)

Example 3-12. Handling a Click event
private void ButtonClicked(object sender, RoutedEventArgs e) {
    MessageBox.Show("Button was clicked");
}


Alternatively, a button's Command property may be set, in which case the specified command will be invoked when the button is clicked. Example 3-13 shows a button that invokes the standard ApplicationCommands.Copy command.

Example 3-13. Invoking a command with a Button
<Button Command="Copy">Copy</Button>

Figure 3-4 shows the three button types provided by WPF. These all derive from a common base class, ButtonBase. This in turn derives from ContentControl, meaning that they all support the content model: you are not restricted to using simple text as the label for a button.

Figure 3-4. Button types


As Figure 3-5 shows, you can use whatever content you like, although you will still get the default look for the button around or alongside your chosen content. (If you wish to replace the whole appearance of the button rather than just customize its caption, you can use a control template. See Chapter 5 for more information on templates.)

Figure 3-5. Buttons with nested content (Color Plate 4)


Although the buttons derive from the common ButtonBase base class, RadioButton and CheckBox derive from it indirectly via the ToggleButton class. This defines an IsChecked property, indicating whether the user has checked the button.

Radio buttons are normally used in groups in which only one button may be selected at any time. Use the RadioButtonList element to indicate that a set of radio buttons act as a group, as shown in Example 3-14.

Example 3-14. Grouping radio buttons
<RadioButtonList>
    <RadioButton>To be</RadioButton>

    <RadioButton>Not to be</RadioButton>
</RadioButtonList

3.3.2. Slider and Scroll Controls

WPF provides controls that allow a value to be selected from a range. They all offer a similar appearance and usage: they show a track, indicating the range, and a draggable "thumb" with which the value can be adjusted. There are two slider controls , HorizontalSlider and VerticalSlider , shown in Figure 3-6. There are also two scrollbar controls, HorizontalScrollBar and VerticalScrollBar, shown in Figure 3-7. The main difference is one of convention rather than functionalitythe scrollbar controls are commonly used in conjunction with some scrolling viewable area, while the sliders are used to adjust values.

Figure 3-6. Horizontal and vertical sliders


Figure 3-7. Horizontal and vertical ScrollBars


Both the sliders and the scrollbars are very similar in use. They all derive from a common base class, RangeBase. This class provides Minimum and Maximum properties, which define the range of values the control represents, and a Value property holding the currently selected value. It also defines SmallChange and LargeChange properties, which determine by how much the Value changes when adjusted with the arrow keys, or the PageUp and PageDown keys, respectively. The LargeChange value is also used when the part of the slider track on either side of the thumb is clicked.

While slider controls have a fixed-size thumb, the thumb on a scrollbar can change in size. If the slide is used in conjunction with a scrollable view, the size of the thumb relative to the track is proportional to the size of the visible area relative to the total scrollable area. For example, if the thumb is about one third of the length or height of the scrollbar, this indicates that one third of the scrollable area is currently in view.

You can control the size of a scrollbar's thumb with the ViewPortSize property. This can be anywhere from 0 to the Maximum property value. If the ViewPortSize is equal to Maximum, the thumb will fill the track, and will not be moveable. The smaller ViewPortSize is, the smaller the thumb will be.

If you want to provide a scrollable view of a larger user-interface area, you would not normally use the scrollbar controls directly. It is usually easier to use the ScrollViewer control.

A ScrollViewer element has a single child. Example 3-15 uses an Ellipse element, but it could be anything. If you want to put multiple elements into a scrollable view, you would nest them inside one of the panels discussed in Chapter 2.

Example 3-15. ScrollViewer
<ScrollViewer HorizontalScrollBarVisibility="Auto">
    <Ellipse Fill="VerticalGradient Green DarkGreen" Height="1000" Width="2000" />
</ScrollViewer>

If the content of a ScrollViewer is larger than the space available, the ScrollViewer can provide scrollbars to allow the user to scroll around the content, as Figure 3-8 shows. By default, a ScrollViewer provides a vertical scrollbar, but not a horizontal one. In Example 3-15, the HorizontalScrollBarVisibility property has been set to Auto, indicating that a horizontal scrollbar should be added if required.

Figure 3-8. ScrollViewer


This Auto visibility we've chosen for the horizontal scrollbar is different from the default vertical behavior. The VerticalScrollBarVisibility defaults to Visible, meaning that the scrollbar is present whether it is required or not.

There are two ways to make sure a scrollbar does not appear. You can set its visibility either to Disabled (the default for horizontal scrollbars) or to Hidden. The distinction is that Disabled constrains the logical size of the ScrollViewer's contents to be the same as the available space. Hidden allows the logical size to be unconstrained, even though the user has no way of scrolling into the excess space. This can change the behavior of certain layout styles.

To examine how these settings affect the behavior of a ScrollViewer, we'll look at what happens to Example 3-16 as we change the ScrollViewer properties.

Example 3-16. A resizable layout
<ScrollViewer ...>
    <Grid>

        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <RowDefinition Height="Auto" />

        <Button Grid.Column="0">Stretched</Button>
        <Button Grid.Column="1">Stretched</Button>
        <Button Grid.Column="2">Stretched</Button>
    </Grid>

</ScrollViewer>

This example shows a Grid containing three Button elements in a row. If the Grid is given more space than it requires, it will stretch the buttons to be wider than necessary. If it is given insufficient space, it will crop the buttons. If it is placed inside a ScrollViewer, it will be possible for the ScrollViewer to provide enough virtual, scrollable space for it, even if the space onscreen is insufficient.

Figure 3-9 shows how the Grid in Example 3-16 appears in a ScrollViewer when there is more than enough space. All four options for HorizontalScrollBarVisibility are shown, and in all four cases, the buttons have been stretched to fill the space.

Figure 3-9. HorizontalScrollBarVisibility settings with enough space


Figure 3-10. HorizontalScrollBarVisibility settings with insufficient space


Figure 3-10 shows the same four arrangements, but with insufficient horizontal space. The top two ScrollViewer elements have horizontal scrolling enabled, with Visible and Auto respectively. As you would expect, the ScrollViewer has provided enough space to hold all of the content and allows the user to scroll the cropped part into view. At the bottom left, where the horizontal scrollbar is set to Hidden, the layout behavior is the sameit has arranged the elements as though there were enough space to hold all of them. The only difference is that it has not shown a scrollbar. At the bottom right, we can see that the behavior resulting from Disabled is different. Here, not only is a scrollbar not shown, but horizontal scrolling is disabled completely. The Grid has therefore been forced to crop the buttons to fit into the available space.

3.3.3. Text Controls

WPF provides controls for editing and displaying text . The simplest text editing control is TextBox. By default, it allows a single line of text to be edited, but by setting AcceptReturn to true, it can edit multiple lines. It provides standard basic text editing facilities: selection support, system clipboard integration (cut, paste, etc.) and multi-level undo support.

Example 3-17 shows two TextBox elements, one with default settings and one in multi-line mode. It also shows PasswordBox, which is similar but is designed for entering passwords. Figure 3-11 shows the results. As you can see, the text in the PasswordBox has been displayed as a line of asterisks. This is common practice, to prevent passwords from being visible to anyone who can see the screen. The PasswordBox also opts out of the ability to copy its contents to the clipboard.

Example 3-17. TextBox and PasswordBox
<StackPanel Orientation="Horizontal">

    <TextBox Margin="5" VerticalAlignment="Center">Single line textbox</TextBox>

    <TextBox AcceptsReturn="True" Margin="5"
             VerticalAlignment="Center">Multiline textbox</TextBox>

    <PasswordBox 
 Margin="5" VerticalAlignment="Center" />
</StackPanel>

Figure 3-11. TextBox and PasswordBox


TextBox and PasswordBox only support plain text. They do not support any kind of nested content attempting to nest anything other than plain text will cause an error at runtime. This makes them easy to use for entering and editing simple data. TextBox provides a Text property that represents the control's contents as a String.

PasswordBox does not have a Text property. Instead, it has a Password property. Rather than returning a String, this returns a SecureString . This still supports only plain-text values. However, it provides two mechanisms to prevent accidental leakage of password data.

First, it stores the string in encrypted form. This means that should the memory containing the string be paged out to the system paging file, its contents will not be readable. (.NET generates a random encryption key at runtime and stores it in a memory location that is locked to prevent it from being written to the paging file.) If a normal string were used, an attacker might be able to retrieve its contents by making a copy of your system swapfile. Various .NET security APIs can be passed a SecureString, meaning that your code never has to deal with the decrypted version.

Secondly, you can call Dispose on a SecureString, which overwrites the password data. This means that even in its encrypted form, the sensitive data can be wiped on demand. With a normal String, data can remain in memory long after you have finished with it, being destroyed only when the garbage collector happens to run.

Be aware that a new SecureString is returned each time you read the Password property, so if you plan to take advantage of this wipe-on-demand behavior, you must Dispose after each time you read this property. The PasswordBox maintains its own internal copy of the string, which it disposes of for you when the control is destroyed.


The simplicity of plain text is good if you require nothing more than plain text as input. However, it is sometimes useful to allow more varied input. WPF therefore offers the RichTextBox.

RichTextBox is very flexibleit can contain almost any content. Example 3-18 shows markup for a RichTextBox containing a mixture of text, graphics, and controls. The results are shown in Figure 3-12.

Example 3-18. RichTextBox with mixed content
<RichTextBox>
    RichTextBox
    <Ellipse Fill="Red" Width="100" Height="25" />
    containing
    <Polyline Stroke="Blue" Points="0,0 10,30 20,33 30,28 40,35 50,10" />
    <Bold>rich</Bold>

    <Button>
        <TextBlock>and <Italic>varied</Italic></TextBlock>
    </Button>
    content!
</RichTextBox>


Figure 3-12. RichTextBox with mixed content (Color Plate 5)


The non-text elements are treated in the same way as characters of textyou can insert text before or after them, or move them around using cut and paste. Windows doesn't define a standard way for a user to "type" an ellipse with the keyboard, so although we can preload the RichTextBox with these elements in markup, it doesn't provide any way for the user to add such elements. However, it is easy enough to write a program that provides such a facility.

Example 3-19 shows the markup for an application that uses a RichTextBox. At the top of the screen, it provides a panel containing controls allowing the user to add rectangles and ellipses. The application is shown in Figure 3-13.

Figure 3-13. Adding non-text elements to a RichTextBox (Color Plate 6)


Example 3-19. Adding non-text elements
<Window x:Class="RichEditApp.Window1"
    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
    Text="RichEditApp"
    >
    <Grid>
        <Grid.RowDefinitions>

            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <StackPanel Orientation="Horizontal">
            <Label x:Name="widthLabel" Target="{Binding ElementName=widthBox}">
                <TextBlock><AccessKey>W</AccessKey>idth:</TextBlock>

            </Label>
            <TextBox x:Name="widthBox">100</TextBox>
            <Label x:Name="heightLabel" Target="{Binding ElementName=heightBox}">
                <TextBlock><AccessKey>H</AccessKey>eight:</TextBlock>

            </Label>
            <TextBox x:Name="heightBox">10</TextBox>
            <Label x:Name="fillLabel" Target="{Binding ElementName=fillBox}">
                <TextBlock><AccessKey>F</AccessKey>ill:</TextBlock>

            </Label>
            <TextBox x:Name="fillBox">Red</TextBox>
            <Label x:Name="strokeLabel" Target="{Binding ElementName=strokeBox}">
                <TextBlock><AccessKey>S</AccessKey>troke:</TextBlock>

            </Label>
            <TextBox x:Name="strokeBox">Black</TextBox>
            <Button Click="AddRectangleClick">
                <TextBlock><AccessKey>R</AccessKey>ectangle</TextBlock>

            </Button>
            <Button Click="AddEllipseClick">
                <TextBlock><AccessKey>E</AccessKey>llipse</TextBlock>
            </Button>

        </StackPanel>
        <RichTextBox 
 VerticalScrollBarVisibility="Visible" Grid.Row="1"
                     x:Name="inputBox" Wrap="True" />

    </Grid>
</Window>

The code to add these elements is fairly straightforward. It is in the code-behind file shown in Example 3-20. The code creates a shape; sets the Width, Height, Fill, and Stroke properties; and then adds it to the RichTextBox content. The RichTextBox provides a TextSelection property indicating the current selection or insertion point. The code simply inserts the shape at the start of that region.

Example 3-20. Adding content to a RichTextBox
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;


namespace RichEditApp {
    public partial class Window1 : Window {
        public Window1(  ) {
            InitializeComponent(  );
        }

        private void AddRectangleClick(object sender, RoutedEventArgs e) {
            Rectangle rect = new Rectangle(  );
            SetShapeParams(rect);
            inputBox.TextSelection.Start.InsertEmbeddedElement(rect);
        }

        private void AddEllipseClick(object sender, RoutedEventArgs e) {
            Ellipse ellipse = new Ellipse(  );
            SetShapeParams(ellipse);
            inputBox.TextSelection.Start.InsertEmbeddedElement(ellipse);
        }

        private void SetShapeParams(Shape s) {
            s.Width = double.Parse(widthBox.Text);
            s.Height = double.Parse(heightBox.Text);
            BrushConverter b = new BrushConverter(  );
            if (fillBox.Text.Length > 0) {
                s.Fill = (Brush) b.ConvertFromString(fillBox.Text);
            }
            if (strokeBox.Text.Length > 0) {
                s.Stroke = (Brush) b.ConvertFromString(strokeBox.Text);
            }
        }
    }
}

3.3.4. Label

In the previous section, Example 3-19 used the Label control. It is typically used to provide a caption for a control that does not have its own built-in captionthe most common example being the TextBox control. Label might appear to be redundant, since the same visual effect can be achieved without a full control; you could just use the low-level TextBlock element. However, Label has an important focus-handling responsibility.

Well-designed user interfaces should be easy to use from the keyboard. A common way of achieving this is to provide access keys . An access key is a letter associated with a control, so that if you hold down the Alt key and then press the access key, the behavior is the same as if you had clicked the control in question. Applications advertise their access keys to the user by underlining the relevant letter in the control's caption when the Alt key is held down. (You can also configure Windows to show access key underlines at all times, whether Alt is pressed or not.) The AccessKey inline element used in Example 3-19 manages this underlining for us.

Figure 3-14 shows a close-up of part of the user interface defined by Example 3-19, showing the access keys underlined, as the user would see if she were holding down the Alt key. With a Button, no special code is required to make the access key work. All you need to do is use the AccessKey inline modifier within the markup of the control. In this case, hitting Alt+R or Alt+E will automatically have the same effect as clicking on the Rectangle or Ellipse buttons.

Figure 3-14. Access key underlines


The TextBox poses slightly more of a challenge. It does not have a captionthe only text it displays is the text being edited. The caption is supplied by a separate element to the left of the TextBox. This is where the Label control comes in. The purpose of the Label control is to provide a place to put the caption with its AccessKey element. When the access key is pressed, the Label will redirect the input to the relevant control, which in this case is a TextBox.

How does the Label know which control it should redirect its access key to? Label has a Target property, indicating the intended target of the access key. We use a binding expression to connect the label to its target. Binding expressions are discussed in detail in Chapter 4. The expression here simply sets the Target property to refer to the named element.

3.3.5. Selectors

Several kinds of controls allow the user to select from a set of items. In some cases, such as the RadioButtonList and TabControl , there is usually exactly one selected item. ComboBox extends this functionality with the option to have no current selection. ListBox goes one step further, with the ability to select multiple items simultaneously. All of these controls inherit their common list and selection functionality from the Selector base class.

The simplest way to use any of these controls is to add content to their Items property. Example 3-21 shows the markup for a ComboBox with various elements added to its Items.

Example 3-21. Content in Items

<ComboBox>
    <ComboBox.Items>
        <Button>Click!</Button>
        <TextBlock>Hello, world</TextBlock>
        <StackPanel Orientation="Horizontal">

            <TextBlock>Ellipse:</TextBlock><Ellipse Fill="Blue" Width="100" />
        </StackPanel>
    </ComboBox.Items>
</ComboBox>

This same technique can also be used with ListBox, TabControl , and RadioButtonList. As you can see in Figure 3-15, each of the controls presents the items in its own way. The RadioButtonList generates a RadioButton for each item, using the item as the caption. The TabControl puts each element in its own TabItem, in order to present it on its own tab page. (Figure 3-15 just shows the first item, but the other three are accessible through the three tab headers.)

Figure 3-15. Content in selectors (left to right, top to bottom: ComboBox, ListBox, TabControl, and RadioButtonList)


All selector controls wrap our items in order to present them in a suitable way. Although this can be convenient, in some cases you will want a little more control. For example, the TabControl shown above isn't particularly usefulit has wrapped our items with tabs that have no title. To fix this, we simply provide our own TabItem elements instead of letting the TabControl generate them for us. We can then set the Header property in order to control the tab-page caption. These techniques are illustrated in Example 3-22.

Example 3-22. Setting tab page headers
<TabControl Grid.Row="3" Grid.Column="1">
    <TabControl.Items>

        <TabItem Header="Button">
            <Button>Click!</Button>

        </TabItem>

        <TabItem>
            <TabItem.Header>
                <TextBlock FontSize="18"
                    FontFamily="Palatino Linotype">Text</TextBlock>
            </TabItem.Header>

            <TextBlock>Hello, world</TextBlock>
        </TabItem>

        <TabItem>
            <TabItem.Header>
                <Ellipse Fill="Blue" Width="30" Height="20" />

            </TabItem.Header>

            <StackPanel Orientation="Horizontal">
                <TextBlock>Ellipse:</TextBlock>
                <Ellipse Fill="Blue" Width="100" />
            </StackPanel>

        </TabItem>

    </TabControl.Items>
</TabControl>

Example 3-22 shows the markup for a TabControl with the same three items as before, but this time with the TabItem elements specified explicitly. In the first of these, the Header property has been set to the text "Button." The other two items illustrate that these headers support nested contentthe first uses a TextBlock to control the text appearance, and the second puts an Ellipse into the header instead of text. Figure 3-16 shows the results.

Figure 3-16. TabItem headers


Providing a fixed set of elements through the Items property makes sense for tab pages and radio buttons, where you are likely to know what elements are required when you design the user interface. But this may not be the case for combo boxes and lists. To enable you to decide at runtime what items will appear, all selector controls offer an alternative means of populating the list: data binding. Instead of using Items, you can provide a data-source object with the ItemsSource property and use data styling to determine how the elements appear. These techniques are described in Chapters 4 and 5.

Regardless of whether you use a fixed set of items or a bound data source, you can always find out when the selected item changes by handling the SelectionChanged event. You can then use either the SelectedIndex or SelectedItem property to find out which item is currently selected.

3.3.6. Menus

Many Windows applications provide access to their functionality through a hierarchy of menus . These are typically presented as either a main menu at the top of the window or as a pop-up "context" menu. WPF provides two menu controls. Menu is for permanently visible menus (such as a main menu), and ContextMenu is for context menus.

Menus in Windows today are typically treated differently from other user-interface elements. There is a special handle type for them. Event handling makes special provisions for menus. In Windows Forms , most visible elements derive from a Control base class, but menus do not. This means that menus tend to be somewhat inflexiblesome user interface toolkits choose not to use the built-in menu handling in Windows simply to avoid the shortcomings. In WPF, menus are just normal controls, so they do not have any special features or restrictions.


Both kinds of menus are built in the same waytheir contents consist of a hierarchy of MenuItem elements. Example 3-23 creates a typical menu. The results are shown in Figure 3-17.

Figure 3-17. Menu


Example 3-23. A main menu
<Menu>
    <MenuItem Header="File">
        <MenuItem Header="New" />
        <MenuItem Header="Open..." />
        <MenuItem Header="Save" />
        <MenuItem Header="Save As..." />

        <MenuItem Mode="Separator" />
        <MenuItem Header="Page Setup..." />
        <MenuItem Header="Print..." />
        <MenuItem Mode="Separator" />
        <MenuItem Header="Exit" />
    </MenuItem>

    <MenuItem Header="Edit">
        <MenuItem Header="Undo" />
        <MenuItem Header="Redo" />
        <MenuItem Mode="Separator" />
        <MenuItem Header="Cut" />
        <MenuItem Header="Copy" />

        <MenuItem Header="Paste" />
        <MenuItem Header="Delete" />
        <MenuItem Mode="Separator" />
        <MenuItem Header="Select All" />
    </MenuItem>
    <MenuItem Header="Help">

        <MenuItem Header="Help Topics" />
        <MenuItem Header="About..." />
    </MenuItem>
</Menu>

ContextMenu is used in a very similar way. The main differences are the appearanceMenu has a horizontal bar at the topand the fact that a Menu can be used anywhere in the UI, while a ContextMenu can be used only as the value of an element's ContextMenu property. Example 3-24 shows a Grid element with a ContextMenu.

Example 3-24. Grid with ContextMenu
<Grid>
    <Grid.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Foo" />
            <MenuItem Header="Bar" />

        </ContextMenu>
    </Grid.ContextMenu>
...
</Grid>

With this context menu in place, a right-click anywhere on the grid will bring up the context menu. Figure 3-18 shows the context menu in action.

Figure 3-18. Context menu


Each MenuItem has a Header property. For children of a Menu, the header determines the label shown on the menu bar. For a MenuItem nested in either a ContextMenu or inside another MenuItem, the Header contains the content for that menu line. The Header property allows either plain text, as shown in Example 3-23, or nested content. Example 3-25 shows a modified version of one of the menu items, exploiting the ability to add structure in order to mark a letter as an access key. Figure 3-19 shows the results.

Example 3-25. Nesting content inside MenuItem.Header
<MenuItem>
    <MenuItem.Header>
        <TextBlock>
            <AccessKey>N</AccessKey>ew...
        </TextBlock>

    </MenuItem.Header>
</MenuItem>

Figure 3-19. Menu item with access key


The menu in Example 3-23 doesn't do anything useful, because there are no event handlers or commands specified. There are two ways you can hook up a MenuItem to some code. You can handle its Click event in much the same way that you would handle a button click. Alternatively, you can set the Command property on the MenuItem, using the same syntax we saw earlier in Example 3-8.

Example 3-26 shows a modified version of the Edit submenu, with menu items associated with the relevant standard commands. As long as the focus is in a control such as TextBox or RichTextBox that understands these standard commands, these commands will be handled without needing any explicit coding. (If the focus is not in such a control, the commands will simply bubble up. If nothing handles the command, it will be ignored.)

Example 3-26. MenuItems with Commands
<MenuItem Header="Edit">
    <MenuItem Header="Undo" Command="Undo" />

    <MenuItem Header="Redo" Command="Redo"/>
    <MenuItem Mode="Separator" />
    <MenuItem Header="Cut" Command="Cut" />
    <MenuItem Header="Copy" Command="Copy" />
    <MenuItem Header="Paste" Command="Paste" />
    <MenuItem Header="Delete" Command="Delete" />

    <MenuItem Mode="Separator" />
    <MenuItem Header="Select All" Command="SelectAll" />
</MenuItem>

Menus often have a shortcut key as well as an accelerator . The accelerator works only when the menu is open. A shortcut, such as Ctrl+S for save, works whether the menu is open or not. The menu isn't responsible for binding the control shortcut to the key gesturedas we saw earlier, we do this with CommandBindings, which associate inputs with commands. However, menus conventionally display the shortcut in order to help users discover them.

If a menu item's Command has an associated key-gestured binding, WPF will automatically display this shortcut in the menu. Since Example 3-26 uses standard clipboard and undo/redo commands, the menu it produces has shortcuts associated, as you can see in Figure 3-20.

Figure 3-20. Automatic shortcut display


If for some reason you choose not to use WPF's command system, you can still display a shortcut. MenuItem provides an InputGestureText property that lets you choose the text that appears in the normal place for such shortcuts. Example 3-27 shows a menu item with both a shortcut and an access key.

Example 3-27. Menu item with shortcut and access key
<MenuItem InputGestureText="Ctrl+N">
    <MenuItem.Header>
        <TextBlock><AccessKey>N</AccessKey>ew</TextBlock>

    </MenuItem.Header>
</MenuItem>

Note that Menu and ContextMenu both derive indirectly from ItemsControl, the same base class as all of the selector controls. This means that you can use the ItemsDataSource property to populate a menu using data binding, rather than using fixed content. This could be useful if you want to make your menu structure reconfigurable. See Chapter 4 for more details on how to use data binding.

3.3.7. Toolbars

Most Windows applications offer toolbars as well as menus. Toolbars provide faster access for frequently used operations, because the user does not need to navigate through the menu systemthe toolbar is always visible onscreen, as shown in Figure 3-21.

Figure 3-21. Application with menu and toolbar (Color Plate 7)


WPF supports toolbars through the ToolBarTray and ToolBar controls. ToolBarTray provides a container into which you can add multiple ToolBar elements. Example 3-28 shows a simple example, which provides the markup for Figure 3-21.

Example 3-28. ToolBarTray and ToolBar
<ToolBarTray Grid.Row="1" Margin="2">
    <ToolBar>
        <Button>
            <Canvas Width="16" Height="16">
                <Polygon Stroke="Black" StrokeThickness="0.5"
                         Fill="LinearGradient 1,1 0.2,0.7 #AAA White"
                         Points="3,1 10,1 13,5 13,15 3,15" />

                <Polygon Stroke="Black" Fill="DarkGray" StrokeThickness="0.5"
                         StrokeLineJoin="Bevel" Points="10,1 10,5 13,5" />
            </Canvas>
        </Button>

        <Button>
            <Canvas Width="16" Height="16">
                <Polygon Stroke="Black" StrokeThickness="0.5"
                         Fill="VerticalGradient Khaki Beige"
                         Points="1,15 1,4 5.5,4 7.5,6 12,6 12,15" />

                <Polygon Stroke="Black" Fill="VerticalGradient Khaki #DB7"
                         StrokeThickness="0.5"
                         Points="1.5,15 4,8 15,8 12,15" />
                <Path    Stroke="Blue" StrokeThickness="1"
                         Data="M 8,2 C 9,1 12,1 14,3"  />
                <Polygon Fill="Blue" Points="15,1 15.5,4.5 12,4" />
            </Canvas>
        </Button>
    </ToolBar>

  </ToolBarTray>

This creates just one toolbar, with a couple of buttons. When you add a button to a toolbar, its size defaults to 17 x 17 logical pixels. If the content is too large, it will be cropped. This is because toolbar buttons are usually expected to be images rather than text. The toolbar sets all the buttons to the same size to ensure consistent spacing. If you provide content that is smaller than 17 x 17, it will be centered. (If you want a larger button, you can set the size explicitly; 17 x 17 is simply the default.)

In this example, we have used some simple vector graphics to draw the usual New and Open... icons. The graphical elements used are explained in more detail in Chapter 7. In practice, you would rarely put graphics inline like thisyou would usually expect drawings to be resources that are simply referred to by the buttons in the toolbar. See Chapter 6 for more details.


Since toolbar buttons are just normal Button elements with specialized visuals, there is nothing particularly special about their behavior. Toolbars really just provide a particular way of arranging and presenting controls. You can also add other elements to toolbars, such as a TextBox or ComboBox. These will just be arranged on the toolbar along with the buttons.


©2008 FAQ - WPF Labs - Discuss - Terms of Use - Privacy Policy - About WPF
- Interview Questions - Sharepoint Articles - Interview Questions Resource Library - All about LINQ - MS Knowledgebase Articles - Electronics and Hardware discussions