By Chris Sells, Ian Griffiths
Price: $39.95 USD
£28.50 GBP
Cover | Table of Contents | Colophon
// MyApp.cs using System; using System.Windows; // the root WPF namespace namespace MyFirstAvalonApp { class MyApp { [STAThread] static void Main() { // the WPF message box MessageBox.Show("Hello, Avalon"); } } }
STAThread attribute, it's a signal to .NET that when COM is initialized on the application's main thread, to make sure it's initialized to be compatible with single-threaded UI work
, as required by WPF applications.
C:\1st>csc /target:winexe /out:.\1st.exe
/r:System.dll
/r:c:\WINDOWS\Microsoft.NET\Windows\v6.0.4030\WindowsBase
.dll
/r:c:\WINDOWS\Microsoft.NET\Windows\v6.0.4030\PresentationCore
.dll
/r:c:\WINDOWS\Microsoft.NET\Windows\v6.0.4030\PresentationFramework
.dll
MyApp.cs// MyApp.cs using System; using System.Windows; // the root WPF namespace namespace MyFirstAvalonApp { class MyApp { [STAThread] static void Main() { // the WPF message box MessageBox.Show("Hello, Avalon"); } } }
STAThread attribute, it's a signal to .NET that when COM is initialized on the application's main thread, to make sure it's initialized to be compatible with single-threaded UI work
, as required by WPF applications.
C:\1st>csc /target:winexe /out:.\1st.exe
/r:System.dll
/r:c:\WINDOWS\Microsoft.NET\Windows\v6.0.4030\WindowsBase
.dll
/r:c:\WINDOWS\Microsoft.NET\Windows\v6.0.4030\PresentationCore
.dll
/r:c:\WINDOWS\Microsoft.NET\Windows\v6.0.4030\PresentationFramework
.dll
MyApp.cs
Microsoft (R) Visual C# 2005 Compiler version 8.00.50215.44
for Microsoft (R) Windows (R) 2005 Framework version 2.0.50215
Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.
WindowsBase, PresentationCore and PresentationFramework), along with the core .NET System assembly, and compiling the MyApp.cs source file.
NavigationApplication in Example 1-14 to serve as the base of your custom application class instead of the Application class.// MyApp.xaml.cs using System; using System.Windows; using System.Windows.Navigation; namespace MyNavApp { public partial class MyApp : NavigationApplication {} }
NavigationApplication itself derives from the Application class and provides additional services such as navigation, history, and tracking the initial page to show when the application first starts, which is specified in the application's XAML file, as in Example 1-15.
<!-- MyApp.xaml.cs -->
<NavigationApplication
x:Class="MyNavApp.MyApp"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Button
is a control, providing content and behavior and a Window
is a container. There are two things that may surprise you about content containment in WPF, however.
<Window ...>
<Button Width="100" Height="100">Hi</Button>
</Window>
<Window ...>
<Button Width="100" Height="100">
<Image Source="tom.png" />
</Button>
</Window>
TextBox, as shown in Figure 1-11 and implemented in Example 1-21.
<Window ...>
<Button Width="100" Height="100">
<TextBox Width="75">edit me</TextBox>
</Button>
</Window>
TextBlock and the Image as content for the Button, we don't really have enough information to place them inside the area of the button. Should they be stacked left-to-right or top-to-bottom? Should one be docked on one edge and one docked to the other? How are things stretched or arranged if the button resizes? These are questions best answered with a panel.|
Panel type
|
Usage
|
|---|---|
DockPanel
|
Allocates an entire edge of the panel area to each child; useful for defining the rough layout of simple applications at a coarse scale.
|
StackPanel
|
Lays out children in a vertical or horizontal stack; extremely simple, useful for managing small scale aspects of layout.
|
Grid
|
Arranges children within a grid; useful for aligning items without resorting to fixed sizes and positions. The most powerful of the built-in panels.
|
Canvas
|
Performs no layout logic—puts children where you tell it to; allows you to take complete control of the layout process.
|
TextBox controls, one for the name and one for the nickname; the actual nickname entries in a ListBox in the middle; and a Button to add new entries. The core data of such an application could easily be built with a class, as shown in Example 1-27.
public class Nickname : INotifyPropertyChanged {
// INotifyPropertyChanged Member
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propName) {
if( PropertyChanged != null ) {
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
string name;
public string Name {
get { return name; }
set {
name = value;
OnPropertyChanged("Name"); // notify consumers
}
}
private string nick;
public string Nick {
get { return nick; }
set {
nick = value;
OnPropertyChanged("Nick"); // notify consumers
}
}
public Nickname() : this("name", "nick") { }
public Nickname(string name, string nick) {
this.name = name;
this.nick = nick;
}
}
INotifyPropertyChanged interface to let consumers of this data know when it has changed.Nickname object made its data available via standard .NET properties, we need something special to support data binding on the target element. While the TextContent property of the TextBlock element is exposed with a standard property wrapper, for it to integrate with WPF services such as data binding, styling and animation, it also needs to be a dependency property
. A dependency property provides several features not present in .NET properties, including the ability to inherit its value from a container element, support externally set defaults, provide for object-independent storage (providing a potentially huge memory savings), and change tracking.Nickname objects in XAML in Example 1-32.
<!-- Window1.xaml -->
<?Mapping XmlNamespace="local" ClrNamespace="DataBindingDemo" ?>
<Window
x:Class="DataBindingDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
xmlns:local="local"
Text="Nicknames">
<Window.Resources>
<local:Nicknames x:Key="names">
<local:Nickname Name="Don" Nick="Naked" />
<local:Nickname Name="Martin" Nick="Gudge" />
<local:Nickname Name="Tim" Nick="Stinky" />
</local:Nicknames>
</Window.Resources>
<DockPanel DataContext="{StaticResource names}">
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center">Name: </TextBlock>
<TextBox Text="{Binding Path=Name}" />
<TextBlock VerticalAlignment="Center">Nick: </TextBlock>
<TextBox Text="{Binding Path=Nick}" />
</StackPanel>
...
</DockPanel>
</Window>
Window.Resources, which is property-element syntax to set the Resources property of the Window1 class. Here, we can add as many named objects as we like, with the name coming from the Key attribute and the object coming from the XAML elements (remember that XAML elements are just a mapping to .NET class names). In this example, we're creating a Nicknames collection named names to hold three Nickname objects, each constructed with the default constructor, and then setting each of the Name and Nick properties.TextBlock controls from our Nickname sample, each of which was set to the same VerticalAlignment (see Example 1-35).
<!-- Window1.xaml -->
<Window ...>
<DockPanel ...>
<StackPanel ...>
<TextBlock VerticalAlignment="Center">Name: </TextBlock>
<TextBox Text="{Binding Path=Name}" />
<TextBlock VerticalAlignment="Center">Nick: </TextBlock>
<TextBox Text="{Binding Path=Nick}" />
</StackPanel>
...
</DockPanel>
</Window>
VerticalAlignment setting into a style, we could do this with a Style element in a Resources block, as shown in Example 1-36.
<Window ...>
<Window.Resources>
<Style x:Key="myStyle" TargetType="{x:Type TextBlock}">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="FontStyle" Value="Italic" />
</Style>
</Window.Resources>
<DockPanel ...>
<StackPanel ...>
<TextBlock Style="{StaticResource myStyle}">Name: </TextBlock>
<TextBox Text="{Binding Path=Name}" />
<TextBlock Style="{StaticResource myStyle}">Nick: </TextBlock>
<TextBox Text="{Binding Path=Nick}" />
</StackPanel>
...
</DockPanel>
</Window>
Setter elements for a specific class (specified with the Type markup extension). The TextBlock myStyle style centers the vertical alignment property and, just for fun, sets the text to bold italic as well. With the style in place, it can be used to set the Style property of any TextBlock that references the style resource. Applying this style as in Example 1-36 yields Figure 1-18.<Button LayoutTransform="scale 3 3"> <StackPanel Orientation="Horizontal"> <Canvas Width="20" Height="18" VerticalAlignment="Center"> <Ellipse Canvas.Left="1" Canvas.Top="1" Width="16" Height="16" Fill="Yellow" Stroke="Black" /> <Ellipse Canvas.Left="4.5" Canvas.Top="5" Width="2.5" Height="3" Fill="Black" /> <Ellipse Canvas.Left="11" Canvas.Top="5" Width="2.5" Height="3" Fill="Black" /> <Path Data="M 5,10 A 3,3 0 0 0 13,10" Stroke="Black" /> </Canvas> <TextBlock VerticalAlignment="Center">Click!</TextBlock> </StackPanel> </Button>
LayoutTransform property on the button, produces Figure 1-21.
Window or Page objects you may have, are most often split between a declarative XAML file for the look and an imperative code file for the behavior. Your applications can be normal, like a standard Windows application, or navigation-based, like the browser. In fact, the latter can be integrated into the browser, and both can be deployed and kept up to date over the Web using ClickOnce.|
Panel type
|
Usage
|
|---|
|
Panel type
|
Usage
|
|---|---|
DockPanel
|
Allocates an entire edge of the panel area to each child; useful for defining the rough layout of simple applications at a coarse scale.
|
StackPanel
|
Lays out children in a vertical or horizontal stack; extremely simple, useful for managing small scale aspects of layout.
|
Grid
|
Arranges children within a grid; useful for aligning items without resorting to fixed sizes and positions. The most powerful of the built-in panels.
|
Canvas
|
Performs no layout logic—puts children where you tell it to; allows you to take complete control of the layout process.
|
DockPanel is useful for describing the overall layout of a simple user interface. You can carve up the basic structure of your window using a DockPanel and then use the other panels to manage the details.DockPanel arranges each child element so that it fills a particular edge of the panel. If multiple children are docked to the same edge, they simply stack up against that edge in order. You may also optionally specify that the final child fills any remaining space not occupied by controls docked to the panel's edges. Although this may sound like a small feature set, the DockPanel provides a surprisingly flexible way of laying out elements, particularly as DockPanels can be nested.DockPanel-based layout. Five buttons have been added to illustrate each of the options. Notice that four of them have a DockPanel.Dock attribute applied. This attached property is defined by DockPanel to allow elements inside a DockPanel to specify their position.
<DockPanel LastChildFill="True">
<Button DockPanel.Dock="Top">Top</Button>
<Button DockPanel.Dock="Bottom">Bottom</Button>
<Button DockPanel.Dock="Left">Left</Button>
<Button DockPanel.Dock="Right">Right</Button>
<Button>Fill</Button>
</DockPanel>
StackPanel
is a very simple panel. It simply arranges its children in a row or a column. We've seen it once already in Example 2-1, but that was a somewhat unrealistic example. You will rarely use StackPanel to lay out your whole user interface. It is at its most useful for small-scale layout—you use DockPanel or Grid to define the overall structure of your user interface, and then StackPanel to manage the details.DockPanel in Example 2-4 for the basic layout of our documentation viewer. We will now use StackPanel to arrange the contents of the search panel on the left-hand side. The markup in Example 2-5 replaces the placeholder TextBlock that contained the text "Search panel goes here."
<StackPanel DockPanel.Dock="Left" Orientation="Vertical"
Background="#ECE9D8">
<TextBlock>Look for:</TextBlock>
<ComboBox />
<TextBlock>Filtered by:</TextBlock>
<ComboBox />
<Button>Search</Button>
<CheckBox>Search in titles only</CheckBox>
<CheckBox>Match related words</CheckBox>
<CheckBox>Search in previous results</CheckBox>
<CheckBox>Highlight search hits (in topics)</CheckBox>
</StackPanel>
Margin property, which is present on all WPF elements. It indicates the amount of space that should be left around the edges of the element when it is laid out. The
StackPanel is difficult, because it is not designed with two-dimensional alignment in mind. We could try to use nesting: Example 2-7 shows a vertical StackPanel with three rows, each with a horizontal StackPanel.
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock>Protocol:</TextBlock>
<TextBlock>Unknown Protocol</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock>Type:</TextBlock>
<TextBlock>Not available</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock>Connection:</TextBlock>
<TextBlock>Not encrypted</TextBlock>
</StackPanel>
</StackPanel>
Grid panel solves this problem. Rather than working a single row or a single column at a time, it aligns all elements into a grid that covers the whole area of the panel. This allows consistent positioning from one row to the next. Example 2-8 shows the same elements as Example 2-7, but arranged with a Grid rather than StackPanels.DockPanel, StackPanel, or Grid will not enable the look you require, and it will be necessary to take complete control of the precise positioning of every element. For example, when you want to build an image out of graphical elements the positioning of the elements is dictated by the picture you are creating, not by any set of automated layout rules. For these scenarios, you will want to use the Canvas.Canvas is the simplest of the panels. It allows the location of child elements to be specified precisely relative to the edges of the canvas. The Canvas doesn't really do any layout at all—it simply puts things where you tell it to.Canvas will seem familiar and natural. However, it is strongly recommended that you avoid it unless you really need this absolute control. The automatic layout provided by the other panels will make your life very much easier, because they can adapt to changes in text and font. They also make it far simpler to produce resizable user interfaces. Moreover, localization tends to be much easier with resizable user interfaces, because different languages tend to produce strings with substantially different lengths. Don't opt for the Canvas simply because it seems familiar.Canvas, you must specify the location of each child element. If you don't, all of your elements will end up at the top left-hand corner. Canvas defines four attached properties for setting the position of child elements. Vertical position is set with either the Top or Bottom property, and horizontal position is determined by either the Left or Right property, an approach that is illustrated in Example 2-16.<Canvas Background="Yellow"> <TextBlock Canvas.Left="10" Canvas.Top="20">Hello</TextBlock> <TextBlock Canvas.Right="10" Canvas.Bottom="20">world!</TextBlock> </Canvas>
Viewbox element automatically scales its content to fill the space available. Viewbox is not strictly speaking a panel—it derives from Decorator. This means that, unlike most panels, it can only have one child. However, its ability to adjust the size of its content in order to adapt to its surroundings make it a useful layout tool.Viewbox but probably should. The window's content is a Canvas containing a rather small drawing. The markup is shown in Example 2-17.
<Window xmlns="http://schemas.microsoft.com/winfx/avalon/2005">
<Canvas Width="18" Height="18" VerticalAlignment="Center">
<Ellipse Canvas.Left="1" Canvas.Top="1" Width="16" Height="16"
Fill="Yellow" Stroke="Black" />
<Ellipse Canvas.Left="4.5" Canvas.Top="5" Width="2.5" Height="3"
Fill="Black" />
<Ellipse Canvas.Left="11" Canvas.Top="5" Width="2.5" Height="3"
Fill="Black" />
<Path Data="M 5,10 A 3,3 90 0 0 13,10" Stroke="Black" />
</Canvas>
</Window>
Viewbox to resize the content automatically. The Viewbox will expand it to be large enough to fill the space, as shown in Figure 2-25. (If you're wondering why the drawing doesn't touch the edges of the window, it's because the Canvas is slightly larger than the drawing it contains.)
Canvas element in a Viewbox element, as is done in Example 2-18.
<Window xmlns="http://schemas.microsoft.com/winfx/avalon/2005">
<Viewbox>
<Canvas Width="20" Height="18" VerticalAlignment="Center">
...as before
</Canvas>
</Viewbox>
</Window>
TextBlock, which is efficient but has limited functionality. TextFlow offers more advanced typography and layout functionality but with slightly more overhead.TextBlock is useful for putting short blocks of text into a user interface. If you don't require any special formatting of the text, you can simply wrap your text in a TextBlock:<TextBlock>Hello, world!</TextBlock>
TextBlock is the simplest text-handling element, it is capable of a little more than this example shows. For example, it has a set of properties for controlling the font, shown in Table 2-2.|
Property
|
Usage
|
|---|---|
FontFamily
|
Typeface name; e.g., "Times New Roman" or "Lucida Console"
|
FontSize
|
Size of the font
|
FontStretch
|
Used to indicate condensed or expanded versions of a typeface, where available
|
FontStyle
|
Normal, Oblique, or Italic
|
FontWeight
|
FrameworkElement base class. We have seen a few of these in passing in the preceding section, but we will now look at them all in a little more detail.Width or Height, the layout system will always attempt to honor your choices. Of course if you make an element wider than the screen, WPF can't make the screen any wider, but as long as what you request is possible, it will be done.Width and Height where possible. By specifying upper and lower limits, you can still allow WPF some latitude to automate the layout.MinWidth of 10000, WPF won't be able to honor that request unless you have some very exotic display hardware. In these cases, your element will be truncated to fit the space available.StackPanel will be as wide as the widest element, meaning that any narrower elements are given excess space. A DockPanel will provide enough space for an element to fill an edge or all the remaining space. Alignment is for these sorts of scenarios, enabling you to determine what the child element does with the extra space.StackPanel with a Height of 100, which contains a Button with a Height of 195.
<StackPanel Height="100" Background="Yellow" Orientation="Horizontal">
<Button>Foo</Button>
<Button Height="30">Bar</Button>
<Button Height="195">Spong</Button>
</StackPanel>
StackPanel
has dealt with the anomaly by truncating the element that was too large. When confronted with contradictory hardcoded sizes like these, most panels take a similar approach and will crop content where it simply cannot fit.TextBlock and its content into a StackPanel, a DockPanel, and a Grid cell.
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Height="100" Background="Yellow" Orientation="Horizontal">
<TextBlock TextWrap="Wrap" FontSize="20">
This is some text that is too long to fit.
</TextBlock>
</StackPanel>
<DockPanel Grid.Row="1" Height="100" Background="Yellow" LastChildFill="False">
<TextBlock TextWrap="Wrap" FontSize="20">
This is some text that is too long to fit.
</TextBlock>
</DockPanel>
<TextBlock Grid.Row="2" TextWrap="Wrap" FontSize="20">
This is some text that is too long to fit.
</TextBlock>
</Grid>
ScrollViewer
.) Moreover, even when there is enough space onscreen, your panel's parent could still choose not to give it to you. For example, if your custom panel is nested inside a Grid, the Grid may well have been set up with a hardcoded width for the column your panel occupies, in which case that's the width you'll get regardless of what you asked for during the measure phase.