Windows Presentation Foundation
- Separation UI / Behaviour
- MVVM
- Intelligent Layout
- Scalable Graphics (vectors != rasters)
- Templates:
- Control: how a control looks like
- Data: how data are rendered
- Binding
- Styling
- 3D
- Triggers
- Declarative change of state
- Animation
- Managed:
- Presentation Framework
- Presentation Core
- Unmanaged:
- MIL Core
- OS:
- DirectX
Can be either compiler or interpreted
Declarative UI (XAML: eXtensible Application Markup Language)
Like namespaces in .NET, scope for unique names, can also be used to map (alias) a local name to the actual namespace.
Examples
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
: maps the overall WPF client / framework XAML namespace as the default, allow to use WPF elements without the prefix.xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
: maps a separate XAML namespace, mapping it (typically) to thex:
prefix.xmlns:custom="clr-namespace:SDKSample;assembly=SDKSampleLibrary"
: a library containing a class, and a reference to it in project settingsx:Class
: Partial class declaration which connects the markup to the partial class code defined behind.
<Page x:Class="WPFApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:SDKSample;assembly=SDKSampleLibrary">
<!-- [...] -->
<custom:ExampleClass/>
<!-- [...] -->
</Page>
Instance of anobject and attributes are properties of the object instance. x:name
not a property but defines a variable (name). If sometimes shorthand not enough (complex type), can use property element syntax as a child element.
Example
<!-- Shorthand syntax -->
<Button Content = "Click Me" Height = "30" Width = "60" />
<!-- Property Element Syntax -->
<Button>
<Button.Content>Click Me</Button.Content>
<Button.Height>30</Button.Height>
<Button.Width>60</Button.Width>
</Button>
Can be used for text or other thing.
Example
<Button Content="Click Me" />
<Button>
<TextBlock Text="Click Me" />
</Button>
Markup extensions are a XAML technique for obtaining a value that is neither a primitive nor a specific XAML type.
Basically: when value are outside of the scope of XAML process using { ... }
Interfaces
public interface IMarkupExtension
{
object ProvideValue(IServiceProvider serviceProvider);
}
public interface IMarkupExtension<out T> : IMarkupExtension
{
new T ProvideValue(IServiceProvider serviceProvider);
}
Example
namespace XAMLMarkupExtension
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class MyMarkupExtension : MarkupExtension
{
public MyMarkupExtension() { }
public String FirstStr { get; set; }
public String SecondStr { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return FirstStr + " " + SecondStr;
}
}
}
<Window x:Class = "XAMLMarkupExtension.MainWindow"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my = "clr-namespace:XAMLMarkupExtension"
Title = "MainWindow" Height = "350" Width = "525">
<Grid>
<Button Content = "{my:MyMarkupExtension FirstStr = Markup, SecondStr = Extension}" Width = "200" Height = "20" />
</Grid>
</Window>
Example
<Window.Resources>
<SolidColorBrush Color = "Blue" x:Key = "myBrush">
</SolidColorBrush>
</Window.Resources>
<TextBlock
Foreground = "{StaticResource myBrush}"
Text = "First Name"
/>
Defers a property value to be a data-bound value, creating an intermediate expression object and interpreting the data context that applies to the element and its binding at run time.
Types:
Default
OneWay
TwoWay
OneWayToSource
OneTime
Example
public partial class MainWindow : Window {
Person person = new Person { Name = "Salman", Age = 26 };
public MainWindow()
{
InitializeComponent();
this.DataContext = person;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
string message = person.Name + " is " + person.Age;
MessageBox.Show(message);
}
}
<TextBox Text = "{Binding Name, Mode = OneWay}"/>
Provides a value for any XAML property attribute by deferring that value to be a reference to a defined resource.
Example
<Button>
<Button.Background>
<SolidColorBrush Color="{DynamicResource {x:Static SystemColors.DesktopColorKey}}" />
</Button.Background>
Hello
</Button>
Links the value of a property in a control template to be the value of another property on the templated control.
Example
<Border Padding="{Binding Padding, RelativeSource={RelativeSource TemplatedParent}" ...>
can be simplified into:
<Border Padding="{TemplateBinding Padding}" ...>
Specifies null as a value for a XAML member.
Example
<Label FontFamily="{x:Null}" />
References any static by-value code entity that is defined in a Common Language Specification (CLS)–compliant way.
Example
<SolidColorBrush Color="{x:Static SystemColors.ControlColor}" />
Provides general support for arrays of objects.
Example
<ListView Margin="10">
<ListView.ItemsSource>
<x:Array Type="{x:Type Color}">
<Color>Black</Color>
<Color>Blue</Color>
<Color>Green</Color>
<Color>Red</Color>
<Color>White</Color>
<Color>Yellow</Color>
</x:Array>
</ListView.ItemsSource>
</ListView>
An attached property is intended to be used as a type of global property that is settable on any object.
One purpose of an attached property is to allow different child elements to specify unique values for a property that is actually defined in a parent element.
Example
Simple attached property
<DockPanel>
<CheckBox DockPanel.Dock="Top">Hello</CheckBox>
</DockPanel>
<Canvas Canvas.Left="120">
<Rectangle Canvas.ZIndex="99" Height="60" Width="60" Fill="Gold"/>
<Rectangle Canvas.ZIndex="98" Height="50" Width="70" Fill="Coral"/>
</Canvas>
or in the behind code
var myDockPanel = new DockPanel();
var myCheckBox = new CheckBox();
myCheckBox.Content = "Hello";
myDockPanel.Children.Add(myCheckBox);
DockPanel.SetDock(myCheckBox, Dock.Top);
Dependency Registration
public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached(
"IsBubbleSource",
typeof(Boolean),
typeof(AquariumObject),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender)
);
public static void SetIsBubbleSource(UIElement element, Boolean value)
{
element.SetValue(IsBubbleSourceProperty, value);
}
public static Boolean GetIsBubbleSource(UIElement element)
{
return (Boolean)element.GetValue(IsBubbleSourceProperty);
}
Object
:System.Object
...DispatcherObject
: object that have only accessed on the thread they created itDependencyObject
: objects supporting dependency properties- ^
Freezable
: read-only state objects, can be copied to non-readonly objects (cannot unfreeze), eg. graphic primitives like bruses, pens, geometries, animations
- ^
Visual
: objects that have their own 2D representationUIElement
: visual objects supporting routed events, command binding, layouts and focusFrameworkElement
: add support for styles, data bindings, resources and vertical and horizontal aligments, plus tooltips and context menuControl
: addsForeground
,Background
,FontSize
, etc. properties
- ^
Visual3D
: see 2D version...UIElement3D
: same
- ^
Orientation
:
Horizontal
Vertical
Example
<StackPanel Orientation="Horizontal">
<Button MinWidth="93">OK</Button>
<Button MinWidth="93" Margin="10,0,0,0">Cancel</Button>
</StackPanel>
DockPanel.Dock
:
Top
Bottom
Left
Right
Also: LastChildFill
Example
<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>
Grid.RowDefinitions
:
RowDefinition
Grid.ColumnDefinitions
:ColumnDefinition
Height
or Width
:
Fixed
Auto
*
Example
<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>
Orientation
:
Horizontal
Vertical
Example
<WrapPanel Orientation="Horizontal">
<Button Content="Button" />
<Button Content="Button" />
</WrapPanel>
Canvas.
:
Left
Right
Example
<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>
(Inherits from FrameworkElement
)
Always 1 child
Draw a border, a background, or even both, around another element.
Example
<Border Background="GhostWhite" BorderBrush="Gainsboro" BorderThickness="1">
<StackPanel Margin="10">
<Button>Button 1</Button>
<Button Margin="0,10">Button 2</Button>
<Button>Button 3</Button>
</StackPanel>
</Border>
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
.
Example
<Button Content="Test" />
<Viewbox Stretch="Uniform">
<Button Content="Test" />
</Viewbox>
Specify how to draw the bullet itself and decorate what ever you need.
Example
<BulletDecorator>
<BulletDecorator.Bullet>
<Polygon Margin=” 2, 0, 0, 0″ Points=”0, 5 5, 0 10, 5 5, 10″ Fill=” Blue” />
<BulletDecorator.Bullet>
<TextBlock Margin=”10, 0, 0, 0″ Text=”This is a bullet” />
<BulletDecorator>
Inherits from ContentControl
, a base class that enables the display of almost any UI imaginable.
Supports:
- access keys
DataTemplate
viaContentTemplate
- Any kind of content
- Custom Control => Template Properties
On the other hand, inherits directly from FrameworkElement
, thus missing out on the behavior that is common to all elements inheriting from Control. The shallow inheritance hierarchy of TextBlock
makes the control lighter weight than Label and better suited for simpler, noninteractive scenarios.
Supports:
- Only strings
- Bold and / or Italic
Allows user to edit formatted text, images, tables, or other rich content.
Requires less system resources than above and it is ideal when only plain text needs to be edited (ie. forms).
Event that invokes handlers on multiple listeners in element tree (instead of the just the source element).
Source Element => Parent
Parent => Source Element
Like windows but supporting class handler (static equivalent)
Command is any class which implements ICommand
interface. There is a minor difference between Commands and Events. Events are defined and associated with UI Controls. But Commands are more abstract and focused on what to be done. One Command can be associated with multiple UI Controls/Options.
ICommand Interface
public interface ICommand
{
bool CanExecute(object parameter);
void Execute(object parameter);
event EventHandler CanExecuteChanged;
}
Relay Command Implementation
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
this._execute = execute;
this._canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this._canExecute == null || this._canExecute(parameter);
}
public void Execute(object parameter)
{
this._execute(parameter);
}
}
Example
<StackPanel>
<Menu>
<MenuItem Command="ApplicationCommands.Paste" />
</Menu>
<TextBox />
</StackPanel>
// Creating the UI objects
var mainStackPanel = new StackPanel();
var pasteTextBox = new TextBox();
var stackPanelMenu = new Menu();
var pasteMenuItem = new MenuItem();
// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);
// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;
// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;
Model-View - ViewModel
- View: the XAML or equivalent implementation, Control Derived (window, page, data template[no code behind])
- ViewModel: often set to the
DataContext
of the View. Does not know the view per say tho. Usually implements either:INotifiyPropertyChanged
INotifyCollectionChanged
IDateErrorInfo
INotifyDataErrorInfo
- Model: where the business logic goes (ie. business domain), reference onto ViewModel
Different from MVC and MVVM (mainly due to the databinding possibilities).
Resources which you cannot manipulate at runtime. The static resources are evaluated only once by the element which refers them during the loading of XAML.
Resources which you can manipulate at runtime and are evaluated at runtime. If your code behind changes the resource, the elements referring resources as dynamic resources will also change.
Specifies the visual structure and visual behaviour of a control.
Example
<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 very flexible and powerful solution to replace the visual appearance of a data item in a control like ListBox
, ComboBox
or ListView
.
If you don't specify a data template, WPF takes the default template that is just a TextBlock
. If you bind complex objects to the control, it just calls ToString()
on it. Within a DataTemplate
, the DataContext
is set the data object. So you can easily bind against the data context to display various members of your data object
Example
<!-- Without DataTemplate -->
<ListBox ItemsSource="{Binding}" />
<!-- With DataTemplate -->
<ListBox ItemsSource="{Binding}" BorderBrush="Transparent"
Grid.IsSharedSizeScope="True"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Key" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBox Grid.Column="1" Text="{Binding Value }" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
It would be better if we could switch the editor depending on the type of the property.
The simplest way to do this is to use a DataTemplateSelector
. The DataTemplateSelector
has a single method to override
: SelectTemplate(object item, DependencyObject container)
. In this method we decide on the provided item which DataTemplate
to choose.
Example
public class PropertyDataTemplateSelector : DataTemplateSelector
{
public DataTemplate DefaultnDataTemplate { get; set; }
public DataTemplate BooleanDataTemplate { get; set; }
public DataTemplate EnumDataTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var dpi = item as DependencyPropertyInfo;
if (dpi.PropertyType == typeof(bool))
{
return BooleanDataTemplate;
}
if (dpi.PropertyType.IsEnum)
{
return EnumDataTemplate;
}
return DefaultnDataTemplate;
}
}
<Window x:Class="DataTemplates.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:DataTemplates"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Window.Resources>
<!-- Default DataTemplate -->
<DataTemplate x:Key="DefaultDataTemplate">
...
</DataTemplate>
<!-- DataTemplate for Booleans -->
<DataTemplate x:Key="BooleanDataTemplate">
...
</DataTemplate>
<!-- DataTemplate for Enums -->
<DataTemplate x:Key="EnumDataTemplate">
...
</DataTemplate>
<!-- DataTemplate Selector -->
<l:PropertyDataTemplateSelector x:Key="templateSelector"
DefaultnDataTemplate="{StaticResource DefaultDataTemplate}"
BooleanDataTemplate="{StaticResource BooleanDataTemplate}"
EnumDataTemplate="{StaticResource EnumDataTemplate}"/>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding}" Grid.IsSharedSizeScope="True"
HorizontalContentAlignment="Stretch"
ItemTemplateSelector="{StaticResource templateSelector}"/>
</Grid>
</Window>
Powerful way to auto-update data between the business model and the user interface
Requirements:
- 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 theINotifyPropertyChanged
interface. - On DependencyProperties it is done by the
PropertyChanged
callback of the property metadata
- On normal .NET properties this is done by raising the
Example
<StackPanel>
<TextBox x:Name="txtInput" />
<Label Content="{Binding Text, ElementName=txtInput, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
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.
Example
<StackPanel DataContext="{StaticResource myCustomer}">
<TextBox Text="{Binding FirstName}"/>
<TextBox Text="{Binding LastName}"/>
<TextBox Text="{Binding Street}"/>
<TextBox Text="{Binding City}"/>
</StackPanel>
To bind two properties of different types together, you need to use a ValueConverter
.
Example
<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>
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();
}
}
Controls that inherit from ItemsControl
which has a ItemsSource
property.
Example
<Window x:Class="CollectionViewFilteringDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CollectionViewDemo"
Height="300"
Width="450">
<Grid Background="DarkGreen">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock FontWeight="Bold" Foreground="White" FontSize="20">Products</TextBlock>
<ListBox Grid.Row="1" ItemsSource="{Binding Products}" DisplayMemberPath="Name"/>
<TextBlock Grid.Column="1" FontWeight="Bold" Foreground="White"
FontSize="20">Options</TextBlock>
<ListBox Grid.Column="1" Grid.Row="1" ItemsSource="{Binding ProductOptions}"
DisplayMemberPath="Option"/>
</Grid>
</Window>
A collection view is a wrapper around a collection that provides the following additional features:
- Navigation
- Sorting
- Filtering
- Grouping
Example
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ListBox ItemsSource={Binding Customers} />
</Window>
public class CustomerView
{
public CustomerView()
{
DataContext = new CustomerViewModel();
}
}
public class CustomerViewModel
{
private readonly ICollectionView _customerView;
public ICollectionView Customers
{
get { return _customerView; }
}
public CustomerViewModel()
{
var customers = GetCustomers();
_customerView = CollectionViewSource.GetDefaultView(customers);
}
}
Values fired by INotifyCollectionChanged
are not reliably marshalled, need to leverage either:
Dispatcher.BeginInvoke
Dispatcher.Invoke
- awaitable
Dispatcher.InvokeAsync
Use the DispatcherSynchronizationContext
(wrapper around Dispatcher
).
Beware SynchronizationContext.Current
is the current one (current thread so it can be null
).
<TextBlock Background="Yellow" Text="{Binding RelativeSource={RelativeSource Self},Path=ActualWidth}"/>
<TextBlock Background="Yellow" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=ActualWidth}"/>