Fosler J., Boutler M. - WPF For Those Who Know Windows Forms
Fosler J., Boutler M. - WPF For Those Who Know Windows Forms
It is possible to write “Avalon” to the bare metal using code (C#, VB, or your favorite CLS-
Compliant-Language of choice), however the designers of the Avalon libraries have optimized the
“feel” of their APIs for XAML – sometimes this is just shortened to the word “markup”. Charles
Petzold has an interesting discussion here about when to use what.
What is xmlns?
Definition from W3C:
XML namespaces provide a simple method for qualifying element and attribute names
used in Extensible Markup Language documents by associating them with namespaces
identified by URI references.
By bringing in custom xml namespaces, we essentially bring in another set of “valid” XML
element types and attribute names.
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
This pulls in the XML schema for the Windows Presentation Foundation itself. By setting
this as the default XML namespace, we can just create <Button> without prepending it
with anything silly like <wpf:Button>.
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
This brings in XAML keywords, markup extensions (the funny stuff you’ll sometimes see
in curly braces). Interesting x: stuff includes
“x:Key” for setting keys in a resource dictionary
“x:Static” for fetching statics off a CLR class
“x:Class” for associating a codebehind
“x:Type” for specifying a type
“x:Null” for setting something to null in XAML
x:Class="WindowsApplication1.Window1"
This helps the XAML compiler to find the associated code-behind – in this case it would
look for WindowsApplication1.Window1 as its codebehind file.
Title="Main Window"
Finally an easy one: this one sets the title for the window to say “Main Window”.
// Window1.xaml.cs
namespace WindowsApplication1 {
public class MyButton : System.Windows.Controls.Button {
}
}
Note that the use of “local” is a matter of choice – it’s what you’ll use to prefix your elements
from that namespace. We could have replaced all the “local” strings with “foo” or
“WindowsApplication1” and it would have worked.
If you name your elements in your XAML file, you can access them as if they were member
variables on the class.
From code:
// Window1.xaml.cs
textBox1.Text = "Hello World!";
You can either write this code in the constructor, or you can use the “Loaded” event instead.
Title="WindowsApplication14"
Loaded="OnWindowLoaded"
>
<Canvas>
<TextBox Name="textBox1"></TextBox>
</Canvas>
</Window>
// Window1.xaml.cs
private void OnWindowLoaded(object sender, RoutedEventArgs e) {
textBox1.Text = "Hello World!";
}
Option 2: Hooking onto the Loaded event from code
public Window1() {
InitializeComponent();
this.Loaded += new RoutedEventHandler(OnWindowLoaded);
}
“Avalon” has three kinds of application styles – a simple Application (Window-based, just like
windows forms), a NavigationApplication (page based, just like Microsoft Money or a
website), and a DocumentApplication (specialized sub-set of NavigationApplication for
displaying content).
An “Avalon” application can either be run in two modes: Express (run from browser) and
Installed (behaves like classic Windows applications). Just like Windows Forms, “Avalon” works
with ClickOnce for installation.
If you create a new “Windows Application (WPF)” in Visual Studio, you’ll get several files:
MyApp.xaml
o Application declaration in xaml
MyApp.xaml.cs or MyApp.xaml.vb, etc
o Application code behind – application startup events and so on
Window1.xaml
o Window declaration in xaml
Window1.xaml.cs or Window1.xaml.vb, etc
o Window code behind – event handlers and so on
MyApp.xaml and its codebehind file are most like the Program.cs from a C# Windows Forms
application. This is where you’ll put anything that applies to the entire application – typically you
may want to associate resources, styles to be shared between all your Windows in your
application.
Where is Application.Run?
Believe it or not, “Avalon” has an Application.Run too – to find it we need to talk about what
happens when you build XAML.
When you build Avalon takes the .xaml and generates a set of files that are then compiled in to
the exe. For <Name>.xaml the following are generated:
<Name>.baml
Binary version of the .xaml file. Compiled into the .exe as a resource
So the build is a two step process – firstly it generates this stuff, then it compiles and generates
an exe.
Peeking at MyApp.g.cs
[STAThread()]
public static int Main(string[] args) {
WindowsApplication22.MyApp app = new
WindowsApplication22.MyApp();
app.InitializeComponent();
return app.Run(args);
}
</Application.Resources>
</Application>
// MyApp.xaml.cs
public partial class MyApp : Application {
private void OnApplicationStartingUp(object sender,
StartupEventArgs e) {
if (e.Args.Length == 1 && e.Args[0] == "/?") {
MessageBox.Show("Help!");
}
}
}
Form-ing Windows and looking at Controls
We’ll start with a brief discussion of Forms and Windows, namespaces, then dive into Controls,
the Control hierarchy and a mapping of the Windows Forms controls to the new WPF ones.
MDI
According to Nick Kramer, PM on the WPF team, version 1 of “Avalon” will unfortunately not
include support for MDI. If you want to use MDI with Avalon, you can use use interop to display
WPF controls inside a Windows Forms application. His blog has some sample code to show how
to achieve this.
Additionally, you may consider structuring your program to use a NavigationWindow instead –
which is a built-in way of building inductive UIs.
The Windows Presentation Foundation lives in several namespaces, all starting for the most part
with “System.Windows” but never including the word “Forms”. PresentationCore (which includes
primitives such as UIElement and drawing constructs in System.Windows.Media*),
PresentationFramework (which includes most the standard controls), UIAutomationProvider,
UIAutomationTypes, and WindowBase(which includes the Dispatcher and supporting types) are
all stock references included in a typical application.
Controls
Controls in “Avalon” work quite a bit differently than
Controls in Windows Forms.
ItemsControls include most of the controls we think of as containing items. (ListBox, ListView,
TreeView). This category is further classified into Selector controls – that is – controls whose
purpose it is in life to make a singular choice out of a list of options. The best example of a
selector is a ComboBox.
1[1]
Note that Window is a ContentElement, which is why the auto-generated code puts some sort
of layout panel as the direct child. E.g. A window could not directly contain two buttons.
What to pick as your base class?
Do I need a control?
Control supports ControlTemplates. If you’re a control vendor you’ll want to derive from some
sort of control. Design your functionality so that you can databind to properties rather than
depending on particular visuals to be present. In order to create properties that work with
templates and triggers, create DependencyProperties.
DispatcherObject – this base class specifies a that the object is not inherently thread safe – it
wants to be accessed on the same thread as its corresponding Dispatcher.
The “Dispatcher” is the equivalent of the “Message Pump” in User32 applications. As events
occur, the “Dispatcher” keeps track of all the things that need to be processed and organizes how
all the events get dispatched out to the relevant dispatcher objects.
DependencyObject – objects of this type know how to support a special kind of property called
a DependencyProperty. DependencyObjects can fish the values for DependencyProperties from
“Avalon”’s underlying propertystore using the GetValue and SetValue methods.
Visual – As the name suggests, “visual” elements have “visual” representations. This class adds
basic mouse hit testing features basic transformation features, and the ability to add “visual”
children.
FrameworkElement – This class adds the ability to Style, bind to dependency properties, fish
into and apply resources from the Resource dictionary, have a notion of size (Height, Width etc),
show ContextMenus and supports ToolTip.
UIElement – This class adds quite a bit of the features that are similar to
System.Windows.Forms.Control: drag/drop, mouse, keyboard, focus.
Control – Adds the ability to control the layout of child elements via ArrangeOverride and
MeasureOverride. Font, Padding, Borders, Background information and TabStop also appear at
this level in the object hierarchy.
Most importantly: Adds the Template property, which allows you to dynamically replace the visual
elements that define the look and feel of the control.
ContentControl – Derives from Control. ContentControls only support one child element – but
this a control which supports adding more controls.
Window – Derives from ContentControl. This is the analog of “Form” in WPF. Because this is a
ContentControl, it can only hold one element – e.g. adding two buttons as direct children to a
Window causes an exception when compiling. This is why the designer adds a layout panel
(typically a Grid) as the immediate child of a window.
ItemsControl - Derives from Control. ItemsControls can support more than one child element.
ItemsControl supports grouping, databinding to a list, and replacing the layout by swapping
in/out the ItemsPanel.
Child Controls/Elements
We talked briefly about ItemsControls being able to hold more than one child control and
ContentControls being able to hold one child element.
In Windows Forms, Controls can be added to Controls using the Control collection on Control. In
other words – you could add a Button to a Panel.Controls and the Panel to a Form.Controls
collection.
// Windows Forms
form.Controls.Add(panel);
panel.Controls.Add(button1);
Form
Panel
Button1
Button2
Panel
Button3
If you notice, the content you can put inside a <Panel> tag is different than what you can put
inside a <Button> tag. If you were to write this in code you would say:
// “Avalon”
panel.Children.Add(button1);
button1.Content = “Button1”;
Obviously, there’s some magic glue that occurs to interpret the content of the panel differently
than the content of the Button. This happens to be an attribute specified on the top of the class
called “ContentPropertyAttribute” that says what property the Content of the XAML tag should be
represented as.
In other words
<Panel>
<Button>Button1</Button>
</Panel>
If you look at the Button.Content property (which comes from the ContentControl class), you can
see that any object can be assigned to it. In our simple case we’ve just assigned a string, but
you could put anything in.
Or create it in XAML – note how the properties can be set in XAML. For this to work, the Book
class has to have an empty constructor. 2[2]
<Button>
<local:Book Title="Grapes of Wrath" Author="Steinbeck"/>
</Button>
If you don’t want to override ToString(), then you can create a DataTemplate to represent the
Book. DataTemplates are a way of saying “Any time you see an element of type “Book” in the
XAML, represent it with the following UI…” For more information, see the “Data” section.
2[2]
Use the “Mapping” attribute to make “local:Book” a valid XAML tag. See “XAML you gotta
know” section for syntax.
What the ContentProperty maps to
Using the power of reflection, we can inspect what the ContentProperty is on some of the
FrameworkElements:
Visibility=“Hidden” may not do what you expect – “Hidden” implies that the element should not
render but should continue to occupy the same space it would if it is “Visible”.
<DockPanel>
<Button DockPanel.Dock="Top">
Top1
</Button>
<Button Visibility="Hidden"
DockPanel.Dock="Top">
Top2
</Button>
<Button>Fill</Button>
</DockPanel>
As you can see there is a white gap where space for “Top2” button is reserved. If we switch to
Visibility = “Collapsed” the space is no longer reserved for “Top2”.
Visibility = “Hidden” can be handy for situations where you want to save space for an element
that may occasionally appear (say an error icon after a text box, etc).
<DockPanel>
<Button DockPanel.Dock="Top">
Top1
</Button>
<Button Name="Foo"
Visibility="Collapsed" DockPanel.Dock="Top">
Top2
</Button>
<Button>Fill</Button>
</DockPanel>
Dialog Boxes
<Window x:Class="WindowsApplication14.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowsApplication14"
>
<Canvas>
<Button Click="Button_Click">Ok</Button>
</Canvas>
</Window>
<!--Window1.XAML-->
<Button Click="OnButtonClicked">Click me!
</Button>
// Window1.xaml.cs
private void OnButtonClicked(object sender,
EventArgs e){
MessageBox.Show("Clicked!");
}
CheckBox System.Windows.Controls.CheckBox
<!--Window1.XAML-->
<CheckBox IsChecked="True"
Checked="OnCheckedChanged" >Use this
option</CheckBox>
Windows Windows Presentation Foundation
Forms
// Window1.xaml.cs
private void OnCheckedChanged(object sender,
EventArgs e){
MessageBox.Show("CheckedChanged!");
}
Notes: ContentControl.
CheckedLis System.Windows.Controls.ListBox
tBox
Notes: ItemsControl. Use a DataTemplate to add a checkbox
to the ListBoxItems.
<UserControl
x:Class="WindowsApplication10.CheckedListBox"
xmlns="http://schemas.microsoft.com/winfx/
2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/
2006/xaml"
>
<UserControl.Resources>
<DataTemplate x:Key="checkedListBox">
<CheckBox IsChecked="{Binding
RelativeSource=/TemplatedParent/TemplatedParent
, Path=IsSelected, Mode=TwoWay}">
<ContentPresenter
Content="{TemplateBinding
Property=Content}"></ContentPresenter>
</CheckBox>
</DataTemplate>
</UserControl.Resources>
<UserControl.FixedTemplate>
<ListBox ItemTemplate="{StaticResource
checkedListBox}" ItemsSource="{Binding}"/>
</UserControl.FixedTemplate>
</UserControl>
ComboBox System.Windows.Controls.ComboBox
<Window x:Class="WindowsApplication10.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/
Windows Windows Presentation Foundation
Forms
xaml/presentation"
xmlns:x=http://schemas.microsoft.com/winfx/
2006/xaml
Title="WindowsApplication10">
<Grid Name="someGrid">
<ComboBox>
<ComboBoxItem>One</ComboBoxItem>
<ComboBoxItem>Two</ComboBoxItem>
<ComboBoxItem>Three</ComboBoxItem>
<ComboBoxItem>Four</ComboBoxItem>
</ComboBox>
</Grid>
</Window>
<Window x:Class="WindowsApplication10.Window1"
xmlns="http://schemas.microsoft.com/winfx/
2006/xaml/presentation"
Windows Windows Presentation Foundation
Forms
xmlns:x="http://schemas.microsoft.com/winfx/
2006/xaml"
Title="WindowsApplication10"
xmlns:wf="clr-
namespace:System.Windows.Forms;assembly=System.
Windows.Forms"
>
<Grid>
<WindowsFormsHost>
<wf:DateTimePicker></wf:DateTimePicker>
</WindowsFormsHost>
</Grid>
</Window>
<TextBlock>
This is a <Hyperlink>hyperlink!
</Hyperlink> You can also now do
<Bold>bold</Bold> and <Italic>italic</Italic>
inline as well!
</TextBlock>
ListBox System.Windows.Controls.ListBox
<ListBox>
<ListBoxItem>One</ListBoxItem>
<ListBoxItem>Two</ListBoxItem>
<ListBoxItem>Three</ListBoxItem>
Windows Windows Presentation Foundation
Forms
</ListBox>
ListView System.Windows.Controls.ListView
3[3]
For more information see section on Child Controls/Elements.
Windows Windows Presentation Foundation
Forms
<ListView>
<ListViewItem>ListViewItem1</ListViewItem>
<ListViewItem>ListViewItem2</ListViewItem>
<ListViewItem>ListViewItem3</ListViewItem>
<ListViewItem>ListViewItem4</ListViewItem>
</ListView>
<Window
xmlns="http://schemas.microsoft.com/winfx/
2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/200
6/xaml"
x:Class="WindowsApplication10.Window1">
<Window.Resources>
<XmlDataProvider x:Key="EmployeeData"
XPath="/EmployeeData">
<EmployeeData xmlns="">
<Employee>
<FirstName>Jesper</FirstName>
<LastName>Aaberg</LastName>
<FavoritePet>Fish</FavoritePet>
</Employee>
<Employee>
<FirstName>Dominik</FirstName>
<LastName>Pahiha</LastName>
<FavoritePet>Cat</FavoritePet>
</Employee>
</EmployeeData>
</XmlDataProvider>
</Window.Resources>
Windows Windows Presentation Foundation
Forms
<StackPanel>
<ListView ItemsSource="{Binding
Source={StaticResource EmployeeData},
XPath=Employee}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0,20,0,0">
<ListView.View>
<GridView>
<GridViewColumn Header="First Name"
DisplayMemberPath="{Binding
XPath=FirstName}" />
<GridViewColumn Header="Last Name"
DisplayMemberPath="{Binding
XPath=FavoritePet}"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</Window>
MaskedTex System.Windows.Controls.TextBox
tBox
Notes: No direct equivalent in WPF. You may be able to
validate input using the
System.ComponentModel.MaskedTextProvider. (I’ve not tried
this). Add references to WindowsFormsIntegration.dll from
program files\reference assemblies and System.Windows.Forms
from the .net tab.
<Window x:Class="WindowsApplication10.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/
xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/200
6/xaml"
Title="WindowsApplication10"
xmlns:wf="clr-
namespace:System.Windows.Forms;assembly=System.
Windows.Forms"
>
<Grid>
<WindowsFormsHost>
<wf:MaskedTextBox></wf:MaskedTextBox>
Windows Windows Presentation Foundation
Forms
</WindowsFormsHost>
</Grid>
</Window>
MonthCale Notes: No equivalent in WPF. Interop with Windows Forms.
ndar Add references to WindowsFormsIntegration.dll from program
files\reference assemblies and System.Windows.Forms from
the .net tab.
<Window x:Class="WindowsApplication10.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/
xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/200
6/xaml"
Title="WindowsApplication10"
xmlns:wf="clr-
namespace:System.Windows.Forms;assembly=System.
Windows.Forms"
>
<Grid>
<WindowsFormsHost>
<wf:MonthCalendar></wf:MonthCalendar>
</WindowsFormsHost>
</Grid>
</Window>
<ProgressBar
Value="50"/>
Windows Windows Presentation Foundation
Forms
RadioButto System.Windows.Controls.RadioButton
n
Notes: ToggleButton.
<StackPanel>
<RadioButton>One</RadioButton>
<RadioButton>Two</RadioButton>
<RadioButton>Three</RadioButton>
</StackPanel>
That said it’s more difficult to work with. To load in text from a
file:
RichTextBox rtb = new RichTextBox();
string text = File.OpenText(@"c:\temp\
foo.txt").ReadToEnd();
rtb.Document.Blocks.Clear(); //if you want to
clear before adding text
rtb.Document.Blocks.Add(new Paragraph(new
Windows Windows Presentation Foundation
Forms
Run(text)));
<TextBox>Some text!</TextBox>
Notes: You can directly set the ToolTip property on the control
<Button>
<Button.ToolTip>
Hello World!
</Button.ToolTip>
</Button>
TreeView System.Windows.Controls.TreeView
<TreeView>
<TreeViewItem Header="1">
<TreeViewItem Header
="1.1"/>
<TreeViewItem Header
="1.2"/>
</TreeViewItem>
<TreeViewItem Header="2">
<TreeViewItem Header
="2.1"/>
</TreeViewItem>
</TreeView>
<Window x:Class="WindowsApplication10.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/
xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/200
6/xaml"
Title="WindowsApplication10"
xmlns:wf="clr-
namespace:System.Windows.Forms;assembly=System.
Windows.Forms"
Height="378" Width="392">
<Grid>
<WindowsFormsHost>
<wf:WebBrowser
Url="http://www.msn.com"></wf:WebBrowser>
</WindowsFormsHost>
</Grid>
</Window>
Mappings of controls in the “Containers” Toolbox Tab
Windows Forms Windows Presentation Foundation
FlowLayoutPanel System.Windows.Controls.StackPanel, for wrapping scenarios use
System.Windows.Controls.WrapPanel
Notes: HeaderedContentControl
<GroupBox Header="Group Box Title">
</GroupBox>
Panel System.Windows.Controls.Canvas
Notes: See Layout section for samples. There is a “Panel” control in “Avalon”, it
is the base class of all Layout containers – StackPanel, Canvas, DockPanel, Grid,
etc.
SplitContainer Grid with GridSplitter
TabControl System.Windows.Controls.TabControl
<TabControl SelectionChanged="OnTabChanged">
<TabItem Name="tab1" Header="Tab 1 Title" >
<TextBox>TabPage1</TextBox>
</TabItem>
<TabItem Name="tab2" Header="Tab 2 Title">
<TextBox>TabPage2</TextBox>
</TabItem>
</TabControl>
Just as Windows Forms had the need to pre-process input from mouse, keyboard, etc, so does
“Avalon”. “Avalon” has re-used the concept of “tunnel” and “bubble”ed events from DHTML.
Bubbling event – starts at the element that raised the event, then goes up the parent chain.
Tunneling event – starts at the topmost element, then works its way to the element that
started the commotion
RoutedEvents usually come in pairs, e.g. PreviewMouseDown and MouseDown. The “Preview”
events are the “Tunnel” versions, as it allows the parents to preview the event before the child
element. …and necessarily, the follow-on event is the “Bubble” version. An alternate way to
handle events in WPF is through the use of commanding.
For more information on events, handling mouse and TextInput, read chapter 3 of Programming
Windows Presentation Foundation by Chris Sells & Ian Griffiths.
<Menu DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Header = "E_xit" Click="OnExitClicked"/>
</MenuItem>
</Menu>
For a Label, use the Target property to specify the UIElement should activate when the
mnemonic is pressed
<Window x:Class="WindowsApplication10.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowsApplication10"
Width="200"
Height="120"
>
<Grid Margin="9">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
Absolute Positioning
In Windows Forms, you can absolutely position controls by specifying the Location property. This
controls the distance between the parent’s top left corner and the control’s top left corner.
int x = 10;
int y = 20;
button.Location = new Point(x,y);
Canvas
The Canvas control allows you to position a control at a fixed pixel coordinate. The Canvas
provides “Attached Properties” for specifying Canvas.Top, Canvas.Left. As of the December
CTP, it looks like specifying Canvas.Right, Canvas.Bottom does not change the size of the item.
<Canvas>
<Button Canvas.Top="10" Canvas.Left="20"></Button>
</Canvas>
Grid
The Grid control, much like its cousin the System.Windows.Forms.TableLayoutPanel is far more
flexible for situations where the controls are likely to grow and shrink and shift around adjacent
controls. Unlike TableLayoutPanel, the Grid can hold more than one control within a cell.
Like the TableLayoutPanel, you can specify absolute positioning within a cell by using the Margin
property on the element.
<Window ...>
<Grid>
<Button Margin="10,20,0,0"></Button>
</Grid>
</Window>
<Grid>
<Button Margin="10,20,0,0"
VerticalAlignment="Top"
HorizontalAlignment="Left"/>
</Grid>
Relative Positioning
All other forms of anchoring can be achieved using the Grid control and combinations of Margin,
VerticalAlignment, HorizontalAlignment and (if there are multiple rows and columns)
Grid.RowSpan and Grid.ColumnSpan.
Say we want a button to be 100px wide – then as the dialog stretches, the button
should grow too. In Windows Forms, we would set the Location of the button, then
set its anchor to Top | Left | Right.
Win Forms “Avalon” “Avalon” Scenario
Anchors Alignments Margins to set
T L R B Horiz Vert Margin Behavior
Center Center Centered within the parent
T Center Top Top Centered horizontally, a fixed position away from the top
L Left Center Left Centered vertically, aligned to the left
T L Left Top Top, Left Fixed position relative to the upper left hand corner
R Right Center Right Centered vertically, aligned to the right
T R Right Top Top, Right Fixed position relative to the upper right hand corner
L R Stretch Center Left, Right Stretched horizontally, vertically centered
T L R Stretch Top Top, Left, Right Stretched horizontally, a fixed position away from the top
B Center Bottom Bottom Centered horizontally, a fixed position from the bottom
T B Center Stretch Top, Bottom Centered horizontally, stretched vertically
L B Left Bottom Left, Bottom Fixed position relative to the bottom left hand corner
T L B Left Stretch Top, Left, Bottom Fixed position from the left, stretched vertically
R B Right Bottom Right, Bottom Fixed position relative to the bottom right hand corner
T R B Right Stretch Fixed position from the right, stretched vertically
L R B Stretch Bottom Left, Right, Bottom Stretched horizontally, fixed position from the bottom
T L R B Stretch Stretch Top, Left, Right, Bottom Stretched horizontally and vertically
In the “Avalon” Grid, we can achieve this by horizontally stretching the control as if we’re going
to fill up the entire cell, then specifying a margin to say how far from the edge of the cell the
control should be.
For a complete mapping of anchors to the Grid, see the following table:
Dock Layout
In Windows Forms, there are six DockStyles: None, Top, Bottom, Left, Right, Fill. When a control
is Dock’ed to a side, it stretches across the entire length of the side. So a button Dock=Dock.Top
would stretch across the entire width of the Form.
The DockPanel in “Avalon” is pretty similar to the Dock property in Windows Forms. There’s one
catch: DockStyle.Fill and no DockStyle.None work differently.
By default in “Avalon”’s DockPanel, there’s a property called LastChildFill, which is set to true.
This means the last child added to the DockPanel will fill up the remaining space.
The Bottom button, as it was added last, will be treated as if it is set to the old DockStyle.Fill.
<DockPanel LastChildFill="False">
<Button
DockPanel.Dock="Left">Left</Button>
<Button
DockPanel.Dock="Right">Right</Button>
<Button DockPanel.Dock="Top">Top</Button>
<Button DockPanel.Dock="Bottom">Bottom</Button>
</DockPanel>
The Dock panel additionally has the ability to not stretch things
from edge to edge. If you specify the Width or Height or
HorizontalAlignment or VerticalAlignment, then the control will no
longer stretch.
<DockPanel LastChildFill="False">
<Button DockPanel.Dock="Top" Width="80">Top</Button>
<Button DockPanel.Dock="Top" Width="100">Top</Button>
<Button DockPanel.Dock="Top" Width="120">Top</Button>
</DockPanel>
StackPanel / FlowLayoutPanel
StackPanel is quite a bit like FlowLayoutPanel. It uses the Orientation property to specify a
Vertical or Horizontal flow. Unlike the FlowLayoutPanel, the StackPanel stretches the items
(same behavior as the DockPanel). The same strategy of using Width, Height,
HorizontalAlignment and VerticalAlignment will prevent stretching across the StackPanel.
<StackPanel Orientation="Vertical">
<Button Width="100" HorizontalAlignment="Left">Button1</Button>
<Button Width="100" HorizontalAlignment="Left">Button2</Button>
<Button Width="100" HorizontalAlignment="Left">Button3</Button>
<Button Width="100" HorizontalAlignment="Left">Button4</Button>
</StackPanel>
Grid / TableLayoutPanel
We’ve already talked a little bit about the grid – however we haven’t talked about Rows and
Columns. The equivilant of TableLayoutPanel.RowStyles and TableLayoutPanel.ColumnStyles for
the Grid is the RowDefinitions and ColumnDefinitions.
The thing to call out here is the “*”, which is a way of dividing out
remaining space in a table proportionally. If you have two
columns, one set to “2*” and the other to one star or “*”, then
the first column will be 2/3 the space and the second column will
be 1/3 the space.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0">Button1</Button>
<Button Grid.Row="0" Grid.Column="1">Button2</Button>
</Grid>
The Grid has a very helpful debugging feature: ShowGridLines = true. This can be toggled to
show where the Rows and Columns line up.
Windows Forms has the concept of being able to arrange child controls in the OnLayout event or
by plugging in a new LayoutEngine. The equivilant “Avalon” concept is overriding the
“ArrangeOverride” method.
Windows Forms 2.0 added the concept of AutoSize and GetPreferredSize to Control. The
equivalent “Avalon” concept is overriding the “MeasureOverride” method. There are caveats to
overriding this method – if you want a child element to be considered for layout, you must call
“Measure” on it.
<Window x:Class="WindowsApplication14.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowsApplication14"
>
<ScrollViewer>
<Button Height="400">Ok</Button>
</ScrollViewer>
</Window>
<Window x:Class="WindowsApplication14.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowsApplication14"
>
<Viewbox>
<Button Click="Button_Click">Ok</Button>
</Viewbox>
</Window>
Transforms
In WPF, there are two major kinds of transforms – LayoutTransforms and RenderTransforms.
These serve to help scale, rotate your control to your heart’s content. The LayoutTransform
occurs at layout time and impacts layout. The RenderTransform occurs when painting, and
does not impact the layout.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="CiderWindowsApplication132.Window1"
Title="Main Window"
Loaded="Window1_Loaded"
>
<Grid>
<Button Name="button1" Width="75" Height="23"></Button>
</Grid>
</Window>
Let’s create a class called Book4[1], which we’ll use for databinding.
// Windows Forms
TextBox textBox = new TextBox();
this.Controls.Add(textBox);
textBox.DataBindings.Add(textAuthorDataBinding);
4[1]
For brevity, this class does not support change notification – it should technically implement
INotifyPropertyChanged (preferred) or add AuthorChanged and TitleChanged events so that as
the Author and Title properties are changed whatever is bound to these properties are also
updated.
the property to set on the target control: TextBox.Text
the source of data: book
the property to access on the source of data: book.Author
Not so suprisingly, the problem space has not changed – while “Avalon” has a different way of
specifying it, “Avalon” bindings require the same three pieces of information.
In “Avalon”, there are several ways of setting up bindings, but for examples sake lets compare
the syntax of the two:
// Windows Forms
Binding textAuthorDataBinding = new Binding(
"Text", // Name of property on tb to bind to
book, // object to bind to (DataSource)
"Author"); // Name of property on book. (DataMember)
The data context is much like Windows Forms’ DataSource property – it can represent an object
or a list of objects for use in databinding. In “Avalon”, DataContext has a twist: the DataContext
is inherited to all child elements – setting the DataContext on “Window” means that all the
buttons and labels and listboxes can automatically look at the same source of data. A
DataContext can be set on any FrameworkElement or FrameworkContentElement, which will
trump the DataContext set on the parent element.
Any CLR object - One can bind to properties, sub properties as well as indexers on a CLR object.
The binding looks for properties etc using
Reflection - uses reflection and propertyInfo to get values of properties
CustomTypeDescriptor - If the object implements ICustomTypeDescriptor, the bindings
uses this interface to get the values for the properties. This is useful, when the object
does not have a static set of properties, but the properties are determined dynamically.
In order for the data to “refresh” when the property changes, the CLR object should consider
implementing INotifyPropertyChanged (preferred) or adding a matching event for the property:
e.g. for a public string Foo { get; set; } create a public event EventHandler FooChanged;
XmlNode – it is possible to databind to a fragment of XML (XML data island) or an entire XML file
using the XmlDataProvider.
ADO.Net Data Table – it is possible bind to fields on the Row of a DataTable as well as walk
relationships in an ADO.Net DataSet from one table to another. This is done using the
ICustomTypeDescriptor implementation on DataView.
If you want to bind to something other than what’s currently stuffed in the DataContext, you can
set the data source explicitly. This is called an Explicit Data Source, because you’re not using
the inherited stuff from the DataContext.
From MSDN
Here’s a breakdown of the rules for parsing PropertyPath syntax from MSDN:
<Window x:Class="WindowsApplication14.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowsApplication14"
Loaded="OnWindowLoaded"
>
<DockPanel>
<TextBox DockPanel.Dock="Top"
Text="{Binding Path=Author}"/>
<ListBox ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True"/>
</DockPanel>
</Window>
public partial class Window1 : Window {
// Window1.xaml.cs
private void OnWindowLoaded(object
sender, EventArgs e) {
List<Book> books = new List<Book>();
books.Add(new Book("Steinbeck", "Grapes of Wrath"));
books.Add(new Book("Twain", "Huckleberry Finn"));
this.DataContext = books;
}
}
…sets up the object for all the child elements of the window to databind to – in our case, this is a
list of books.
Since we did not specify Source, ElementName, or RelativeSource, the TextBox will look for a
DataContext that is set somewhere either on itself or on one of its parents. It will access the
“Author” property of the currently selected Book in the list of books.
<ListBox ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True"/>
The ItemsSource property specifies the place to populate the listbox items from. Because we
have again not specified Source, ElementName, or RelativeSource, the listbox will go looking for
a set DataContext.
Databinding Walkthroughs
The easiest way to describe databinding is through sample code, here are several “howtos”.
<DockPanel>
<DockPanel.Resources>
<DataTemplate x:Key="BookTemplate" >
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Author}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Path=Title}"/>
</StackPanel>
</DataTemplate>
</DockPanel.Resources>
Foreach Book in the listbox, the DataTemplate will be evaluated. We will generate three text
blocks: one bound to the currentBook.Author property one which just shows a dash “-“ and one
which shows the currentBook.Title.
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Author}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Path=Title}"/>
</StackPanel>
<Window x:Class="WindowsApplication14.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowsApplication14"
Loaded="OnWindowLoaded"
>
<DockPanel>
<DockPanel.Resources>
<DataTemplate x:Key="BookTemplate" >
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Author}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Path=Title}"/>
</StackPanel>
</DataTemplate>
</DockPanel.Resources>
<TextBox DockPanel.Dock="Top" Name="textBox" Text="{Binding
Path=Author}"></TextBox>
<ListBox Name="listBox" ItemTemplate="{StaticResource BookTemplate}"
ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True">
</ListBox>
</DockPanel>
</Window>
- The source of the binding is specified as a UIElement by specifying the element name
- The source of the binding represents the object we want, so Path is not specified,
although we could have written this more explicitly as:
<Label
Target="{Binding ElementName=nameTextBox, Path=.}">
_Name
</Label>
<Window x:Class="WindowsApplication14.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowsApplication14"
xmlns:sys="clr-
namespace:System;assembly=System"
>
<Window.Resources>
<sys:String x:Key="someText">Some
Text</sys:String>
</Window.Resources>
<StackPanel>
<!-- Display the string -->
<TextBox
Text="{Binding Source={StaticResource someText}, Path=.}"/>
<Window x:Class="WindowsApplication14.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowsApplication14"
xmlns:sys="clr-namespace:System;assembly=System"
>
<Window.Resources>
<XmlDataProvider x:Key="booksResource"
XPath="BookList"
Source="Books.xml">
</XmlDataProvider>
</Window.Resources>
<Grid DataContext="{StaticResource booksResource}">
<ListBox ItemsSource="{Binding XPath=Book/Author}"></ListBox>
</Grid>
</Window>
The XmlDataProvider is keyed off the name “booksResource” and using the XPath syntax (this is
an XML standard), the XmlDataProvider represents all the child elements underneath the
“<BooksList>” xml node in the file books.xml.
We can now reference the XmlDataProvider using the StaticResource markup extension, and set
our Grid’s DataContext to represent what the XmlDataProvider is returning back to us – all the
child nodes under BooksList.
Finally the ListBox inherits the DataContext from it’s parent Grid, but tweaks the data it wants to
display – it only wants to display the Authors – so using the XPath syntax (see Appendix B) it
further drills into the XML hierarchy to pull out all the Authors under the book nodes.
<Window x:Class="WindowsApplication14.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowsApplication14"
>
<Window.Resources>
<XmlDataProvider
x:Key="booksResource"
XPath="Books" >
<Books xmlns="">
<Book>
<Title>Grapes of Wrath</Title>
<Author>Steinbeck</Author>
</Book>
<Book>
<Title>Huckleberry Finn</Title>
<Author>Twain</Author>
</Book>
</Books>
</XmlDataProvider>
</Window.Resources>
DataTemplates are a way of generating a certain set of UI to visually represent a piece of data.
In this case, we want to generate two text blocks – one representing the Value of the XML node
and one representing the Name of the XML node. The use of a hierarchical data template means
that whenever a child occurs of the same data type the template is automatically re-applied.
<Window x:Class="WindowsApplication14.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowsApplication14"
xmlns:sys="sys"
xmlns:sysXml="clr-namespace:System.Xml;assembly=System.Xml"
>
<Window.Resources>
<XmlDataProvider x:Key="booksResource"
XPath="Books"
Source="Books.xml">
</XmlDataProvider>
<HierarchicalDataTemplate
x:Key="booksDataTemplate"
DataType='{x:Type
sysXml:XmlNode}'
ItemsSource='{Binding
Path=ChildNodes}'>
<TextBlock Text='{Binding
Path=Value}'>
<TextBlock Text='{Binding Path=Name}' />
</TextBlock>
</HierarchicalDataTemplate>
</Window.Resources>
Advanced Databinding
“Avalon” binding actually occurs via a class called CollectionView. CollectionView is an abstraction
of the DataView class in ADO.Net – it represents a view on top of a collection. CollectionView
supports Sorting, Filtering and Grouping. CollectionView also supports and tracks the “current
item” – it fills the same role as CurrencyManager in Windows Forms.
Resources in Avalon are more than just the “classic” Win32 or Windows Forms resources such as
images, strings, and so on.
Resources can include pretty much any CLR object that can be declared in XAML – that is it must
have a default constructor and properties that are not order dependent.
Styles
Brushes
Templates
DataSources
Use DynamicResources when: the value of the resource could change during the lifetime of the
Application.
Use StaticResources when: it’s clear that you don’t need your resource re-evaluated when
fetching it – static resources perform better than dynamic resources.
Individual resources in the Resources section require a x:Key="someKey" so that the resource
can be looked up using the {StaticResource someKey} or {DynamicResource someKey}
markup extension later.
<Window x:Class="WindowsApplication22.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowsApplication22"
xmlns:sys="clr-namespace:System;assembly=System"
>
<Window.Resources>
<sys:String x:Key="labelText">Hello World</sys:String>
</Window.Resources>
<Grid>
<Label Content="{StaticResource labelText}"/>
</Grid>
</Window>
There is also a notion of application-wide resources; all elements can access elements defined
within the <Application.Resources>, typically defined in the MyApp.xaml file.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<StackPanel.Resources>
<SolidColorBrush x:Key="MyBrush" Color="gold"/>
</StackPanel.Resources>
<TextBlock Foreground="{StaticResource MyBrush}" Text="Text"/>
<Button Background="{StaticResource MyBrush}">Button</Button>
</StackPanel>
</Window>
<StackPanel Name="stackPanel1">
<StackPanel.Resources>
<SolidColorBrush x:Key="MyBrush" Color="gold"/>
</StackPanel.Resources>
<TextBlock Name="textBlock1" Text="Text"/>
<Button Name="button1">Button</Button>
</StackPanel>
</Window>
// Window1.xaml.cs
private void OnMainWindowLoaded(object sender, EventArgs e) {
SolidColorBrush brush = stackPanel1.FindResource("MyBrush")
as SolidColorBrush;
if (brush != null) {
textBlock1.Foreground = brush;
button1.Background = brush;
}
}
Note that FindResource will surf up the element hierarchy to find the resource – this call would
have worked too:
SolidColorBrush brush = textBlock1.FindResource("MyBrush") as
SolidColorBrush;
However, calling this.FindResource (where this is the Window) will fail to find the brush, as the
brush is not defined within the Window’s resource dictionary.
<Grid>
<!—
Searches for the “AquaButton resource
- not defined on Grid
- not defined on Window
- is defined on Application
-->
<Button Style="{StaticResource AquaButton}">I'm Aqua</Button>
</Grid>
</Window>
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="CiderWindowsApplication1.Window1"
Title="Main Window">
<Grid>
<Button Style="{StaticResource AquaButton}">I'm Aqua</Button>
</Grid>
</Window>
Templates – Templates are a way of defining the visual structure of a control. There are really
two kinds of templates: ControlTemplates and DataTemplates
ControlTemplates – When a class derives from the Control class, it gets a Template property.
The Template property is a way of factoring out all the “visual elements” of the control so that
anyone at any time can replace the look and feel of the control. The ControlTemplate can get
back to the properties of the control through a form of databinding called TemplateBinding.
The canonical example of this is use in a listbox. In Windows Forms, you add some object – say
a “Customer” to the Items collection. The only way (without owner drawing) that the customer
can be represented is via some sort of string. In particular the customer.ToString() is called to
fetch the string to show unless the “DisplayMember” property is set.
What if you have an image for your customer or some other sort of rich data? The DataTemplate
is a way of saying “foreach customer you see, create an <image> a <textblock> for their first
name and a <textblock> for their last name.”. Now whenever anyone uses a customer object
*anywhere* in the UI, these elements will be created to visually represent the customer.
Use Styles when what you want to do, you could achieve by just changing the external properties
of a control.
Use ControlTemplates when you want to change the visual structure of a control.
Triggers are a way of changing your templates and styles whenever either a property has
changed or an event has occurred.
Can you have more than one style apply to the same object?
No. You cannot have two styles specifying the same target type in the same Resources block. If
you have a nested panel with its own resource block the style from the local block trumps the
one from the outer block. For example:
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Yellow"></Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="Red"></Setter>
</Style>
</Grid.Resources>
<Button >
hello world!
</Button>
</Grid>
The way to achieve this is to either move the second setter line into the first style block, or if you
want two different styles use the “BasedOn” semantic. You can create a style that is “BasedOn”
another style.
<Window.Resources>
<Style x:Key="YellowButton"
TargetType="{x:Type Button}">
<Setter Property="Background" Value="Yellow"/>
</Style>
<Style x:Key="YellowButtonWithRedText"
BasedOn="{StaticResource YellowButton}"
TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="Red"/>
</Style>
</Window.Resources>
<Grid>
<Button Style="{StaticResource YellowButtonWithRedText}">
Hello World!
</Button>
</Grid>
BackgroundWorker still works with WPF, although it has a different synchronization context to
make it work properly with WPF.
The equivalent to BeginInvoke is BeginInvoke, however this method is tucked away on the
Dispatcher.
this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.
Normal, new EventHandler(UpdateUI));
Settings
ApplicationSettingsBase works with Avalon, ASP.Net and Windows Forms.
For more information on settings visit the FAQ. The following is the code for an options dialog:
<Window x:Class="WikiExplorer.OptionsDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WikiExplorer Options"
Width="400"
Height="160"
WindowStartupLocation="CenterOwner"
>
<Grid Name="optionsGrid" Margin="9" ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="OkCancel"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="OkCancel"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="OkCancel"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height ="Auto"/>
<RowDefinition Height ="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.Resources>
<Style x:Key="OkCancelButtons" TargetType="{x:Type Button}">
<Setter Property="Margin" Value="3"></Setter>
<Setter Property="VerticalAlignment" Value="Bottom"></Setter>
<Setter Property="Width" Value="Auto"></Setter>
</Style>
</Grid.Resources>
<!--URL information-->
<Label VerticalAlignment="Center"
Grid.Row="0" Grid.Column="0"
TabIndex="0">_Url</Label>
<TextBox Name="urlTextBox"
Text="{Binding Path=Uri}"
Margin="3"
Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="4"
TabIndex="1"/>
<!--Local Disk Cache Information-->
<Label VerticalAlignment="Center"
Grid.Row="1" Grid.Column="0"
TabIndex="0">_Local cache</Label>
<TextBox Name="localDiskCacheLocationTextBox"
Text="{Binding Path=LocalCacheLocation}"
Margin="3,3,40,3"
Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="4"
TabIndex="1"/>
<Button Name="Browse"
Click="OnLocalDiskCacheBrowseClicked"
Grid.Row="1" Grid.Column="4"
VerticalAlignment="Center" HorizontalAlignment="Right"
Width="40">...</Button>
<!--OK/Cancel/Restore-->
<Button Name="OkButton"
Click="OnOkClicked"
IsDefault="True"
Style="{StaticResource OkCancelButtons}"
Grid.Row="2" Grid.Column="2">_Ok</Button>
<Button Name="restoreDefaultSettings"
Click="OnRestoreClicked"
Style="{StaticResource OkCancelButtons}"
Grid.Row="2" Grid.Column="3">_Restore Defaults</Button>
<Button Name="CancelButton"
IsCancel="True"
Style="{StaticResource OkCancelButtons}"
Grid.Row="2" Grid.Column="4">_Cancel</Button>
</Grid>
</Window>
public OptionsDialog() {
InitializeComponent();
explorerOptions = new WikiExplorerOptions();
this.DataContext = explorerOptions;
this.Background = SystemColors.ControlBrush;
}
[UserScopedSetting]
[DefaultSettingValue("http://www.flexwiki.com/")]
public string Uri {
get {
return this["Uri"] as string;
}
set {
this["Uri"]=value;
}
}
[UserScopedSetting]
[DefaultSettingValue("")]
public string LocalCacheLocation {
get {
string localCache = this["LocalCacheLocation"] as
string;
if (string.IsNullOrEmpty(localCache)) {
localCache =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalAp
plicationData), "WikiExplorer");
}
return localCache;
}
set {
this["LocalCacheLocation"] = value;
}
}
get {
Uri uri = new Uri(this.Uri);
}
}
In Avalon, StackPanel and FlowLayoutPanel have distinct behaviors regarding stretching and alignment. StackPanel uses the Orientation property to specify vertical or horizontal flow and automatically stretches items, similar to the DockPanel, unless constraints like Width, Height, HorizontalAlignment, or VerticalAlignment are applied. In contrast, FlowLayoutPanel does not inherently stretch items across its panel. This makes StackPanel preferable for situations requiring uniform spacing and alignment control .
In Avalon, RoutedEvents are used to process input at multiple levels and support both 'tunneling' and 'bubbling' event types. Tunneling events, which start at the topmost element and work their way down to the target element, allow ancestors to preview events before they reach the target. Bubbling events, on the other hand, start at the element that raised the event and propagate up the parent chain. This dual structure provides flexible event handling, allowing for comprehensive input management akin to DHTML event models .
Avalon handles input preprocessing similarly to Windows Forms by needing to manage mouse, keyboard, and other inputs. Specific to Avalon is the incorporation of routed events, which include tunneled 'Preview' events allowing parents to process inputs before children, and bubbled 'result' events to manage responses. Commanding is another feature, offering an alternative event handling mechanism that abstracts command logic from UI event handling, reducing coupling and increasing flexibility .
The 'LastChildFill' property in the DockPanel of Avalon is critical for determining how the last added child fills available space. By default, it is set to true, causing the last child to fill any remaining space comprehensively, mimicking a fill behavior. To align with traditional docking styles where no element inherently fills remaining space, 'LastChildFill' can be set to false. This introduces explicit size and alignment control for each child, akin to traditional DockStyle configurations in Windows Forms .
In Avalon, the HierarchicalDataTemplate enhances TreeView controls by enabling dynamic generation of UI elements based on data hierarchies. When displaying XML data, a HierarchicalDataTemplate specifies how each node and its children should be represented visually, applying the template automatically for child nodes of the same type. This allows developers to define once how XML nodes are displayed and leverage automatic reapplication of these templates as children are processed .
In Avalon, setting the DataContext provides a default data source for elements and their descendants, facilitating straightforward binding. If a different source is needed, explicit alternatives include using the Source property to override the inherited DataContext, the ElementName property for element-to-element binding, or the RelativeSource property for binding within templates or to ancestors .
CollectionView in Avalon functions as an abstraction over collections, enabling advanced data manipulation through sorting, filtering, and grouping. It acts as a view layer similar to ADO.Net's DataView, providing synchronization with the 'current item' and managing the state of data in bindings. By supporting interfaces like INotifyPropertyChanged and attached properties, CollectionView facilitates powerful, responsive interfaces in data-driven applications .
The primary advantage of using Grid over TableLayoutPanel in Avalon is its flexibility for dynamic layout adjustments. The Grid can hold more than one control within a cell, making it possible to use complex layouts. In Avalon, the Grid's ability to specify absolute positioning with the Margin property allows developers to fine-tune control placements, while RowDefinition and ColumnDefinition facilitate both fixed and dynamic sizing by accommodating Absolute, Auto, and Percent-based measurements .
The Canvas control in Avalon allows positioning by using fixed pixel coordinates through attached properties like Canvas.Top and Canvas.Left. It differs from other controls because specifying Canvas.Right and Canvas.Bottom does not change the size of the item. This means the Canvas is used primarily for absolute positioning rather than dynamic resizing or alignment, making it distinct compared to more flexible controls such as the Grid, which accommodates growth, shrinkage, and shifting of adjacent controls .
In Avalon, specifying the 'Margin' property allows for absolute positioning within a Grid cell by defining the space outside a control, essentially moving it inward from the cell’s edges by the specified amounts. By using margins, developers can precisely position controls within cells without affecting the overall flow of the Grid layout, maintaining both the flexibility of resizable parents and the precision typical of absolute positioning methods .