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.
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.)
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.
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.
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-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>
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>
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.
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.
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.)
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.
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.
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.
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>
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.
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.
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.
|