Sunteți pe pagina 1din 76

Menus in WPF

Menu

The Menu control derives from HeaderedItemsControl. It stacks it items horizontally and draws the
typical gray background. The only property that the Menu adds to ItemsControl is
the IsMainMenu property. This controls if the menu grabs the focus if the user presses F10 or the ALT
key.

<Menu IsMainMenu="True">
<MenuItem Header="_File" />
<MenuItem Header="_Edit" />
<MenuItem Header="_View" />
<MenuItem Header="_Window" />
<MenuItem Header="_Help" />
</Menu>

MenuItem

The MenuItem is a HeaderedItemsControl. The content of the Header property is the caption of
the menu. The Itemsof a MenuItems are its sub menus. The Icon property renders a second content
on the left of the caption. This is typically used to draw a little image. But it can be used for type of
content.

You can define a keyboard shortcut by adding an underscore in front of a character.

<MenuItem Header="_Edit">
<MenuItem Header="_Cut" Command="Cut">
<MenuItem.Icon>
<Image Source="Images/cut.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Copy" Command="Copy">
<MenuItem.Icon>
<Image Source="Images/copy.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Paste" Command="Paste">
<MenuItem.Icon>
<Image Source="Images/paste.png" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>

Checkable MenuItems

You can make a menu item checkable by setting the IsCheckable property to true. The check state
can be queried by the IsChecked property. To get notified when the check state changes you can
add a handler to the Checked and Unchecked property.

<MenuItem Header="_Debug">
<MenuItem Header="Enable Debugging" IsCheckable="True" />
</MenuItem>

Separators

Separator is a simple control to group menu items. It's rendered as a horizontal line. It can also be
used in ToolBar and StatusBar.

<Menu>
<MenuItem Header="_File">
<MenuItem Header="_New..." />
<Separator />
<MenuItem Header="_Open..." />
<Separator />
<MenuItem Header="_Save" />
<MenuItem Header="_Save As..." />
<Separator />
<MenuItem Header="_Exit" />
</MenuItem>
</Menu>
Callbacks

You can register a callback to any menu item by adding a callback to the Click event.

<Menu>
<MenuItem Header="_File">
<MenuItem Header="_New..." Click="New_Click"/>
</MenuItem>
</Menu>

private void New_Click(object sender, RoutedEventArgs e)


{
MessageBox.Show("You clicked 'New...'");
}

How to bind MenuItems dynamically using MVVM

If you are using the model-view-viewmodel pattern, you probably want to define the available menu
command dynamically in your code and then bind them to a MenuItem control. The following sample
shows you how to do this:

<Menu>
<Menu.Resources>
<Style x:Key="ThemeMenuItemStyle" TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}"></Setter>
<Setter Property="Command" Value="{Binding ActivateCommand}"/>
<Setter Property="IsChecked" Value="{Binding IsActive}" />
<Setter Property="IsCheckable" Value="True"/>
</Style>
</Menu.Resources>
<MenuItem Header="Themes" ItemsSource="{Binding Themes}"
ItemContainerStyle="{StaticResource ThemeMenuItemStyle}" />
</Menu>

Keyboard Shortcuts

To add a keyboard shortcut to a menu item, add a underscode "_" in front of the caracter you want to
use as your hot key. This automatically sets the InputGestureText to an appropriate value. But you
can also override the proposed text by setting this property to a text of your choice.
WPF TextBox

How to enable spell checking

TextBox and RichTextBox provide an out-of-the-box spell checking functionality. It is available for the
following languages: English, Spanish, German and French. It can be enabled by setting the attached
property SpellCheck.IsEnabled to true.

<TextBox SpellCheck.IsEnabled="True" Language="en-US" />

How to validate input

By using a regular expression, you can easily limit and validate the input of the user. The following
code snippet shows how to do it:

protected override void OnTextInput(TextCompositionEventArgs e)


{
string fullText = Text.Remove(SelectionStart, SelectionLength) +
e.Text;
if (_regex != null && !_regex.IsMatch(fullText))
{
e.Handled = true;
}
else
{
base.OnTextInput(e);
}
}
Radio Button

Introduction

The RadioButton control has its name from old analog radios which had a number of programmable
station buttons. When you pushed one in, the previosly selected poped out. So only one station can be
selected at a time.

The RadioButton control has the same behavior. It lets the user choose one option out of a few. It
the list of options gets longer, you should prefer a combo or list box instead.

To define which RadioButtons belong togehter, you have to set the GroupName to the same name.

To preselect one option set the IsChecked property to True.

<StackPanel>
<RadioButton GroupName="Os" Content="Windows XP" IsChecked="True"/>
<RadioButton GroupName="Os" Content="Windows Vista" />
<RadioButton GroupName="Os" Content="Windows 7" />
<RadioButton GroupName="Office" Content="Microsoft Office 2007"
IsChecked="True"/>
<RadioButton GroupName="Office" Content="Microsoft Office 2003"/>
<RadioButton GroupName="Office" Content="Open Office"/>
</StackPanel>

How to DataBind Radio Buttons in WPF

The radio button control has a known issue with data binding. If you bind the IsChecked property to
a boolean and check the RadioButton, the value gets True. But when you check another RadioButton,
the databound value still remains true.
The reason for this is, that the Binding gets lost during the unchecking, because the controls internally
calls ClearValue() on the dependency property.

<Window.Resources>
<EnumMatchToBooleanConverter x:Key="enumConverter" />
</Window.Resources>

<RadioButton Content="Option 1" GroupName="Options1"


IsChecked="{Binding Path=CurrentOption, Mode=TwoWay,
Converter={StaticResource enumConverter},
ConverterParameter=Option1}" />
<RadioButton Content="Option 2" GroupName="Options2"
IsChecked="{Binding Path=CurrentOption, Mode=TwoWay,
Converter={StaticResource enumConverter},
ConverterParameter=Option2}" />
<RadioButton Content="Option 3" GroupName="Options3"
IsChecked="{Binding Path=CurrentOption, Mode=TwoWay,
Converter={StaticResource enumConverter},
ConverterParameter=Option3}" />

public class EnumMatchToBooleanConverter : IValueConverter


{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value == null || parameter == null)
return false;

string checkValue = value.ToString();


string targetValue = parameter.ToString();
return checkValue.Equals(targetValue,
StringComparison.InvariantCultureIgnoreCase);
}

public object ConvertBack(object value, Type targetType,


object parameter, CultureInfo culture)
{
if (value == null || parameter == null)
return null;

bool useValue = (bool)value;


string targetValue = parameter.ToString();
if (useValue)
return Enum.Parse(targetType, targetValue);

return null;
}
}
ToolTips in WPF

<Button Content="Submit">
<Button.ToolTip>
<ToolTip>
<StackPanel>
<TextBlock FontWeight="Bold">Submit Request</TextBlock>
<TextBlock>Submits the request to the server.</TextBlock>
</StackPanel>
</ToolTip>
</Button.ToolTip>
</Button>

How to show ToolTips on disabled controls

When you disable a control with IsEnabled=False the tooltip does not show anymore. If you want
to have the tooltip appear anyway you have to set the attaached
property ToolTipService.ShowOnDisabled to True.

<Button IsEnabled="False"
ToolTip="Saves the current document"
ToolTipService.ShowOnDisabled="True"
Content="Save">
</Button>

How to change the show duration of a ToolTip

The static class ToolTipService allows you to modify the show duration of the tooltip

<Button ToolTip="Saves the current document"


ToolTipService.ShowDuration="20"
Content="Save">
</Button>
WPF Expander Control

Introduction

The Expander control is like a GroupBox but with the additional feature to collapse and expand its
content. It derives from HeaderedContentControl so it has a Header property to set the header
content, and a Content property for the expandable content.

It has a IsExpanded property to get and set if the expander is in expanded or collapsed state.

In collapsed state the expander takes only the space needed by the header. In expanded state it takes
the size of header and content together.

<Expander Header="More Options">


<StackPanel Margin="10,4,0,0">
<CheckBox Margin="4" Content="Option 1" />
<CheckBox Margin="4" Content="Option 2" />
<CheckBox Margin="4" Content="Option 3" />
</StackPanel>
</Expander>

Popup Control
Introduction follows...

How to make the popup close, when it loses focus

Just set the StaysOpen property to False. Unfortunately this is not the default behavior

<Popup StaysOpen="False" />


WPF Calendar Control

Introduction

Since WPF 4.0, Microsoft provides a full featured calendar control. It provides the following features:

Set the displayed date


Multiple selection modes
Blackout dates
Calendar Modes

Set the displayed date

The calendar displays by default the current date. But you can specify any other date to be displayed
by setting the DisplayDate property.

<Calendar DisplayDate="01.01.2010" />

Selection Modes

The calendar control provides multiple modes for selection. You can set the SelectionMode property
to SingleDateSingleRange, MultipleRanges or None.

<Calendar SelectionMode="MultipleRange" />


Blackout dates

The calendar control provides a feature to black out dates that are not valid for selection. You can
define multiple ranges by setting the BlackoutDates property to one or
multiple CalendarDateRange.

<Calendar SelectionMode="{Binding SelectedItem,


ElementName=selectionmode}" >
<Calendar.BlackoutDates>
<CalendarDateRange Start="01/01/2010" End="01/06/2010" />
<CalendarDateRange Start="05/01/2010" End="05/03/2010" />
</Calendar.BlackoutDates>
</Calendar>

Calendar Modes

The calendar supports three modes to display ranges of dates: Year, Month and Decade. You can
control the mode by setting the DisplayMode property.

<Calendar DisplayMode="Year" />


omboBox with Live Preview

The Live Preview Pattern

If you are using Microsoft Office 2007 or later, you are familiar with the "live preview" concept. They
are using it for all kind of selections like color, fonttype or fontsize. The idea behind this pattern is to
give the user an immediate feedback, how the object would look like, if he does the selection, without
actually doing it. So he can leave the combo and nothing has changed.

How to use the LivePreviewComboBox Control

I encapsulated this functionality into a custom control called LivePreviewComboBox that provides
an additional dependency property LivePreviewItem where you can bind to. The following code
snipped explains how to use it:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:LivePreviewComboBox">

<StackPanel>
<TextBlock Text="Preview Value:" />
<TextBlock Text="{Binding LivePreviewItem, ElementName=liveBox}"
/>
<l:LivePreviewComboBox x:Name="liveBox"/>
</StackPanel>

</Window>
WPF Slider Control

How to Make a Slider Snap to Integer Values

If you just set the Minimum and Maximum of a slider and choose a value the result is determined by
the pixel position of the thumb. The value is typically a high-precision value with many decimal places.
To allow only integer values you have to set the IsSnapToTickEnabled property to True.
<Slider Minimum="0"
Maximum="20"
IsSnapToTickEnabled="True"
TickFrequency="2"

Dialogs in WPF

OK and Cancel Buttons in a Dialog

You have a modal dialog with several buttons on it and you want to automatically close it, when the
user presses on some of them. To do this you have to set IsCancel="true" on all buttons that
should close the dialog and return false. On one button you set IsDefault="true" this will be
executed when you press [Enter]. It closes the dialog and returns... also false. To return true here you
have to register a callback that sets the DialogResult to true

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Button Content="Cancel" IsCancel="True" />
<Button Click="OkClick" Content="Ok" IsDefault="true" />
</StackPanel>
</Window>

private void OkClick(object sender, RoutedEventArgs e)


{
this.DialogResult = true;
}
WPF PasswordBox Control
The password box control is a special type of TextBox designed to enter passwords. The typed in
characters are replaced by asterisks. Since the password box contains a sensible password it does not
allow cut, copy, undo and redo commands.

<StackPanel>
<Label Content="Password:" />
<PasswordBox x:Name="passwordBox" Width="130" />
</StackPanel>

Change the password character

To replace the asteriks character by another character, set the PasswordChar property to the character
you desire.

<PasswordBox x:Name="passwordBox" PasswordChar="*" />

Limit the length of the password

To limit the length of the password a user can enter set the MaxLength property to the amount of
characters you allow.

<PasswordBox x:Name="passwordBox" MaxLength="8" />

Databind the Password Property of a WPF PasswordBox

When you try to databind the password property of a PasswordBox you will recognize that you cannot
do data binding on it. The reason for this is, that the password property is not backed by a
DependencyProperty.

The reason is databinding passwords is not a good design for security reasons and should be avoided.
But sometimes this security is not necessary, then it's only cumbersome that you cannot bind to the
password property. In this special cases you can take advantage of the following PasswortBoxHelper.

<StackPanel>
<PasswordBox w:PasswordHelper.Attach="True"
w:PasswordHelper.Password="{Binding Text, ElementName=plain,
Mode=TwoWay}"
Width="130"/>
<TextBlock Padding="10,0" x:Name="plain" />
</StackPanel>

The PasswordHelper is attached to the password box by calling


the PasswordHelper.Attach property. The attached
property PasswordHelper.Password provides a bindable copy of the original password property
of the PasswordBox control.

public static class PasswordHelper


{
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.RegisterAttached("Password",
typeof(string), typeof(PasswordHelper),
new FrameworkPropertyMetadata(string.Empty,
OnPasswordPropertyChanged));

public static readonly DependencyProperty AttachProperty =


DependencyProperty.RegisterAttached("Attach",
typeof(bool), typeof(PasswordHelper), new PropertyMetadata(false,
Attach));

private static readonly DependencyProperty IsUpdatingProperty =


DependencyProperty.RegisterAttached("IsUpdating", typeof(bool),
typeof(PasswordHelper));

public static void SetAttach(DependencyObject dp, bool value)


{
dp.SetValue(AttachProperty, value);
}

public static bool GetAttach(DependencyObject dp)


{
return (bool)dp.GetValue(AttachProperty);
}

public static string GetPassword(DependencyObject dp)


{
return (string)dp.GetValue(PasswordProperty);
}

public static void SetPassword(DependencyObject dp, string value)


{
dp.SetValue(PasswordProperty, value);
}

private static bool GetIsUpdating(DependencyObject dp)


{
return (bool)dp.GetValue(IsUpdatingProperty);
}

private static void SetIsUpdating(DependencyObject dp, bool value)


{
dp.SetValue(IsUpdatingProperty, value);
}
private static void OnPasswordPropertyChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
passwordBox.PasswordChanged -= PasswordChanged;

if (!(bool)GetIsUpdating(passwordBox))
{
passwordBox.Password = (string)e.NewValue;
}
passwordBox.PasswordChanged += PasswordChanged;
}

private static void Attach(DependencyObject sender,


DependencyPropertyChangedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;

if (passwordBox == null)
return;

if ((bool)e.OldValue)
{
passwordBox.PasswordChanged -= PasswordChanged;
}

if ((bool)e.NewValue)
{
passwordBox.PasswordChanged += PasswordChanged;
}
}

private static void PasswordChanged(object sender, RoutedEventArgs e)


{
PasswordBox passwordBox = sender as PasswordBox;
SetIsUpdating(passwordBox, true);
SetPassword(passwordBox, passwordBox.Password);
SetIsUpdating(passwordBox, false);
}
}
WPF DataGrid Control

Introduction

Since .NET 4.0, Microsoft is shipping a DataGrid control that provides all the basic functionality needed,
like:

Auto generation of columns


Manual definition of columns
Selection
Grouping
Column sorting, reordering and resizing
Row Details
Alternating BackgroundBrush
Frozen columns
Headers Visibility
How to template autogenerated columns

Basic usage: Auto generate columns

To show a basic data grid , just drop a DataGrid control to your view and bind the ItemsSource to
a collection of data objects and you're done. The DataGrid provides a feature
called AutoGenerateColumns that automatically generates column according to the public
properties of your data objects. It generates the following types of columns:

TextBox columns for string values


CheckBox columns for boolean values
ComboBox columns for enumerable values
Hyperlink columns for Uri values
<DataGrid ItemsSource="{Binding Customers}" />

Manually define columns

Alternatively you can define your columns manually by setting the AutoGenerateColumns property
to False. In this case you have to define the columns in the Columns collection of the data grid. You
have the following types of columns available:

DataGridCheckBoxColumn for boolean values


DataGridComboBoxColumn for enumerable values
DataGridHyperlinkColumn for Uri values
DataGridTemplateColumn to show any types of data by defining your own cell template
DataGridTextColumn to show text values
<DataGrid ItemsSource="{Binding Customers}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTemplateColumn Header="Image" Width="SizeToCells"
IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Image}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>

Selection

The data grid includes a variety of selection modes. They are configured by
the SelectionMode and SelectionUnit property.

The SelectionMode can be set to Single or Extended to define if one or multiple units
can be selected simultaneously.
The SelectionUnit defines the scope of one selection unit. It can be set
to Cell, CellAndRowHeader and FullRow.

<DataGrid ItemsSource="{Binding Customers}"


SelectionMode="Extended" SelectionUnit="Cell" />

Column sorting, reordering and resizing

The data grid provides features to sort, reorder and resize columns. They can be enabled or disabled
by the following properties:
CanUserReorderColumns enables or disables column re-ordering
CanUserResizeColumns enables or disables column resizing
CanUserResizeRows enables or disables row resizing
CanUserSortColumns enables or disables column sorting

<DataGrid ItemsSource="{Binding Customers}"


CanUserReorderColumns="True" CanUserResizeColumns="True"
CanUserResizeRows="False" CanUserSortColumns="True"/>

Grouping

The data grid also supports grouping. To enable grouping you have to define
a CollectionView that contains to least one GroupDescription that defines the criterias how to
group.

Customers = new ListCollectionView(_customers);


Customers.GroupDescriptions.Add(new PropertyGroupDescription("Gender"));

Second thing you need to do is defining a template how the groups should look like. You can do this
by setting the GroupStyle to something like the following snippet.

<DataGrid ItemsSource="{Binding GroupedCustomers}">


<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type
GroupItem}">
<Expander>
<Expander.Header>
<StackPanel
Orientation="Horizontal">
<TextBlock Text="{Binding
Path=Name}" />
<TextBlock Text="{Binding
Path=ItemCount}"/>
<TextBlock Text="Items"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
</DataGrid>

Row Details

The data grid provides a feature that shows a detail panel for a selected row. It can be enabled by
setting a DataTemplate to the RowDetailsTemplate property. The data template gets the object
that is bound to this row passed by the DataContext and can bind to it.
<DataGrid ItemsSource="{Binding Customers}">
<DataGrid.Columns>
<DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"
/>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<Image Height="100" Source="{Binding Image}" />
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>

Row Details depending on the type of data

You can specify a RowDetailsTemplateSelector that selects a data template according to the
type or data that this row contains. To do this, create a type that derives
from DataTemplateSelector and override the SelectTemplate method. In the items argument
you get the data and you can determine which data template to display. Return an instance of that
data template as return value.

public class GenderTemplateSelector : DataTemplateSelector


{
public DataTemplate MaleTemplate { get; set; }
public DataTemplate FemaleTemplate { get; set; }

public override DataTemplate SelectTemplate(object item,


DependencyObject container)
{
var customer = item as Customer;
if (customer == null)
return base.SelectTemplate(item, container);

if( customer.Gender == Gender.Male)


{
return MaleTemplate;
}
return FemaleTemplate;
}
}

<l:GenderTemplateSelector x:Key="genderTemplateSelector">
<l:GenderTemplateSelector.MaleTemplate>
<DataTemplate>
<Grid Background="LightBlue">
<Image Source="{Binding Image}" Width="50" />
</Grid>
</DataTemplate>
</l:GenderTemplateSelector.MaleTemplate>
<l:GenderTemplateSelector.FemaleTemplate>
<DataTemplate>
<Grid Background="Salmon">
<Image Source="{Binding Image}" Width="50" />
</Grid>
</DataTemplate>
</l:GenderTemplateSelector.FemaleTemplate>
</l:GenderTemplateSelector>

<DataGrid ItemsSource="{Binding Customers}"


RowDetailsTemplateSelector="{StaticResource
genderTemplateSelector}" />

Alternating BackgroundBrush

You can define a an AlternatingRowBackground that is applied every even row. You can
additionally specify an AlternationCount if you only want to ink every every n-th data row.
<DataGrid ItemsSource="{Binding Customers}"
AlternatingRowBackground="Gainsboro" AlternationCount="2"/>

Frozen Columns

The data grid also supports the feature to freeze columns. That means they stay visible while you scoll
horizontally through all columns. This is a useful feature to keep a referencing column like an ID or a
name always visible to keep your orientation while scrolling.

To freeze a numer of columns just set the FrozenColumnCount property to the number of columns
you want to freeze.

<DataGrid ItemsSource="{Binding Customers}" FrozenColumnCount="2" />

Headers visbility

You can control the visibility of row and column headers by setting
the HeadersVisibility property to either None,Row,Column or All

<DataGrid ItemsSource="{Binding Customers}" HeadersVisibility="None" />

How to template autogenerated columns

If you want to autogenerate columns using AutoGenerateColumns="True", you cannot


use CellTemplates, because the DataGridautogenerates either a text, combo, hyperlink or
checkbox column, but none of these are templateable. A simple workaround is to hook into the
autogeneration, cancel it and always create a DataGridTemplateColumn. The following snippet
shows the idea (the code is just a draft):

public class MyDataGrid : DataGrid


{

public DataTemplateSelector CellTemplateSelector


{
get { return
(DataTemplateSelector)GetValue(CellTemplateSelectorProperty); }
set { SetValue(CellTemplateSelectorProperty, value); }
}

public static readonly DependencyProperty CellTemplateSelectorProperty


=
DependencyProperty.Register("Selector",
typeof(DataTemplateSelector), typeof(MyDataGrid),
new FrameworkPropertyMetadata(null));

protected override void


OnAutoGeneratingColumn(DataGridAutoGeneratingColumnEventArgs e)
{
e.Cancel = true;
Columns.Add(new DataGridTemplateColumn
{
Header = e.Column.Header,
CellTemplateSelector = CellTemplateSelector
});
}
}

<l:MyDataGrid ItemsSource="{Binding}"
AutoGenerateColumns="True"
CellTemplateSelector="{StaticResource templateSelector}" />

Context Menus in WPF


Context Menus can be defined on any WPF controls by setting the ContextMenu property to an
instance of a ContextMenu. The items of a context menu are normal MenuItems.

<RichTextBox>
<RichTextBox.ContextMenu>
<ContextMenu>
<MenuItem Command="Cut">
<MenuItem.Icon>
<Image Source="Images/cut.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Command="Copy">
<MenuItem.Icon>
<Image Source="Images/copy.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Command="Paste">
<MenuItem.Icon>
<Image Source="Images/paste.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</RichTextBox.ContextMenu>
</RichTextBox>

Show ContextMenus on a disabled controls

If you rightclick on a disabled control, no context menu is shown by default. To enable the context
menu for disabled controls you can set the ShowOnDisabled attached property of
the ContextMenuService to True.

<RichTextBox IsEnabled="False" ContextMenuService.ShowOnDisabled="True">


<RichTextBox.ContextMenu>
<ContextMenu>
...
</ContextMenu>
</RichTextBox.ContextMenu>
</RichTextBox>

Merge ContextMenus

If you want to fill a menu with items coming from multiple sources, you can use
the CompositeCollection to merge multiple collection into one.

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Grid Background="Transparent">
<Grid.Resources>
<x:Array Type="{x:Type sys:Object}" x:Key="extensions">
<Separator />
<MenuItem Header="Extension MenuItem 1" />
<MenuItem Header="Extension MenuItem 2" />
<MenuItem Header="Extension MenuItem 3" />
</x:Array>
</Grid.Resources>
<Grid.ContextMenu>
<ContextMenu>
<ContextMenu.ItemsSource>
<CompositeCollection>
<MenuItem Header="Standard MenuItem 1" />
<MenuItem Header="Standard MenuItem 2" />
<MenuItem Header="Standard MenuItem 3" />
<CollectionContainer Collection="{StaticResource
extensions}" />
</CompositeCollection>
</ContextMenu.ItemsSource>
</ContextMenu>
</Grid.ContextMenu>
</Grid>
</Window>

How to bind a Command on a ContextMenu within a DataTemplate using


MVVM

Since the Popuup control has it's separate visual tree, you cannot use find ancestor to find the Grid.
The trick here is to use the PlacementTargetproperty, that contains the element, the ContextMenu
is aligned to, what is the Grid in our case.

But this is only half of the solution. Because of the data template, the DataContext is set to a
dataitem, and not the view model. So you need another relative source lookup, to find the view model.
Trick Nr. 2 is to use the Tag property to bind the view model from outside to the grid, which is
the PlacementTarget used above. And there we are.

<DataTemplate>
<Grid Tag="{Binding DataContext, RelativeSource={RelativeSource
AncestorType={x:Type ListBox}}}">
<Grid.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag,
RelativeSource={RelativeSource Self}}">
<MenuItem Content="Cut" Command="{Binding CutCommand}" />
<MenuItem Content="Copy" Command="{Binding CopyCommand}" />
<MenuItem Content="Paste" Command="{Binding PasteCommand}" />
</ContextMenu>
</Grid.ContextMenu>
</Grid>
</DataTemplate>

How to open a context menu from code

The following sample shows you how to open a context menu of a control programmatically:

private void OpenContextMenu(FrameworkElement element)


{
if( element.ContextMenu != null )
{
element.ContextMenu.PlacementTarget = element;
element.ContextMenu.IsOpen = true;
}
}

WPF ListBox Control

Introduction

The ListBox control displays a list of items. The user can select one or multiple items depending on the
selection mode. The typical usage of a listbox in WPF is to bind its items to a list of business objects
and display them by applying a data template.

<ListBox Margin="20">
<ListBoxItem>New York</ListBoxItem>
<ListBoxItem>Los Angeles</ListBoxItem>
<ListBoxItem>Paris</ListBoxItem>
<ListBoxItem>Zurich</ListBoxItem>
</ListBox>

How to define a Trigger for IsSelected in the DataTemplate

If you want to change the appearance of a ListBoxItem when it is selected, you have to bind the
IsSelected property of the ListBoxItem. But this is a bit tricky, you have to use a relative source with
FindAcestor to navigate up the visual tree until you reach the ListBoxItem.

<DataTemplate x:Key="myDataTemplate">
<Border x:Name="border" Height="50">
<TextBlock Text="{Binding Text}" />
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource=
{RelativeSource Mode=FindAncestor, AncestorType=
{x:Type ListBoxItem}},Path=IsSelected}" Value="True">
<Setter TargetName="border" Property="Height" Value="100"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>

How to apply a DataTemplate to a ListBox

Introducing DataTemplates

All controls deriving from ItemsControl have a DataTemplate that specifies how an object bound
to an item is presented to the user. The default template renders a single line of text per item - as we
know a listbox from HTML or WinForms.

But WPF allows us to put whatever we like into a DataTemplate. If we want to display a list of
customers we can provide a data template that shows an image for each customer next to its name
and age. In my opinion this is one of the most powerful features in WPF.

In the following steps I will show you how to create such an DataTemplate

Bind the data to the ListBox

We bind a list of employees to the listbox. Because the listbox does not know anything about our
employee class, it calls the ToString() method. The result is not yet very pleasing.

<Grid x:Name="LayoutRoot">
<ListBox Margin="10" ItemsSource="{Binding}"/>
</Grid>

Override the ToString() method to improve the display

What we do next is to overriding the ToString() method to provide some useful information instead of
just the type name. But a string is not really what we call a cool user experience.

public override string ToString()


{
return string.Format("{0} {1} ({2}), {3})",
Firstname, Lastname, Age, Role);
}

Create a DataTemplate

The ultimate flexibility is to create your own DataTemplate. A data template can consist of multiple
controls like images, textblocks, etc. It han have a flexible width and height, background color and
shape. There are no limits for your creativity.

An easy way to create a data template is to use Expression Blend. Open the context menu of your list
box and choose "Edit Other Templates" -> "Edit Generated Items" -> "Create Empty".
Blend will ask you to specify a name for your DataTemplate because it will define it in the resources of
either the Application, the current document of an external resource dictionary. The name you specify
is the key for the resource dictionary.

After you press OK, you will be in the editor to design the data template. The item data (in our sample
an employee) is set to the DataContext, so you can easily access all properties of the employee by
using databinding.
<DataTemplate x:Key="EmployeeDataTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Margin="5" BorderBrush="Black" BorderThickness="1">
<Image Source="{Binding Path=Image}" Stretch="Fill" Width="50"
Height="50" />
</Border>
<StackPanel Grid.Column="1" Margin="5">
<StackPanel Orientation="Horizontal" TextBlock.FontWeight="Bold" >
<TextBlock Text="{Binding Path=Firstname,
FallbackValue=FirstName}" />
<TextBlock Text="{Binding Path=Lastname, FallbackValue=LastName}"
Padding="3,0,0,0"/>
</StackPanel>
<TextBlock Text="{Binding Path=Age, FallbackValue=Age}" />
<TextBlock Text="{Binding Path=Role, FallbackValue=Role}" />
</StackPanel>
</Grid>
</DataTemplate>

How to strech an WPF ListBox Item to span the whole width


If you create a data template with a right-aligned element (like the age in our exmple), it only spans
over the needed width, because the content of a listbox is left-aligned by default.

This is the DataTemplate I created for this example.

<Style TargetType="ListBox" x:Key="strechedItemStyle">


<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid Background="#330000FF">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}"
HorizontalAlignment="Left" Grid.Column="0"/>
<TextBlock Text="{Binding Age}"
HorizontalAlignment="Right" Grid.Column="1"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>

To make the listbox item span the whole width of the listbox you need to set
the HorizontalContentAlignment property to Stretch.

This is the way to set it directly on the listbox element:

<ListBox x:Name="listBox" Margin="20"


Style="{StaticResource strechedItemStyle}"
HorizontalContentAlignment="Stretch" />

If you want to set this property within a style you can do it with the following lines of code

<Style TargetType="ListBox" x:Key="strechedItemStyle">


<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
Change the Background of a selected ListBox Item
If you select an item in a listbox it gets the default selection color (usually blue) as background. Even if
you specify a custom data template. The reason is that the blue background (or gray if the control is
not focussed) is drawn outside of the data template. So you have no chance to override it from within
the data template.

The color used to draw the blue and gray background are system colors. So the easiest way to get rid
of these backgrounds is to locally override the highlight and control brushes of the system colors.

The best way to do this is to create a style for the listbox. Place the style in the resources of a parent
element. For e.g. Window.Resources

<Style x:Key="myListboxStyle">
<Style.Resources>
<!-- Background of selected item when focussed -->
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="Red" />
<!-- Background of selected item when not focussed -->
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}"
Color="Green" />
</Style.Resources>
</Style>

The following XAML snippet shows how to apply the style to the listbox.

<Grid x:Name="LayoutRoot">
<ListBox Style="{StaticResource myListboxStyle}" />
</Grid>
Change the arrangement of items in a listbox
All WPF controls deriving from ItemsControl provide an ItemsPanel property that allows you to
replace the internal layout panel that arranges the items.

Horizontal

We override the ItemsPanel property and set it to a StackPanel layout manager with an
orientation set to Horizontal. We use an VirtualizingStackPanel that works just like a normal
StackPanel, except that is does only create the items that are visible. If you scroll it automatically
creates the new items that become visible and recycles the hidden.

<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

Wrapped

Using a WrapPanel to layout the items row by row is a bit more complex. Since the layout panel is
wrapped by a ScrollContentPresenter (as seen by the scrolbar of the exmple above) the
available width is infinite. So the WrapPanel does not see a reason to wrap to a new line.

What we need to do is to set the width of the warp panel to the ActualWidth if the internal
ScrollContentPresenter. The ScrollContentPresenter can be found by using the FindAncestor mode of
the relative source extension.

<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

WPF ListView Control

How to Hide the Header of a ListView

To hide the header of a ListView you can modify the Visibility property of the
ColumnHeaderContainer by overriding the style locally.
<ListView>
<ListView.View>
<GridView>
<GridView.ColumnHeaderContainerStyle>
<Style>
<Setter Property="FrameworkElement.Visibility"
Value="Collapsed"/>
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridView.Columns>
...
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>

WPF TextBlock Control

How to change the line height within a TextBlock

To change the line hight within a TextBlock, you have to set the LineHeight to the desired height (in
logical units) and also the LineStackingStrategy to BlockLineHeight, because otherwhise you
will not see any effect.

<TextBlock Text="This is a
multiline text."
LineHeight="25" LineStackingStrategy="BlockLineHeight" />
How to remove the icon of a WPF window

Unfortumately WPF does not provide any function to remove the icon of a window. One solution could
be setting the icon to a transparent icon. But this way the extra space between the window border and
title remains.

The better approach is to use a function provided by the Win32 API to remove the icon.

public partial class Window1 : Window


{
public Window1()
{
InitializeComponent();
}

protected override void OnSourceInitialized(EventArgs e)


{
IconHelper.RemoveIcon(this);
}
}

public static class IconHelper


{
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hwnd, int index);

[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hwnd, int index, int
newStyle);

[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hwnd, IntPtr
hwndInsertAfter,
int x, int y, int width, int height, uint flags);

[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hwnd, uint msg,
IntPtr wParam, IntPtr lParam);

const int GWL_EXSTYLE = -20;


const int WS_EX_DLGMODALFRAME = 0x0001;
const int SWP_NOSIZE = 0x0001;
const int SWP_NOMOVE = 0x0002;
const int SWP_NOZORDER = 0x0004;
const int SWP_FRAMECHANGED = 0x0020;
const uint WM_SETICON = 0x0080;

public static void RemoveIcon(Window window)


{
// Get this window's handle
IntPtr hwnd = new WindowInteropHelper(window).Handle;

// Change the extended window style to not show a window icon


int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle |
WS_EX_DLGMODALFRAME);

// Update the window's non-client area to reflect the changes


SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE |
SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}

Introduction to WPF Layout


Why layout is so important

Best Practices

Vertical and Horizontal Alignment

Margin and Padding

Width and Height

Content Overflow Handling

Why layout is so important

Layout of controls is critical to an applications usability. Arranging controls based on fixed pixel
coordinates may work for an limited enviroment, but as soon as you want to use it on different screen
resolutions or with different font sizes it will fail. WPF provides a rich set built-in layout panels that
help you to avoid the common pitfalls.

These are the five most popular layout panels of WPF:

Grid Panel
Stack Panel
Dock Panel
Wrap Panel
Canvas Panel

Best Practices

Avoid fixed positions - use the Alignment properties in combination with Margin to
position elements in a panel
Avoid fixed sizes - set the Width and Height of elements to Auto whenever possible.
Don't abuse the canvas panel to layout elements. Use it only for vector graphics.
Use a StackPanel to layout buttons of a dialog
Use a GridPanel to layout a static data entry form. Create a Auto sized column for the labels
and a Star sized column for the TextBoxes.
Use an ItemControl with a grid panel in a DataTemplate to layout dynamic key value lists. Use
the SharedSizefeature to synchronize the label widths.

Vertical and Horizontal Alignment

Use the VerticalAlignment and HorizontalAlignmant properties to dock the controls to one
or multiple sides of the panel. The following illustrations show how the sizing behaves with the
different combinations.

Margin and Padding

The Margin and Padding properties can be used to reserve some space around of within the control.

The Margin is the extra space around the control.


The Padding is extra space inside the control.
The Padding of an outer control is the Margin of an inner control.

Height and Width

Alltough its not a recommended way, all controls provide a Height and Width property to give an
element a fixed size. A better way is to use
the MinHeight, MaxHeight, MinWidth and MaxWidth properties to define a acceptable range.
If you set the width or height to Auto the control sizes itself to the size of the content.

Overflow Handling

Clipping

Layout panels typically clip those parts of child elements that overlap the border of the panel. This
behavior can be controlled by setting the ClipToBounds property to true or false.

Scrolling

When the content is too big to fit the available size, you can wrap it into a ScrollViewer. The
ScrollViewer uses two scroll bars to choose the visible area.

The visibility of the scrollbars can be controlled by the vertical and


horizontal ScrollbarVisibility properties.
<ScrollViewer>
<StackPanel>
<Button Content="First Item" />
<Button Content="Second Item" />
<Button Content="Third Item" />
</StackPanel>
</ScrollViewer>
WPF StackPanel

Introduction

The StackPanel in WPF is a simple and useful layout panel. It stacks its child elements below or
beside each other, dependening on its orientation. This is very useful to create any kinds of lists. All
WPF ItemsControls like ComboBox, ListBox or Menu use a StackPanel as their internal layout
panel.

<StackPanel>
<TextBlock Margin="10" FontSize="20">How do you like your
coffee?</TextBlock>
<Button Margin="10">Black</Button>
<Button Margin="10">With milk</Button>
<Button Margin="10">Latte machiato</Button>
<Button Margin="10">Chappuchino</Button>
</StackPanel>

Stack Items horizontally

A good example for a horizontal stack panel are the "OK" and "Cancel" buttons of a dialog window.
Because the size of the text can change if the user changes the font-size or switches the language we
should avoid fixed sized buttons. The stack panel aligns the two buttons depending on their desired
size. If they need more space they will get it automatically. Never mess again with too small or too
large buttons.

<StackPanel Margin="8" Orientation="Horizontal">


<Button MinWidth="93">OK</Button>
<Button MinWidth="93" Margin="10,0,0,0">Cancel</Button>
</StackPanel>

Grid Panel
Introduction

How to define rows and columns

How to add controls to the grid

Resize columns or rows

How to share the width of a column over multiple grids

Using GridLenghts from code

Introduction

The grid is a layout panel that arranges its child controls in a tabular structure of rows and columns. Its
functionality is similar to the HTML table but more flexible. A cell can contain multiple controls, they
can span over multiple cells and even overlap themselves.
The resize behaviour of the controls is defined by
the HorizontalAlignment and VerticalAlignment properties who define the anchors. The
distance between the anchor and the grid line is specified by the margin of the control

Define Rows and Columns

The grid has one row and column by default. To create additional rows and columns, you have to
add RowDefinitionitems to the RowDefinitions collection and ColumnDefinition items to
the ColumnDefinitions collection. The following example shows a grid with three rows and two
columns.

The size can be specified as an absolute amount of logical units, as a percentage value or
automatically.

Fixed Fixed size of logical units (1/96 inch)


Auto Takes as much space as needed by the contained control
Star (*) Takes as much space as available (after filling all auto and fixed sized columns),
proportionally divided over all star-sized columns. So 3*/5* means the same as 30*/50*.
Remember that star-sizing does not work if the grid size is calculated based on its content.

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
</Grid>

How to add controls to the grid

To add controls to the grid layout panel just put the declaration between the opening and closing tags
of the Grid. Keep in mind that the row- and columndefinitions must precced any definition of child
controls.

The grid layout panel provides the two attached properties Grid.Column and Grid.Row to define
the location of the control.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Name:"/>
<Label Grid.Row="1" Grid.Column="0" Content="E-Mail:"/>
<Label Grid.Row="2" Grid.Column="0" Content="Comment:"/>
<TextBox Grid.Column="1" Grid.Row="0" Margin="3" />
<TextBox Grid.Column="1" Grid.Row="1" Margin="3" />
<TextBox Grid.Column="1" Grid.Row="2" Margin="3" />
<Button Grid.Column="1" Grid.Row="3" HorizontalAlignment="Right"
MinWidth="80" Margin="3" Content="Send" />
</Grid>

Resizable columns or rows

WPF provides a control called the GridSplitter. This control is added like any other control to a cell
of the grid. The special thing is that is grabs itself the nearest gridline to change its width or height
when you drag this control around.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="Left" Grid.Column="0" />
<GridSplitter HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Grid.Column="1" ResizeBehavior="PreviousAndNext"
Width="5" Background="#FFBCBCBC"/>
<Label Content="Right" Grid.Column="2" />
</Grid>

The best way to align a grid splitter is to place it in its own auto-sized column. Doing it this way
prevents overlapping to adjacent cells. To ensure that the grid splitter changes the size of the previous
and next cell you have to set the ResizeBehavior to PreviousAndNext.
The splitter normally recognizes the resize direction according to the ratio between its height and
width. But if you like you can also manually set the ResizeDirection to Columns or Rows.
<GridSplitter ResizeDirection="Columns"/>

How to share the width of a column over multiple grids

The shared size feature of the grid layout allows it to synchronize the width of columns over multiple
grids. The feature is very useful if you want to realize a multi-column listview by using a grid as layout
panel within the data template. Because each item contains its own grid, the columns will not have the
same width.

By setting the attached property Grid.IsSharedSizeScope to true on a parent element you


define a scope within the column-widths are shared.

To synchronize the width of two columndefinitions, set the SharedSizeGroup to the same name.

<ItemsControl Grid.IsSharedSizeScope="True" >


<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="FirstColumn" Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Key}" TextWrapping="Wrap"/>
<TextBlock Text="{Binding Path=Value}" Grid.Column="1"
TextWrapping="Wrap"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Useful Hints

Columns and rows that participate in size-sharing do not respect Star sizing. In the size-sharing
scenario, Star sizing is treated as Auto. Since TextWrapping on TextBlocks within an SharedSize column
does not work you can exclude your last column from the shared size. This often helps to resolve the
problem.

Using GridLenghts from code

If you want to add columns or rows by code, you can use the GridLength class to define the
differenz types of sizes.

Auto sized GridLength.Auto


Star sized new GridLength(1,GridUnitType.Star)
Fixed size new GridLength(100,GridUnitType.Pixel)
Grid grid = new Grid();

ColumnDefinition col1 = new ColumnDefinition();


col1.Width = GridLength.Auto;
ColumnDefinition col2 = new ColumnDefinition();
col2.Width = new GridLength(1,GridUnitType.Star);

grid.ColumnDefinitions.Add(col1);
grid.ColumnDefinitions.Add(col2);

Dock Panel

Introduction

The dock panel is a layout panel, that provides an easy docking of elements to the left, right, top,
bottom or center of the panel. The dock side of an element is defined by the attached
property DockPanel.Dock. To dock an element to the center of the panel, it must be the last child of
the panel and the LastChildFill property must be set to true.

<DockPanel LastChildFill="True">
<Button Content="Dock=Top" DockPanel.Dock="Top"/>
<Button Content="Dock=Bottom" DockPanel.Dock="Bottom"/>
<Button Content="Dock=Left"/>
<Button Content="Dock=Right" DockPanel.Dock="Right"/>
<Button Content="LastChildFill=True"/>
</DockPanel>

Multiple elements on one side


The dock panel layout supports multiple elements on one side. Just add two or more elements with
the same dock side. The panel simply stacks them.

<DockPanel LastChildFill="True">
<Button Content="Dock=Left"/>
<Button Content="Dock=Left"/>
<Button Content="Dock=Top" DockPanel.Dock="Top"/>
<Button Content="Dock=Bottom" DockPanel.Dock="Bottom"/>
<Button Content="Dock=Right" DockPanel.Dock="Right"/>
<Button Content="LastChildFill=True"/>
</DockPanel>

Change the stacking order

The order of the elements matters. It determines the alignment of the elements. The first elements
gets the whole width or height. The following elements get the remaining space.

Canvas Panel
Introduction

The Canvas is the most basic layout panel in WPF. It's child elements are positioned by explicit
coordinates. The coordinates can be specified relative to any side of the panel usind
the Canvas.Left, Canvas.Top, Canvas.Bottom and Canvas.Right attached properties.

The panel is typically used to group 2D graphic elements together and not to layout user interface
elements. This is important because specifing absolute coordinates brings you in trouble when you
begin to resize, scale or localize your application. People coming from WinForms are familiar with this
kind of layout - but it's not a good practice in WPF.

<Canvas>
<Rectangle Canvas.Left="40" Canvas.Top="31" Width="63" Height="41"
Fill="Blue" />
<Ellipse Canvas.Left="130" Canvas.Top="79" Width="58" Height="58"
Fill="Blue" />
<Path Canvas.Left="61" Canvas.Top="28" Width="133" Height="98"
Fill="Blue"
Stretch="Fill" Data="M61,125 L193,28"/>
</Canvas>

Override the Z-Order of Elements

Normally the Z-Order of elements inside a canvas is specified by the order in XAML. But you can
override the natural Z-Order by explicity defining a Canvas.ZIndex on the element.
<Canvas>
<Ellipse Fill="Green" Width="60" Height="60" Canvas.Left="30"
Canvas.Top="20"
Canvas.ZIndex="1"/>
<Ellipse Fill="Blue" Width="60" Height="60" Canvas.Left="60"
Canvas.Top="40"/>
</Canvas>

Wrap Panel

Introduction

The wrap panel is similar to the StackPanel but it does not just stack all child elements to one row, it
wraps them to new lines if no space is left. The Orientation can be set
to Horizontal or Vertical.
The WrapPanel can be used to arrange tabs of a tab control, menu items in a toolbar or items in an
Windows Explorer like list. The WrapPanel is often used with items of the same size, but its not a
requirement.

<WrapPanel Orientation="Horizontal">
<Button Content="Button" />
<Button Content="Button" />
<Button Content="Button" />
<Button Content="Button" />
<Button Content="Button" />
</WrapPanel>

How to use the ViewBox in WPF

Introduction

The ViewBox is a very useful control in WPF. If does nothing more than scale to fit the content to the
available size. It does not resize the content, but it transforms it. This means that also all text sizes
and line widths were scaled. Its about the same behavior as if you set the Stretch property on an
Image or Path to Uniform.

Although it can be used to fit any type of control, it's often used for 2D graphics, or to fit a scalable
part of a user interface into an screen area.
<Button Content="Test" />

<Viewbox Stretch="Uniform">
<Button Content="Test" />
</Viewbox>
How to create a Custom Layout Panel in WPF
WPF already ships with a rich set of layout panels. Each of them fits to solve a particular problem.

Canvas - To arrange elements by X,Y coordinates.


Grid - To arrange elements based on rows and columns.
StackPanel - To stack elements horizontally or vertically.
DockPanel - To dock elements to a particular side.
WrapPanel - To stack elements and automatically begin a new row.

But sometimes none of the included layout panels helps you to arrange child elements in the way you
like it. In this case its easy to write your own layout panel. All you need to do is to create a new class
that derives from Panel. There are two methods to override:

MeasureOverride - to determine the required size of the panel according to the desired size
of the child elements and the available space.
ArrangeOverride - to arrange the elements in the finally available space. The final size can be
smaller than the requested.

public class MySimplePanel : Panel


{
// Make the panel as big as the biggest element
protected override Size MeasureOverride(Size availableSize)
{
Size maxSize = new Size();

foreach( UIElement child in InternalChildern)


{
child.Measure( availableSize );
maxSize.Height = Math.Max( child.DesiredSize.Height,
maxSize.Height);
maxSize.Width= Math.Max( child.DesiredSize.Width,
maxSize.Width);
}
}

// Arrange the child elements to their final position


protected override Size ArrangeOverride(Size finalSize)
{
foreach( UIElement child in InternalChildern)
{
child.Arrange( new Rect( finalSize ) );
}
}
}
DataBinding in WPF

Introduction

WPF provides a simple and powerful way to auto-update data between the business model and the
user interface. This mechanism is called DataBinding. Everytime when the data of your business model
changes, it automatically reflects the updates to the user interface and vice versa. This is the preferred
method in WPF to bring data to the user interface.

Databinding can be unidirectional (source -> target or target <- source), or bidirectional (source <-
> target).

The source of a databinding can be a normal .NET property or a DependencyProperty. The target
property of the binding must be a DependencyProperty.

To make the databinding properly work, both sides of a binding must provide a change
notification that tells the binding when to update the target value. On normal .NET properties this is
done by raising the PropertyChanged event of the INotifyPropertyChanged interface. On
DependencyProperties it is done by the PropertyChanged callback of the property metadata

Databinding is typically done in XAML by using the {Binding} markup extension. The following
example shows a simple binding between the text of a TextBox and a Label that reflects the typed
value:

<StackPanel>
<TextBox x:Name="txtInput" />
<Label Content="{Binding Text, ElementName=txtInput,
UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>

DataContext
Every WPF control derived from FrameworkElement has a DataContext property. This property
is meant to be set to the data object it visualizes. If you don't explicity define a source of a binding, it
takes the data context by default.

The DataContext property inherits its value to child elements. So you can set
the DataContext on a superior layout container and its value is inherited to all child elements. This is
very useful if you want to build a form that is bound to multiple properties of the same data object.

<StackPanel DataContext="{StaticResource myCustomer}">


<TextBox Text="{Binding FirstName}"/>
<TextBox Text="{Binding LastName}"/>
<TextBox Text="{Binding Street}"/>
<TextBox Text="{Binding City}"/>
</StackPanel>

ValueConverters

If you want to bind two properties of different types together, you need to use a ValueConverter. A
ValueConverter converts the value from a source type to a target type and back. WPF already includes
some value converters but in most cases you will need to write your own by implementing
the IValueConverter interface.

A typical example is to bind a boolean member to the Visibility property. Since the visibility is an
enum value that can be Visible, Collapsed or Hidden, you need a value converter.

<StackPanel>
<StackPanel.Resources>
<BooleanToVisibilityConverter x:Key="boolToVis" />
</StackPanel.Resources>

<CheckBox x:Name="chkShowDetails" Content="Show Details" />


<StackPanel x:Name="detailsPanel"
Visibility="{Binding IsChecked,
ElementName=chkShowDetails,
Converter={StaticResource boolToVis}}">
</StackPanel>
</StackPanel>

The following example shows a simple converter that converts a boolen to a visibility property. Note
that such a converter is already part of the .NET framework.

public class BooleanToVisibilityConverter : IValueConverter


{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
if (value is Boolean)
{
return ((bool)value) ? Visibility.Visible :
Visibility.Collapsed;
}

return value;
}

public object ConvertBack(object value, Type targetType, object


parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}

Tip: you can derive your value converter from MarkupExtension and return its own instance in
the ProvideValueoverride. So you can use it directly without referencing it from the resources.

Another Tip: When you get the error "No constructor for type '...' has 0 parameters.", you need to add
an default constructor to your converter, even it's not needed. Just for the WPF designer.

Data Validation in WPF


What we want to do is a simple entry form for an e-mail address. If the user enters an invalid e-mail
address, the border of the textbox gets red and the tooltip is showing the reason.

Implementing a ValidationRule (.NET 3.0 style)

In this example I am implementing an generic validation rule that takes a regular expression as
validation rule. If the expression matches the data is treated as valid.
/// <summary>
/// Validates a text against a regular expression
/// </summary>
public class RegexValidationRule : ValidationRule
{
private string _pattern;
private Regex _regex;

public string Pattern


{
get { return _pattern; }
set
{
_pattern = value;
_regex = new Regex(_pattern, RegexOptions.IgnoreCase);
}
}

public RegexValidationRule()
{
}

public override ValidationResult Validate(object value, CultureInfo


ultureInfo)
{
if (value == null || !_regex.Match(value.ToString()).Success)
{
return new ValidationResult(false, "The value is not a valid
e-mail address");
}
else
{
return new ValidationResult(true, null);
}
}
}

First thing I need to do is place a regular expression pattern as string to the windows resources

<Window.Resources>
<sys:String x:Key="emailRegex">^[a-zA-Z][\w\.-]*[a-zA-Z0-9]@
[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]
*[a-zA-Z]$</sys:String>
</Window.Resources>

Build a converter to convert ValidationErrors to a multi-line string

The following converter combines a list of ValidationErrors into a string. This makes the binding
much easier. In many samples on the web you see the following binding expression:

{Binding RelativeSource={RelativeSource
Self},Path=(Validation.Errors)[0].ErrorContent}
This expression works if there is one validation error. But if you don't have any validation errors the
data binding fails. This slows down your application and causes the following message in your debug
window:

System.Windows.Data Error: 16 : Cannot get Item[] value (type


ValidationError) from (Validation.Errors) (type
ReadOnlyObservableCollection`1).
BindingExpression:Path=(0).[0].ErrorContent; DataItem=TextBox...

The converter is both, a value converter and a markup extension. This allows you to create and use it at
the same time.

[ValueConversion(typeof(ReadOnlyObservableCollection<ValidationError>),
typeof(string))]
public class ValidationErrorsToStringConverter : MarkupExtension,
IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return new ValidationErrorsToStringConverter();
}

public object Convert(object value, Type targetType, object parameter,


CultureInfo culture)
{
ReadOnlyObservableCollection<ValidationError> errors =
value as ReadOnlyObservableCollection<ValidationError>;

if (errors == null)
{
return string.Empty;
}

return string.Join("\n", (from e in errors


select e.ErrorContent as
string).ToArray());
}

public object ConvertBack(object value, Type targetType, object


parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}

Create an ErrorTemplate for the TextBox

Next thing is to create an error template for the text box.

<ControlTemplate x:Key="TextBoxErrorTemplate" TargetType="Control">


<Grid ClipToBounds="False" >
<Image HorizontalAlignment="Right" VerticalAlignment="Top"
Width="16" Height="16" Margin="0,-8,-8,0"
Source="{StaticResource ErrorImage}"
ToolTip="{Binding ElementName=adornedElement,
Path=AdornedElement.(Validation.Errors),

Converter={k:ValidationErrorsToStringConverter}}"/>
<Border BorderBrush="Red" BorderThickness="1" Margin="-1">
<AdornedElementPlaceholder Name="adornedElement" />
</Border>
</Grid>
</ControlTemplate>

The ValidationRule and the ErrorTemplate in Action

Finally we can add the validation rule to our binding expression that binds the Text property of a
textbox to a EMail property of our business object.

<TextBox x:Name="txtEMail" Template={StaticResource TextBoxErrorTemplate}>


<TextBox.Text>
<Binding Path="EMail" UpdateSourceTrigger="PropertyChanged" >
<Binding.ValidationRules>
<local:RegexValidationRule Pattern="{StaticResource
emailRegex}"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>

How to manually force a Validation

If you want to force a data validation you can manually call UpdateSource() on the binding
expression. A useful scenario could be to validate on LostFocus() even when the value is empty or
to initially mark all required fields. In this case you cann call ForceValidation() in
the Loaded event of the window. That is the time, when the databinding is established.

The following code shows how to get the binding expression from a property of a control.

private void ForceValidation()


{
txtName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
How to Solve Execution Problems of RoutedCommands in a
WPF ContextMenu

The Problem

I recently run into a problem, with RoutedCommands in a ContextMenu. The problem was, that
the commands could not be executed, even if the CommandBinding on the parent window allowed
it.

The following example shows the problem with simple window that has a Menu and a ContextMenu
on it. Both menus contains a MenuItem with a "Cut" command set.

<Window x:Class="RoutedCommandsInPopups.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel x:Name="stack" Background="Transparent">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Cut" Command="Cut" />
</ContextMenu>
</StackPanel.ContextMenu>
<Menu>
<MenuItem Header="Edit" >
<MenuItem Header="Cut" Command="Cut" />
</MenuItem>
</Menu>
</StackPanel>
</Window>

In the codebehind of the Window I added a CommandBinding to handle the "Cut" command.
public Window1()
{
InitializeComponent();

CommandBindings.Add(
new CommandBinding(ApplicationCommands.Cut, CutExecuted, CanCut));
}

private void CutExecuted(object sender, ExecutedRoutedEventArgs e)


{
MessageBox.Show("Cut Executed");
}

private void CanCut(object sender, CanExecuteRoutedEventArgs e)


{
e.CanExecute = true;
}

The Reason

The reason is, that ContextMenus are separate windows with their own VisualTree and LogicalTree.

The reason is that the CommandManager searches for CommandBindings within the current focus
scope. If the current focus scope has no command binding, it transfers the focus scope to the parent
focus scope. When you startup your application the focus scope is not set. You can check this by
calling FocusManager.GetFocusedElement(this) and you will receive null.

The Solution

Set the Logical Focus

The simplest solution is to initially set the logical focus of the parent window that is not null. When
the CommandManager searches for the parent focus scope it finds the window and handels the
CommandBinding correctly.

public Window1()
{
InitializeComponent();

CommandBindings.Add(
new CommandBinding(ApplicationCommands.Cut, CutExecuted, CanCut));

// Set the logical focus to the window


Focus();
}

...or the same in XAML


<Window x:Class="RoutedCommandsInPopups.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FocusManager.FocusedElement="
{Binding RelativeSource={x:Static RelativeSource.Self},
Mode=OneTime}>
...
</Window>

Manually bind the CommandTarget

Another solution is to manually bind the CommandTarget to the parent ContextMenu.

<MenuItem Header="Cut" Command="Cut" CommandTarget="


{Binding Path=PlacementTarget,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}}"/>

How to create a simple WPF multitouch foto viewer

Create a new WPF 4.0 solution.

public class TouchImage : Image


{
private MatrixTransform _matrixTransform;

public TouchImage()
{
ManipulationMode = ManipulationModes.All;
_matrixTransform = new MatrixTransform();
RenderTransform = _matrixTransform;
}

protected override void OnManipulationDelta(ManipulationDeltaEventArgs


e)
{
var manipulation =
e.GetCumulativeManipulation((IInputElement)Parent);

var delta = e.GetDeltaManipulation((IInputElement)Parent);


Matrix matrix = _matrixTransform.Matrix;

var originalCentre = new Point(ActualWidth / 2, ActualHeight / 2);


matrix.Translate(delta.Translation.X, delta.Translation.Y);
matrix.Transform(originalCentre);

var centre = matrix.Transform(originalCentre);


matrix.RotateAt(delta.Rotation, centre.X, centre.Y);
centre = matrix.Transform(originalCentre);
matrix.ScaleAt(delta.Scale, delta.Scale, centre.X, centre.Y);

_matrixTransform.Matrix = matrix;
e.Handled = true;

base.OnManipulationDelta(e);
}
}

Mouse Handling in WPF

How to get the Absolute Position of the Mouse on the Screen

You can call Mouse.GetPosition(this) on any WPF element. This function returns the relative
offset of the mouse to the upper left corner of your control.

To get the absolute screen cordinates, call the PointToScreen() function.

Point absoluteScreenPos = PointToScreen( Mouse.GetPosition( new Point(),


this ));

Control Templates

Introduction

Controls in WPF are separated into logic, that defines the states, events and properties and template,
that defines the visual appearance of the control. The wireup between the logic and the template is
done by DataBinding.

Each control has a default template. This gives the control a basic appearance. The default template is
typically shipped together with the control and available for all common windows themes. It is by
convention wrapped into a style, that is identified by value of the DefaultStyleKey property that
every control has.
The template is defined by a dependency property called Template. By setting this property to
another instance of a control template, you can completely replace the appearance (visual tree) of a
control.

The control template is often included in a style that contains other property settings. The following
code sample shows a simple control template for a button with an ellipse shape.

<Style x:Key="DialogButtonStyle" TargetType="Button">


<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Ellipse Fill="{TemplateBinding Background}"
Stroke="{TemplateBinding BorderBrush}"/>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<Button Style="{StaticResource DialogButtonStyle}" />


A Button without and with a custom control template

ContentPresenter

When you create a custom control template and you want to define a placeholder that renders the
content, you can use the ContentPresenter. By default it adds the content of
the Content property to the visual tree of the template. To display the content of another property
you can set the ContentSource to the name of the property you like.

Triggers

{RelativeSource TemplatedParent} not working in DataTriggers of a


ControlTemplate

If you want to bind to a property of a property on your control like Data.IsLoaded you cannot use a
normal Trigger, since it does not support this notation, you have to use a DataTrigger.

But when you are using a DataTrigger, with {RelativeSource TemplatedParent} it will not
work. The reason is, that TemplatedParent can only be used within the ControlTemplate. It is not
working in the Trigger section. You have to use the {RelativeSource Self} instead.

What if a Binding working or a Setter is not applied when using a control


template

There is something you need to know when setting a value of an element within a control template:
The value does have a lower precendence as the local value! So if you are setting the local value in the
constructor of the contained element, you cannot override it within the controltemplate. But if you use
the element directly in your view, it will work. So be aware of this behavior!.

Here you can find more information about DependencyProperty value precendence: Dependency
Property Value Precedence

Introduction to Styles in WPF

Introduction

Imagine you want to create an application with a unique design. All your buttons should have an
orange background and an italic font. Doing this the conventional way means that you have to set
the Background and the FontStyle property on every single button.

<StackPanel Orientation="Horizontal" VerticalAlignment="Top">


<Button Background="Orange" FontStyle="Italic"
Padding="8,4" Margin="4">Styles</Button>
<Button Background="Orange" FontStyle="Italic"
Padding="8,4" Margin="4">are</Button>
<Button Background="Orange" FontStyle="Italic"
Padding="8,4" Margin="4">cool</Button>
</StackPanel>

This code is neither maintainable nor short and clear. The solution for this problem are styles.

The concept of styles let you remove all properties values from the individual user interface elements
and combine them into a style. A style consists of a list of setters. If you apply this style to an element
it sets all properties with the specified values. The idea is quite similar to Cascading Styles Sheets (CSS)
that we know from web development.

To make the style accessible to your controls you need to add it to the resources. Any control in WPF
have a list of resources that is inherited to all controls beneath the visual tree. That's the reason why
we need to specify a x:Key="myStyle" property that defines a unique resource identifier.

To apply the style to a control we set the Style property to our style. To get it from the resources we
use the {StaticResource [resourceKey]} markup extension.

<Window>
<Window.Resources>
<Style x:Key="myStyle" TargetType="Button">
<Setter Property="Background" Value="Orange" />
<Setter Property="FontStyle" Value="Italic" />
<Setter Property="Padding" Value="8,4" />
<Setter Property="Margin" Value="4" />
</Style>
</Window.Resources>

<StackPanel Orientation="Horizontal" VerticalAlignment="Top">


<Button Style="{StaticResource myStyle}">Styles</Button>
<Button Style="{StaticResource myStyle}">are</Button>
<Button Style="{StaticResource myStyle}">cool</Button>
</StackPanel>
</Window>

What we have achieved now is

A maintainable code base


Removed the redundancy
Change the appearance of a set of controls from a single point
Possibility to swap the styles at runtime.

Style inheritance

A style in WPF can base on another style. This allows you to specify a base style that sets common
properties and derive from it for specialized controls.

<Style x:Key="baseStyle">
<Setter Property="FontSize" Value="12" />
<Setter Property="Background" Value="Orange" />
</Style>

<Style x:Key="boldStyle" BasedOn="{StaticResource baseStyle}">


<Setter Property="FontWeight" Value="Bold" />
</Style>

Designtime attributes

To improve the design experience, Microsoft provides special designtime attributes that can be added
to any WPF element and serve as a hint for the design tool.
The designtime attributes are defined in a special namespace, that is usually mapped to the d: prefix.
To tell the XAML parser not to interprete these attributes at runtime, the markup compatibility
namespace is mapped to mc: and with the mc:Ignorable="d" instruction, the d: namespace is
excluded.

<Window
xmlns:d ="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" />

d:DesignHeight and d:DesignWidth

The d:DesignHeight and d:DesignWidth sets a fixed height and width for the element at
designtime

<UserControl
xmlns="http://schemas.microsoft.com/..."
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
d:DesignWidth="640" d:DesignHeight="480" >
<UserControl />

d:LayoutOverrides

If a property is set to a fixed value at runtime, but you want to override it at designtime, you can use
the d:LayoutOverrides attribute. All properties that should be ignored at designtime can be listed,
separated by a semicolon.

<Border Height="250" Width="160" d:LayoutOverrides="Width, Height" >


</Border>

Drag and Drop in WPF

Introduction

Drag&Drop can drasticly improve the productiviy and user experience of a software. But only a few
programmers provide drag and drop functionality in their applications, because they think its much
more dificult than it really is. This article shows how simple drag and drop can be implemented in WPF.
Drag&Drop in 6 Steps

1. Detect a drag as a combinatination of MouseMove and MouseLeftButtonDown


2. Find the data you want to drag and create a DataObject that contains the format, the data
and the allowed effects.
3. Initiate the dragging by calling DoDragDrop()
4. Set the AllowDrop property to True on the elements you want to allow dropping.
5. Register a handler to the DragEnter event to detect a dragging over the drop location. Check
the format and the data by calling GetDataPresent() on the event args. If the data can be
dropped, set the Effect property on the event args to display the appropriate mouse cursor.
6. When the user releases the mouse button the DragDrop event is called. Get the data by
calling the GetData()method on the Data object provided in the event args.

...and that's all the magic.

Drag

To start the drag operation, we have to detect a mouse move while the left mouse button is pressed.
To do this we have to hook up handlers on
the PreviewMouseMove and PreviewMouseLeftButtonDown events.

To prevent occasionally drags, its a good design to not start the drag operation until the user has
moved the mouse cursor by a couple of pixels. WPF provides a constant that contains the amount of
pixel that Windows uses.

When the drag is initiated, we need to specify the data we want to drag. In our case its the data of
the ListViewItem we dragged. We find the ListViewItem in the OriginalSource of the mouse
event args. By calling ItemContainerGenerator.ItemFromContainer we get the data behind
the ListViewItem.

Create a DataObject to transport the data to the drop location. The constructor takes two
arguments. A string that describes the format and the data we want to drag.

<ListView x:Name="DragList"
PreviewMouseLeftButtonDown="List_PreviewMouseLeftButtonDown"
PreviewMouseMove="List_MouseMove"/>

private void List_PreviewMouseLeftButtonDown(object sender,


MouseButtonEventArgs e)
{
// Store the mouse position
startPoint = e.GetPosition(null);
}
private void List_MouseMove(object sender, MouseEventArgs e)
{
// Get the current mouse position
Point mousePos = e.GetPosition(null);
Vector diff = startPoint - mousePos;

if (e.LeftButton == MouseButtonState.Pressed &&


Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance
||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance )
{
// Get the dragged ListViewItem
ListView listView = sender as ListView;
ListViewItem listViewItem =

FindAnchestor<ListViewItem>((DependencyObject)e.OriginalSource);

// Find the data behind the ListViewItem


Contact contact = (Contact)listView.ItemContainerGenerator.
ItemFromContainer(listViewItem);

// Initialize the drag & drop operation


DataObject dragData = new DataObject("myFormat", contact );
DragDrop.DoDragDrop(listViewItem, dragData, DragDropEffects.Move);
}
}

// Helper to search up the VisualTree


private static T FindAnchestor<T>(DependencyObject current)
where T : DependencyObject
{
do
{
if( current is T )
{
return (T)current;
}
current = VisualTreeHelper.GetParent(current);
}
while (current != null);
return null;
}

Drop

To make an element be a drop location, set the AllowDrop property to true. When the user drags an
item over the element, the DragEnter event is called. In this event you can analyze the data and
decide if a drop is allowed or not.
When the user releases the mouse button the Drop event is called. The data is available in
the DataObject provided in the DragEventArgs.

<ListView x:Name="DropList"
Drop="DropList_Drop"
DragEnter="DropList_DragEnter"
AllowDrop="True" />

private void DropList_DragEnter(object sender, DragEventArgs e)


{
if (!e.Data.GetDataPresent("myFormat") ||
sender == e.Source)
{
e.Effects = DragDropEffects.None;
}
}

private void DropList_Drop(object sender, DragEventArgs e)


{
if (e.Data.GetDataPresent("myFormat"))
{
Contact contact = e.Data.GetData("myFormat") as Contact;
ListView listView = sender as ListView;
listView.Items.Add(contact);
}
}

Behaviors

A simple Border can be dragged by mouse - because of an attached drag behavior.


Introduction

Behaviors are a new concept, introduced with Expression Blend in Version 3, to encapsulate pieces of
functionality into a reusable component. These components than can be attached to controls to
give them an additional behavior.

The ideas behind behaviors are to give the interaction designer more flexibility to design complex user
interactions without writing any code.

Example of a behaviors are drag&drop, input validation, pan and zoom, re-position of elements, etc...
The list of possible behaviors is very long.

Imaging an application that has a list of customers and the user can add some of them to subscriber
lists. This interaction can be designed by providing an "Add" button next to each subscriber list. But if
the interaction designer wants to add drag&drop functionality, he needs to discuss it with the
developer and wait until the implementation is done. With behaviors he just drags a drag and drop
behavior on each list and we are done.

How to use behaviors in Expression Blend 3

Using behaviors in Expression Blend is as simple as adding an element to the design surface. In the
asset library you find a new secion called "Behaviors". It lists all behaviors available within your project.
Just grab one of these and drag it onto the element you want to add this behavior and thats it.

The behavior appears as an child element in the visual tree. By clicking on it you can configure the
properties of the behavior.
How does it work

To add behaviors to an element you need some kind of an extension point. This is an attached
property called Interaction.Behaviors.
This attached property holds the list of behaviors for that element and pass a reference to the element
into the behavior. The behavior than can register itself to events and property changes and so extend
the functionality of the element.

The idea is simple, but very clever. They don't need any new infrastructure, they just reuse the existing
one.

<Border Background="LightBlue" >


<e:Interaction.Behaviors>
<b:DragBehavior/>
</e:Interaction.Behaviors>
<TextBlock Text="Drag me around!" />
</Border>

How to implement your own behavior

The following example shows the implementation of the drag behavior we used above. Just derive
from Behavior<T;gt; and override the OnAttached() method.

public class DragBehavior : Behavior<UIElement>


{
private Point elementStartPosition;
private Point mouseStartPosition;
private TranslateTransform transform = new TranslateTransform();

protected override void OnAttached()


{
Window parent = Application.Current.MainWindow;
AssociatedObject.RenderTransform = transform;

AssociatedObject.MouseLeftButtonDown += (sender, e) =>


{
elementStartPosition = AssociatedObject.TranslatePoint( new
Point(), parent );
mouseStartPosition = e.GetPosition(parent);
AssociatedObject.CaptureMouse();
};

AssociatedObject.MouseLeftButtonUp += (sender, e) =>


{
AssociatedObject.ReleaseMouseCapture();
};

AssociatedObject.MouseMove += (sender, e) =>


{
Vector diff = e.GetPosition( parent ) - mouseStartPosition;
if (AssociatedObject.IsMouseCaptured)
{
transform.X = diff.X;
transform.Y = diff.Y;
}
};
}
}

Using the ZoomBehavior in Visual Studio


Adding the magnifier can also be done directly in XAML, without using Blend 3:

Download my Utilities assembly and the Microsoft Expression Interactivity assembly. These
assemblies are in a Zip file. Extract them to a local folder.
In Visual Studio, add a reference to both assemblies to your WPF application
o Right click on the References folder
o Select Add Reference from the context menu
o Browse to the folder in which you saved both assemblies above, and select them. Then
press OK.
In XAML, add two XML namespaces to the Window tag:

xmlns:i="http://schemas.microsoft.com/expression/2009/interactivity"
1 xmlns:zoom="clr-
2 namespace:GalaSoft.Utilities.Wpf.Zoom;assembly=GalaSoft.Utilities"

Add the following attached property to the element you want to attach the magnifier to (in that
case, the Rectangle named TestRectangle).
Note: Typically, you want to add this behavior to the LayoutRoot panel (the first element under
the main window). Even if you add the ZoomBehavior to a child element somewhere in the
window (like in this sample), the magnifier will be added to the windows Content element, and
will be visible in the whole window.

1 <Rectangle x:Name="TestRectangle"
Fill="#FF821A32"
2 Stroke="#FF000000"
3 HorizontalAlignment="Right"
4 Margin="0,260,17,354"
5 Width="179">
6
7 <i:Interaction.Behaviors>
<zoom:ZoomBehavior x:Name="MyZoom" />
8 </i:Interaction.Behaviors>
9
10 </Rectangle>
11
12

Here too, to toggle the magnifier on/off, you need to add the namespace
GalaSoft.Utilities.Wpf.Zoom to your code-behind, and then call the extension method
ToggleZoom() on the element to which you added the behavior. For example:

1 TestRectangle.ToggleZoom();

And of course here too you can use a binding on the IsVisible property

<i:Interaction.Behaviors>
1 <zoom:ZoomBehavior x:Name="MyZoom"
2 IsVisible="{Binding
3 ElementName=ToggleZoomCheckBox,
4 Path=IsChecked,
Mode=TwoWay}" />
5 </i:Interaction.Behaviors>
Using properties
There are a number of properties that you can use to customize the magnifiers look and feel. All these
properties are Dependency Properties, which means that they can be data bound or animated, such as in
the sample that you can download below.

Zoom factor
The property ZoomFactor can be set to any value between 1.0 and Double.MaxValue. The default value
is 2.0.

Magnifier height and width


The properties MagnifierWidth and MagnifierHeight govern the dimensions of the magnifying glass.
Default value: 200 px * 200 px.

Distance from the mouse


When the magnifier is visible, the mouse cursor turns into a cross. The magnifier appears next to the
cursor, either left or right, below or above, depending on the mouse position. If you move the mouse too
fast, the cursor might land on the magnifier, which you want to avoid. In that case, and depending on
the shape of the magnifier, you may want to increase the distance from the mouse, using the
DistanceFromMouse property. Default value: 5 pixels.

Magnifier shape
By default, the magnifier is an Ellipse. You can however specify any shape you want, using the Template
property.
Create a ControlTemplate, either in Blend or in Visual Studio.
The ControlTemplate must target a Control (TargetType property).
One of the elements of the ControlTemplate must set its Fill (or Background) property to a
TemplateBinding on the Controls Background. This is where the magnified view will appear.
Save this template in the windows resources (for example).
Set the ZoomBehaviors Template property to the saved ControlTemplate.

For instance:

1
<ControlTemplate x:Key="ZoomGlassTemplate1"
2
TargetType="{x:Type Control}">
3 <Grid Background="Silver">
4 <Rectangle Fill="{TemplateBinding Background}"
5 Stroke="#FF000000"
6 StrokeThickness="5"
StrokeDashArray="1 2" />
7 </Grid>
8 </ControlTemplate>
9

and

1 <i:Interaction.Behaviors>
2 <zoom:ZoomBehavior x:Name="MyZoom"
3 Template="{StaticResource ZoomGlassTemplate1}"
4 DistanceFromMouse="20" />
</i:Interaction.Behaviors>
5

How to automatically crop an image

The following method allows you to automatically crop an image to it's content.

public static ImageSource AutoCropBitmap(BitmapSource source)


{
if (source == null)
throw new ArgumentException("source");

if (source.Format != PixelFormats.Bgra32)
source = new FormatConvertedBitmap(source,
PixelFormats.Bgra32, null, 0);

int width = source.PixelWidth;


int height = source.PixelHeight;
int bytesPerPixel = source.Format.BitsPerPixel / 8;
int stride = width * bytesPerPixel;

var pixelBuffer = new byte[height * stride];


source.CopyPixels(pixelBuffer, stride, 0);

int cropTop = height, cropBottom = 0, cropLeft = width, cropRight = 0;

for (int y = 0; y < height; y++)


{
for (int x = 0; x < width; x++)
{
int offset = (y * stride + x * bytesPerPixel);
byte blue = pixelBuffer[offset];
byte green = pixelBuffer[offset + 1];
byte red = pixelBuffer[offset + 2];
byte alpha = pixelBuffer[offset + 3];

//TODO: Define a threshold when a pixel has a content


bool hasContent = alpha > 10;

if (hasContent)
{
cropLeft = Math.Min(x, cropLeft);
cropRight = Math.Max(x, cropRight);
cropTop = Math.Min(y, cropTop);
cropBottom = Math.Max(y, cropBottom);
}
}
}

return new CroppedBitmap(source,


new Int32Rect(cropLeft, cropTop, cropRight - cropLeft,
cropBottom - cropTop));
}

How to flip an image horizontally or vertically

The easiest way to flip an image in WPF is to use a TransformedBitmap. It's a wrapper around
a BitmapImage that takes a generic Transform object.

var flippedImage = new TransformedBitmap(originalImage, new


ScaleTransform( -1,1));

How to use FlowDocuments

How to save or load a FlowDocument embedded images

There is a way to save and load a flow document including all embedded images by putting them into
a XamlPackage. The following sample shows how to do this:
// Save
var source = new FlowDocument();
var range = new TextRange(source.ContentStart, source.ContentEnd);
using (var stream = File.Create("output.pak"))
{
range.Save(stream, DataFormats.XamlPackage);
}

// Load
using(var stream = File.OpenRead("output.pak"))
{
var target = new FlowDocument();
var range = new TextRange(target.ContentStart, target.ContentEnd);
range.Load(stream, DataFormats.XamlPackage);
}

How to navigate by hyperlinks

If you have a FlowDocument that contains Hyperlinks and you want to perform some action when the
user clicks on the link, you can use hook up to the
RoutedEvent Hyperlink.RequestNavigateEvent. The following code snippet shows you how to
do it:

<Window x:Class="HyperlinksInFlowDocument.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<FlowDocumentReader>
<FlowDocument>
<Paragraph>
<Hyperlink
NavigateUri="http://www.google.ch">Google</Hyperlink>
</Paragraph>
</FlowDocument>
</FlowDocumentReader>
</Grid>
</Window>

public Window1()
{
InitializeComponent();
AddHandler(Hyperlink.RequestNavigateEvent, new
RoutedEventHandler(OnNavigationRequest));
}

public void OnNavigationRequest(object sender, RoutedEventArgs e)


{
var source = e.OriginalSource as Hyperlink;
if (source != null)
Process.Start(source.NavigateUri.ToString());
}

S-ar putea să vă placă și