"Nationalism always breaks its promises because nationalists hate enemies in their countries more than they hate the enemies of their countries. Millions of American conservatives proved it when they voted for Donald Trump, even though he was an open admirer of a hostile foreign power."

— Nick Cohen in The Guardian

In 2013, when Obama proposed a military response to Assad's chemical weapon attacks, Donald Trump, Mitch McConnell, Paul Ryan, and other Republican hypocrites opposed the move. Suddenly it's 2017, and the same double-dealing scam artists need a distraction from the Russia scandal and their healthcare incompetence, so they're all for it.

WPF Notes

Framework notes — WPF 4.5 — XAML 2006

WPF Notes

Compiled by Jeremy Kelly
www.anthemion.org

These are my personal WPF notes, covering WPF 4.5 and XAML 2006. They are here primarily for my convenience, but you are welcome to use them, subject to the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. If you find a mistake, please let me know.

Most example code follows the Split Notation.

This page includes a two-column print style. For best results, print in landscape, apply narrow margins, and change the Scale setting in your browser's print options to 60%.

Contents

I am still compiling these notes. Check back later for more.

XAML

XAML (Extensible Application Markup Language) is an XML-derived language that documents the configuration of objects and object hierarchies, particularly UI controls. Most XAML elements represent object instances that will be instantiated at run time by calling default constructors. The relative structure of XAML elements defines parent-child relationships among the objects. The attributes for a given element often represent values to be assigned to the object's properties:


<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/
    presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="nMain.tWinCd"
  Title="Enter code">

  <StackPanel>
    <TextBox Name="EdCd"/>
    <Button Name="BtnOK" Click="BtnOK_Click" Content="OK"/>
  </StackPanel>
</Window>

The newest version is XAML 2009, which can be read manually with the System.Xaml assembly introduced in .NET 4.0. The Visual Studio XAML Designer only supports XAML 2006, as do the code-behind classes produced by Visual Studio.

It is possible to instantiate generic classes with XAML, and, when this is done, the x:TypeArguments attribute is used to specify type arguments for the generic. In XAML 2006, only root elements can be generic. To use a generic class elsewhere in the hierarchy, create a subclass that passes type arguments to the generic in its inheritance list, and reference that in the XAML.

Namespaces

An XML namespace is declared by assigning a Uniform Resource Identifier (URI) to the xmlns attribute. In XML, namespaces are used to resolve name conflicts, which occur if data from different sources in the same file use the same name in different ways. In XAML, they are used to associate element and attribute names with classes and class properties in some library, particularly the .NET Framework. This namespace, for example, allows elements like Window and Button to be mapped to their associated classes:


xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

In this case, the URI does not represent a web resource, it is simply a unique name that is known to the WPF assemblies. The XAML root element must declare at least one namespace so the root itself can be created. Namespaces declared in one element are usable in that element's children too.

Appending a colon and an alternative name:


xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

produces a prefix that can be attached to names in the contained XAML:


<Button x:Name="BtnStop" Content="Stop"/>

WPF uses assembly attributes to associate namespaces with URIs. Assemblies that were not designed for WPF will not have this attribute, but they can still be used in XAML by prefixing the .NET namespace with clr-namespace:


xmlns:coll="clr-namespace:System.Collections;assembly=mscorlib"

The assembly name must be appended to the namespace unless the .NET namespace happens to reside in the assembly containing the XAML.

Type converters

All XAML attribute values are strings. Most properties have non-string types, so .NET defines numerous type converters that convert these strings to other types. In this case:


<Button x:Name="BtnReady" Content="Ready" Background="Gray"/>

the BrushConverter class converts the Background attribute to a Brush instance. This is equivalent to:


BtnReady.Background = System.Windows.Media.Brushes.Gray;

Type conversion is enabled in the backing class by adding the TypeConverter attribute to the property itself, or to the type that is stored in the property. Custom type converters are created by subclassing the TypeConverter class.

Property elements

If the type converter for a given property produces a parent class that is overly generic, or if it is necessary to initialize the object, the property can be set with a property element. This is a sub-element with a name that references the containing type and the name of the targeted property:


<Button x:Name="BtnReady" Content="Ready">
  <Button.Background>
    <LinearGradientBrush>
      <GradientStop Color="LightGray" Offset="0.0"/>
      <GradientStop Color="Gray" Offset="1.0"/>
    </LinearGradientBrush>
  </Button.Background>
</Button>

This example constructs a LinearGradientBrush instance and assigns it to the button's Background property, represented by the Button.Background element.

Markup extensions

Complex objects and special values like null can be assigned to properties with markup extensions. To invoke a markup extension, enclose the name of the extension class and zero or more comma-delimited parameters within curly braces:


BorderBrush="{x:Null}"

Markup extensions can accept positional parameters or named parameters. Positional parameters correspond to string arguments in a constructor defined by the extension class. In this case, local:tWinMain.sTextAct is passed to the constructor:


Content="{x:Static local:tWinMain.sTextAct}"

Named parameters, which consist of name/value pairs, assign values to properties in the class. The value portion of the pair can itself invoke a type converter or another markup extension:


Content="{Binding Path=Title}"

Markup extension classes can also be used as property elements:


<Button.BorderBrush>
  <x:Null/>
</Button.BorderBrush>
<Button.Content>
  <x:Static Member="local:tWinMain.sTextAct"/>
</Button.Content>

All parameters must be named when extension classes are used this way.

Markup extensions are created by subclassing from MarkupExtension. It is customary for the name of such a class to end with Extension, and, when this is done, the word Extension can be omitted from the extension name in XAML.

Object element children

A XAML element representing an object can contain a child that is a type converter value, a Content value, or a collection instance.

If a type converter can convert the contained string to an instance of the containing type, that converter will be invoked. In this example, a converter creates a Cursor instance and assigns it to the Cursor property:


<Button.Cursor>Hand</Button.Cursor>

with this being is equivalent to:


Cursor="Hand"

Many classes use an attribute to designate one property as the content property, with this often (but not always) being named Content. This supports the assignment of complex objects that could not be defined in an attribute:


<Button>
  <Image Source="IconPrev.png"/>
</Button>

Children can also be used to populate a collection. If the collection implements IList, and if an empty collection instance has already been assigned to the collection property (as all WPF controls do by default), then the contained instances will be added:


<ComboBox x:Name="BoxDef">
  <ComboBox.Items>
    <ComboBoxItem>Start</ComboBoxItem>
    <ComboBoxItem>Restart</ComboBoxItem>
  </ComboBox.Items>
</ComboBox>

If the collection property happens to be the object's content property, the property can be omitted:


<ComboBox x:Name="BoxDef">
  <ComboBoxItem>Start</ComboBoxItem>
  <ComboBoxItem>Restart</ComboBoxItem>
</ComboBox>

Collection members cannot be added this way if the collection property does not already contain an instance, though they can be added to a new instance defined in the XAML, which is then assigned to the property.

If the collection implements IDictionary, the members must be associated with keys. Instead of wrapping each value instance with another class, this is done with the XAML Key keyword:


<Application.Resources>
  <Image x:Key="Begin" Source="IconBegin.png"/>
  <Image x:Key="Other" Source="IconOther.png"/>
</Application.Resources>

Element names

Many WPF classes derive from FrameworkElement or FrameworkContentElement, both of which implement FindName. This method can be used to locate and reference instances generated by the XAML:


var oqEdRank = oqWin.FindName("EdRank") as TextBox;

Names can be assigned to XAML elements in one of two ways. The FrameworkElement and FrameworkContentElement classes both define a Name property:


Name="EdRank"

In classes that do not provide such a property, the name can be assigned with the XAML Name keyword:


x:Name="EdRank"

Both approaches produce the same functionality, but only one should be used for a given instance.

Element names can also be referenced from other parts of the XAML. The Binding markup extension uses the element name to support data binding:


Target="{Binding ElementName=EdRank}"

Certain type converters also use the name:


Target="EdRank"

Using XAML

In Visual Studio, adding a new window unit to a C# project produces two files: a XAML file, and a C# code-behind file with the same name, plus the xaml.cs extension. The code-behind is nested under the XAML file in the Solution Explorer. It contains a partial class that derives from Window:


namespace nMain {
  partial class tWin: Window {
    public tWin() {
      InitializeComponent();
    }
    ...

The Window element in the XAML includes a Class attribute that references the code-behind class:


x:Class="nMain.tWin"

This must be a subclass of the XAML element type.

When the application is built, its XAML is compiled into a compact form called BAML (Binary Application Markup Language) that is stored as a resource in the containing assembly. The build also generates a C# file with the g.cs extension within obj/Debug or obj/Release. This file completes the partial definition of the code-behind class:


namespace nMain {
  partial class tWin: Window, IComponentConnector {
    internal System.Windows.Controls.Button BtnReady;

    public void InitializeComponent() {
      ...

    public void IComponentConnector.Connect(int aIDConn, object
      aqTarg) {
      ...

In particular, it implements the InitializeComponent method called from the constructor in the code-behind file. This method reads the resource BAML and instantiates the objects therein. Note that event handlers are attached to objects before properties are set, so that handlers can respond to property changes during construction. The generated definition also creates references for all named objects (including controls) contained by the window. References are associated with instances in the Connect method, which is called as the BAML is interpreted. The references are internal by default. To use a different access level, add x:FieldModifier attributes to the elements.

The code-behind class is assumed to be public. All partial definitions must have the same access level, so the generated definition is made public by default. To change the access level of the generated definition, add the x:ClassModifier attribute to the root node.

If WPF is used with a language without partial classes, a Subclass attribute can be added to the Window element. The generated class will take its name from Class, and the code-behind class will inherit from that class, while taking its name from Subclass.

XAML can also be read or written manually with the XamlReader and XamlWriter classes. To instantiate the structure from a XAML stream:


Window cWinFromXAML(Stream aqStm) {
  var oqWin = XamlReader.Load(aqStm) as Window;
  ...

WPF Infrastructure

WPF (Windows Presentation Foundation) is a framework that produces themable, resolution-independent user interfaces. It is rendered with Direct3D. Most WPF classes derive from the following 'core' classes:

WPF base classes
WPF base classes

DispatcherObject manages a work queue for the UI thread, somewhat like the message loop in a Win32 application. Aside from the Freezable subclass and its children, instances should not be accessed outside the UI thread.

DependencyObject supports dependency properties, covered below.

Visual is used to implement 2D controls. UIElement adds layout, control focus, event routing, and command binding functionality. FrameworkElement adds resource, data binding, and style support, along with tooltip and context menu functionality. Control adds additional styling features.

Visual3D and UIElement3D support 3D controls.

ContentElement offers many of the functions in UIElement, but it is used to create text elements, so it also supports text flow and wrapping. FrameworkContentElement adds some of the functionality in FrameworkElement. Neither class renders itself, so instances must be associated with one of the Visual classes.

Freezable classes support frozen and unfrozen states. When frozen, instances become read-only and (unlike other WPF instances) accessible to multiple threads. This class is typically used to implement graphics primitives.

In WPF, reference is sometimes made to logical and visual trees. A logical tree is a parent/child hierarchy that shows the instances explicitly created by the developer, whether in XAML or procedural code. A visual tree shows the instances that render those elements. Many logical tree instances will also be found in the visual tree, but some will not, and many visual tree instances will be absent from the logical tree, since they are created and contained by the logical instances.

Dependency properties

A dependency property is a .NET property backed by a DependencyProperty instance:


class tPanStat: Control {
  public static readonly DependencyProperty CkEdProperty
    = DependencyProperty.Register("CkEd", typeof(bool),
      typeof(tPanStat), new PropertyMetadata(false));

  public bool CkEd {
    get { return (bool)GetValue(CkEdProperty); }
    set { SetValue(CkEdProperty, value); }
  }
  ...

It is used to implement data binding, UI styles, and animations, among other things. The DependencyProperty is expected to be public and static, its name must end with Property, and it should be readonly.

The DependencyProperty is instantiated with one of the static Register methods, which accept an instance of PropertyMetadata or one of its subclasses. Metadata can be used to:

  • Specify a default value for the property;
  • Pass a callback that coerces input values to a valid range;
  • Pass a callback that validates input, throwing if it is invalid;
  • Pass a callback that responds to property changes;
  • Configure other behavior, such as property value inheritance.

Metadata can be changed in a subclass by calling OverrideMetadata from the property instance within the subclass's static constructor.

The .NET property is known as the property wrapper. The getter passes the DependencyProperty to GetValue, while the setter passes it to SetValue; these methods are implemented by DependencyObject, from which the containing class must derive. The property wrapper is called by the XAML compiler, but WPF calls GetValue and SetValue directly, so the wrapper must do nothing more than call those functions.

Value Precedence

When a dependency property is read, possible values are drawn from many sources. WPF starts by finding the base value, which it selects from the object tree. In order of precedence, this value is one of:

  1. The local value, produced by writing to the dependency property with its wrapper or with SetValue. A local value can be removed by passing the DependencyProperty instance to the ClearValue function in DependencyObject.
  2. The parent template value. A template is used to replace the object tree that implements a given control. If the dependency property is attached to an element within such a tree, the value may be drawn from the control that uses the template, or from a trigger attached to that control.
  3. The style value. A style is a collection of property values that can be applied to multiple control instances. The value can be drawn from triggers attached to the style, triggers attached to templates within the style, or from style setters.
  4. The default style or theme style value. Every WPF control has a default style that defines its basic appearance. Because this style varies with the Windows theme, it is also known as the theme style. The value can be drawn from the style, or from a trigger attached to the style.
  5. The inherited value. As explained below, an attached dependency property can be used to associate a value with an instance that does not itself define the property. Attached properties can also be configured to 'inherit' values from parent elements in the XAML tree, as FontSize does.
  6. The default value specified in the dependency property metadata.

After the base value is selected, it is replaced or modified by animations, if any target the property being read. The animated value is coerced and validated, if such callbacks have been defined, before finally being returned.

Attached properties

An attached property is one that can associate values with elements other than the one that defines the property. In XAML, this allows a value that is assigned to a parent element to be used by its children, even though the property is not found in the parent:


<StackPanel TextElement.FontFamily="Arial">
  ...

In this case, assigning a font in the StackPanel changes the font in its child elements that use the TextElement class, even though StackPanel does not define a font property.

Conversely, attached properties allow values assigned to children to be used by a parent:


<DockPanel>
  <Label Name="LblMain" DockPanel.Dock="Top">FRAME 1/A</Label>
  ...

In this case, specifying a dock position in the Label changes the way the containing Dock lays out its children, even though Label does not define such a property.

In the XAML, the property attribute is prefixed with the class that defines the attached property. This class is called the attached property provider. In general, an attached property allows the instance that invokes it to say something about itself to the provider.

The provider implements the attached property by creating a dependency property with the RegisterAttached function:


class tJob: DependencyObject {
  ...

class tMgrRun {
  public static readonly DependencyProperty WgtRunProperty
    = DependencyProperty.RegisterAttached(
      "WgtRun",
      typeof(float),
      typeof(tMgrRun),
      new PropertyMetadata(1.0F)
    );
  ...

It then defines public static accessor functions that begin with Get or Set, and end with the property name:


public static float GetWgtRun(tJob aqJob) {
  return (float)aqJob.GetValue(WgtRunProperty);
}

public static void SetWgtRun(tJob aqJob, float aWgt) {
  aqJob.SetValue(WgtRunProperty, aWgt);
}

Both functions accept an instance of the class to which the property will be attached. Because this class is used to call GetValue and SetValue, it must derive from DependencyObject. The Set function is called when the XAML is processed, and it can be used to set the property procedurally as well. A property wrapper can also be added to allow use as a regular dependency property.

A dependency property must be implemented as an attached property if it is to 'inherit' values from parent elements in the XAML tree. When this is done, a default should be specified to ensure that a value is always available.

Routed events

Much like the dependency property, a routed event is a .NET event backed by a RoutedEvent instance:


class tSwitch: Control {
  public static readonly RoutedEvent FlipEvent
    = EventManager.RegisterRoutedEvent("Flip",
      RoutingStrategy.Bubble, typeof(RoutedEventHandler),
      typeof(tSwitch));

  public event RoutedEventHandler Flip {
    add { AddHandler(FlipEvent, value); }
    remove { RemoveHandler(FlipEvent, value); }
  }

  void OnFlip(tArgsEventFlip aqArgs) {
    RaiseEvent(new RoutedEventArgs(FlipEvent, this));
  }
  ...

Routed events are more flexible than ordinary .NET events, allowing event signals to pass up or down through XAML element hierarchies.

The class that defines and invokes the event is called the event sender or event source. The class that implements the handler is called the event listener. The RoutedEvent instance is expected to be public and static, its name must end with Event, and it should be readonly as well. It is instantiated with the RegisterRoutedEvent function in EventManager. This function accepts the RoutingStrategy enumeration, which defines how the event travels through a XAML hierarchy:

  • Bubbling causes the event to travel up, starting with the source element;
  • Tunneling causes the event to travel down, starting with the XAML root element, and ending with the source;
  • Direct causes the event to be handled only at the source, much like a traditional .NET event.

The .NET event is called an event wrapper, and it allows handlers to be added with operator+= and removed with operator-=, like other events. The 'adder' passes the RoutedEvent to AddHandler, while the 'remover' passes it to RemoveHandler. These functions are implemented by UIElement and ContentElement, so the event sender must derive from one of them.

Raising an event on some instance entails checking that instance for a handler, and invoking the handler if it is found. By convention, the method that does this has a name begins with On, followed by the event name. In an ordinary .NET event, this function confirms that the handler delegate is non-null, and then invokes it; in WPF, a similar process is started by calling RaiseEvent, though the handler may be in another part of the object tree.

RoutedEventArgs is the base class for all routed event data, an instance of which is passed to RaiseEvent. This class offers the following properties:

  • RoutedEvent stores the event instance;
  • OriginalSource stores the visual tree element where the event originated. This is the instance that called RaiseEvent;
  • Source stores the logical tree element where the event originated;
  • Handled indicates whether the event has been handled.

The Bubbling or Tunneling process continues after Handled has been set to true, though, in most cases, no more elements are checked for handlers. One of the AddHandler overloads includes a handledEventsToo parameter that allows a handler to receive events that have already been handled elsewhere.

UI input is generally signaled with Bubbling events. In WPF, some Bubbling events are preceeded by preview events, which are Tunneling events with names that begin with Preview. Preview events allow parent controls to modify event data or handle events preemptively, since the same RoutedEventArgs instance is passed to handlers of both types.

In XAML, a handler is attached by specifying the event name as an attribute, with the handler name as its value. Like the event sender, the event listener must be a subclass of UIElement or ContentElement:


<Canvas MouseLeftButtonDown="eTrack">
  ...

In the handler, the control that catches the event is passed as the sender:


private void eTrack(object aqSend,
  MouseButtonEventArgs aqArgs) {
  ...

Attached events

Any routed event can also be used as an attached event, which is one that is handled from an instance that does not also define it. In XAML, this allows a single handler in a parent element to process events from a number of descendents:


<StackPanel CheckBox.Click="eUpd">
  <CheckBox x:Name="cCkNone" Content="None"/>
  <CheckBox x:Name="cCkRnd" Content="Random"/>
  <CheckBox x:Name="cCkLast" Content="Last"/>
</StackPanel>

This is useful when implementing controls, since the events generated by various subsidiary elements can be handled in one place.

The event sender maintains a strong reference to the listener, so handlers must be removed from the event if the listener is to be garbage-collected. Alternatively, handlers can be added with weak references by calling the AddHandler method in the generic WeakEventManager class.

Class handlers

Specifying a handler with XAML (or in procedural code with the event wrapper) creates an instance listener, which functions only for that instance. An event listener that subclasses DependencyObject can also implement class listeners, which function within every instance of the class. This can only be done in procedural code.

A class listener invokes a static class handler:


static protected void csOnFlipClass(object aqSend,
  RoutedEventArgs aqArgs) {
  ...

The class handler is added to the event by calling RegisterClassHandler within the event listener's static constructor:


static tSwitch() {
  EventManager.RegisterClassHandler(typeof(tSwitch),
    FlipEvent, new RoutedEventHandler(csOnFlipClass));
  ...

Class handlers can be added to any routed event. If the event meets an instance of the registered class — whether it was raised there or in another part of the XAML hierarchy — the class handlers will be invoked before any instance handler that might also be defined. Different handlers can be defined by different classes within the class hierarchy, and, when this is done, handlers in the most-derived classes are invoked first. Because the class handler is static, it cannot manipulate the instance except through the sender or event data parameters.

Many UIElement classes provide virtual methods that have names beginning with OnPreview or On. These methods are invoked by a class handler in the base class, and overriding them allows class handling functionality to be implemented without registering a new handler. When this is done, the base implementation should be called even if the override marks the event handled.

Application instance

The base Application class includes properties and methods such as:

static Current
Returns the application instance.
MainWindow
Gets or sets the main window.
Properties
Returns a dictionary storing arbitrary key/value pairs.
Resources
Gets or sets a ResourceDictionary instance that stores application resources.
ShutdownMode
Ordinarily, the application ends when the last window is closed, or when Application.Shutdown is explicitly invoked. Setting this property to OnMainWindowClose causes it to end when the main window is closed. Setting it to OnExplicitShutdown causes it to run until Shutdown is called.
subclass

Layout

Distance units

Windows uses its DPI setting to determine the number of physical pixels constituting one logical inch. Originally, the Windows DPI was fixed at 96, but it can now be changed to a percentage of that value. Because the physical pixel size is constant for a given display, increasing the DPI causes the logical inch to increase in physical size.

The default unit in XAML is the device-independent pixel (DIP), which is 1/96 of a logical inch. This unit is explicitly specified by appending px to the attribute value. Logical inches or centimeters are specified with in or cm. Points are specified with pt, with each of these spanning 1/72 of a logical inch. The Windows DPI may or may not match the actual pixel density of the display, but when it does, logical inches are found to equal physical inches, point sizes on the display match sizes in print, and DIPs correspond directly to physical pixels.

Physical pixel sizes are calculated by finding the size in logical pixels and then multiplying by the DPI:

physical pixels = logical inches · DPI

Therefore, to convert device-independent pixels:

physical pixels = DIPs / 96 · DPI

To convert points:

physical pixels = points / 72 · DPI

Sizing controls

In general, WPF controls size themselves to fit their own content. Sizes can be explicitly set with Width and Height, but if the content changes size unexpectedly, it could be truncated. These properties have default values of Double.NaN, which can be represented in XAML as NaN, though Auto is preferred. The properties do not generally return the actual size within the layout.

Sizes can be constrained with MinWidth, MaxWidth, MinHeight, and MaxHeight. The Max sizes have default values of Double.PositiveInfinity, which can be represented in XAML with Infinity.

DesiredSize is a read-only property that is used by containers during layout. RenderSize is also read-only, and it gives the actual size in the layout, as do ActualWidth and ActualHeight. These properties should not be read outside the LayoutUpdated event, however.

Margins and padding

Margin and Padding are Thickness properties that control spacing outside and inside a control. The Thickness structure stores one, two, or four doubles. In XAML, multiple values are specified with a comma-delimited list:


Padding="8,8,24,8"

Unlike CSS, the first value specifies the left measurement, or the side measurements if two values are given. Only one or four values can be passed to the Thickness constructor in procedural code.

Margins can be negative, but padding values cannot.

Control visibility

The Visibility property determines whether the control is visible and whether it participates in the layout. The associated Visibility enumeration has three values:
  • Visible causes the control to be rendered and laid-out as usual;
  • Hidden prevents the control from being rendered, but does not remove it from the layout;
  • Collapsed prevents the control from being rendered and removes it from the layout.

Control alignment

The HorizontalAlignment and VerticalAlignment properties affect a control's position within the space allotted by its parent, as well as its dimensions, if they have not been explicitly set. Each property accepts an enumeration of the same name. In both properties, the default value, Stretch, causes the control to fill the space. In HorizontalAlignment, Left, Center, and Right size the control to its own content, and align it within the space. In VerticalAlignment, Bottom, Center, and Top do the same.

The HorizontalContentAlignment and VerticalContentAlignment properties affect the content within the control in like manner, and they use the same enumerations.

The FlowDirection property facilitates the use of right-to-left scripts, like Arabic. When set to RightToLeft, the Left and Right alignment values reverse their effect. Many other properties and controls also reverse the horizontal axis when RightToLeft is set. FlowDirection does not itself change the direction of text.

Content overflow

A control's ClipToBounds property determines whether content is truncated at the control's boundaries, or whether it is allowed to overflow. This property is found in all UIElement subclasses, but a number of panels clip regardless of its value. Canvas does allow clipping to be disabled, and other controls can be made to disable clipping by placing a Canvas between them and their children in the object hierarchy.

Scrolling content

Any control can be made to scroll by placing it inside a ScrollViewer instance. The VerticalScrollBarVisibility and HorizontalScrollBarVisibility properties in this class accept members of the ScrollBarVisibility enumeration:

  • Visible causes the scrollbar to be displayed at all times;
  • Auto causes the scrollbar to appear only when it is needed;
  • Hidden prevents the scrollbar from appearing, but it allows scrolling with the keyboard;
  • Disabled hides the scrollbar and disables keyboard scrolling.

When a given scrollbar is Disabled, the viewer's content receives as much space along that axis as the viewer itself has. When any other value is set, the content receives as much space as it requests.

Some controls use ScrollViewer instances internally, and their scrollbars can be configured by invoking VerticalScrollBarVisibility and HorizontalScrollBarVisibility as attached properties.

Scaling content

A control can be scaled by placing it inside a Viewbox instance. Unlike ScaleTransform, which scales the control to some fraction of its original size, Viewbox scales it to fit the box. The control can be made to fit in different ways, according to the Stretch property, which accepts a Stretch enumeration:

  • None leaves the content unscaled;
  • Fill scales the content to fit the width and height of the box. No whitespace will be produced, but the content's aspect ratio may change;
  • Uniform sets the content to the largest size that fits entirely inside the box without changing the content's aspect ratio. The box will not be completely filled unless the aspect ratios match;
  • UniformToFill scales the content to fill the entire box without changing the content aspect ratio. If the aspect ratios do not match, some content will overflow the box.

Scaling is also affected by the StretchDirection property, which accepts a StretchDirection enumeration. This determines whether the content can be scaled UpOnly, DownOnly, or in Both directions, as is the default.

Transforms

WPF controls provide two Transform properties that can be used to alter control orientation, shape, and position. LayoutTransform is implemented in FrameworkElement, and its transform is applied before the layout. RenderTransform is implemented in UIElement, and its transform applies after, leaving the layout to reflect the original, untransformed size and position. Both properties can be set and used together.

Though all FrameworkElement instances provide the transform properties, controls that display non-WPF content may not implement them completely.

Rotation transform

Specific operations are represented by subclasses of Transform. The RotateTransform class rotates the control by Angle degrees:


<Button x:Name="BtnLaunch" Click="BtnLaunch_Click">
  <Button.RenderTransform>
    <RotateTransform Angle="-90"/>
  </Button.RenderTransform>
  Launch
</Button>

The CenterX and CenterY properties in this transform set the DIP position about which the rotation occurs. This point can also be set with the RenderTransformOrigin property in the control. Unlike CenterX and CenterY, this property accepts a Point bearing unit coordinates, which range from [0.0, 0.0] in the top-left corner to [1.0, 1.0] in the bottom-right. This is useful when the control size is unknown.

The text inside the control can be rotated by replacing it with a TextBlock:


<Button x:Name="BtnLaunch" Click="BtnLaunch_Click">
  <TextBlock RenderTransformOrigin="0.5,0.5">
    <TextBlock.RenderTransform>
      <RotateTransform Angle="-90" CenterX="0" CenterY="0"/>
    </TextBlock.RenderTransform>
    Launch
  </TextBlock>
</Button>

The CenterX, CenterY, and RenderTransformOrigin properties are ignored in the layout transform.

Scale transform

The ScaleTransform class provides ScaleX and ScaleY properties that store scaling factors for each dimension. A value of 1.0 leaves the size unchanged. The class also implements the CenterX and CenterY properties, along with support for RenderTransformOrigin. As with RotateTransform, these specify the single, unmoving point about which the transform occurs. If a corner is selected, that point will keep its original position, and the sides opposite the corner will move away to resize the control. If an inside point is selected, all four sides will move away, but the distance they move will vary according to their original distance from the point. As a result, the inside point will maintain its unit coordinate position after the transform.

If ScaleTransform is applied as a LayoutTransform, and if the target element uses the Stretch alignment, the transform will have no effect unless it enlarges the control more than Stretch has done.

Controls typically clip their content, and this clipping is performed during the layout. Therefore, an element that is enlarged as a RenderTransform may not be clipped as expected, and one that is reduced may show evidence of clipping even when the reduced size fits entirely within the parent.

Other transforms

Controls can also be transformed with classes such as:

  • TranslateTransform, which moves the control horizontally and vertically, but only when applied as a RenderTransform;
  • SkewTransform, which changes rectangles into parallelograms;
  • MatrixTransform, which applies an arbitrary matrix transformation.

A set of transforms can be applied by combining them within a TransformGroup instance:


<Button.LayoutTransform>
  <TransformGroup>
    <ScaleTransform ScaleX="0.5" ScaleY="0.5"/>
    <RotateTransform Angle="-90"/>
  </TransformGroup>
</Button.LayoutTransform>

Panels

Like other controls, the Window class provides a Content property that can be set only once. To display more than one control, a Panel must be added to the window. WPF offers a range of Panel subclasses that arrange controls in different ways.

Canvas

The Canvas panel supports absolute positioning. Controls are placed by assigning offsets to the Left, Right, Bottom, and Top attached properties in Canvas, with each defining a DIP distance from the specified side to the nearest side of the control. Setting an offset also causes the control to be anchored to that side, so that the distance is maintained if the Canvas size changes. Only one side in each left/right or bottom/top pair can be anchored:


<Canvas>
  <Button x:Name="BtnStop" Click="BtnStop_Click"
    Width="80" Height="40"
    Canvas.Right="10" Canvas.Top="10"
    Content="Stop"/>
</Canvas>

If the control has margins on the anchored sides, they will be added to the distance; any other margins will be ignored. Neither HorizontalAlignment nor VerticalAlignment function within a Canvas.

Canvas also implements a ZIndex attached property that controls the z-order of overlapping controls.

Stack panel

A StackPanel displays its children in a horizontal or vertical sequence, this direction being determined by the Orientation property. Each control in a vertical StackPanel receives all the horizontal space in the panel, plus as much vertical space as the control itself requires.

VirtualizingStackPanel is a similar control that derives from VirtualizingPanel. When data binding is used, this panel creates controls as their cells become visible, and releases them as they go out of view, allowing large datasets to be displayed without compromising performance. The ListBox control uses this panel internally.

Wrap panel

When its Orientation is set to Horizontal, a WrapPanel displays its children in a row that wraps to a new line when it reaches the right side of the panel. Additional rows are added until all children have been placed:

Wrap panel
Wrap panel

Setting Orientation to Vertical produces a column that wraps when it reaches the bottom. By default, the controls in a horizontal WrapPanel determine their own widths, while each control's height is set to match the tallest control in its row. Setting ItemWidth or ItemHeight in the panel overrides these sizes, clipping the controls if necessary.

Dock panel

The DockPanel class implements a Dock attached property that allows child controls to attach themselves to the Left, Right, Bottom, or Top side of the panel. The controls follow the sides of the panel when it is resized:


<DockPanel>
  <StackPanel DockPanel.Dock="Top" Height="40"
    Orientation="Horizontal">
    ...
  </StackPanel>
  <TextBlock DockPanel.Dock="Bottom" Height="40">
    ...
  </TextBlock>
  <StackPanel Width="40" DockPanel.Dock="Left">
    ...
  </StackPanel>
  <Canvas>
    ...
  </Canvas>
</DockPanel>

In this case, the child controls have HorizontalAlignment or VerticalAlignment set to Stretch by default, so they expand to cover as much of the panel sides as possible, without covering controls that were docked before them:

Dock panel
Dock panel

The last child fills the remaining space, unless the panel's LastChildFill property is set to false, allowing the control to be docked like other children.

Grid

The Grid panel places controls within the cells of a table. Rows and columns are defined by adding RowDefinition and ColumnDefinition instances to the RowDefinitions and ColumnDefinitions property elements. Child controls are associated with cells by setting the Row and Column attached properties implemented in Grid:


<Grid>
  <Grid.RowDefinitions>
    <RowDefinition/>
    <RowDefinition/>
  </Grid.RowDefinitions>
  <Grid.ColumnDefinitions>
    <ColumnDefinition/>
    <ColumnDefinition/>
  </Grid.ColumnDefinitions>

  <Button x:Name="BtnUp" Grid.Row="0" Grid.Column="0"
    Grid.ColumnSpan="2" Click="BtnUp_Click"/>
  <Button x:Name="BtnLeftDown" Grid.Row="1" Grid.Column="0"
    Click="BtnLeftDown_Click"/>
  <Button x:Name="BtnRightDown" Grid.Row="1" Grid.Column="1"
    Click="BtnRightDown_Click"/>
</Grid>

Rows and columns can be created in the XAML designer by clicking the left and right margins. If no rows are explicity defined, or no columns, one will be created automatically at run time. Controls are made to span multiple cells by setting the ColumnSpan and RowSpan attached properties, also provided by Grid.

Cells can be left empty, or multiple children can be added to the same cell, with later additions appearing above others in the z-order. The Grid class offers no way to style cells, but a cell can be colored by placing a Rectangle instance at the bottom of its z-order, after setting the rectangle's Fill. A border can be defined by setting the Stroke property within the Rectangle, or by placing the Rectangle inside a Border instance.

By default, each row or column receives an equal portion of the grid's height or width. This is changed by setting the Height or Width properties in the row and column definitions. In XAML:

  • Assigning a number produces pixel or absolute sizing, which sets the row or column to a specific DIP size;
  • Assigning Auto produces auto sizing, which sizes the row or column to the largest control it contains;
  • Assigning '*' or a number followed by '*' produces star or proportional sizing. When a single row or column is set to '*', it consumes all space not allocated to absolute or auto-sized elements. When multiple rows or columns are set this way, the available space is split evenly between them. When '*' is prefixed by a number, the allocation is weighted by that number.

In procedural code, the size is set by assigning a GridLength instance to Height or Width. One of the GridLength constructors accepts a GridUnitType enumeration that selects absolute, auto, or proportional sizing.

Grid splitter

Adding a GridSplitter allows cells to be resized at run time. By default, a splitter is displayed in only one cell, yet its effect applies to the entire row or column, so it should always be made to span the grid.

A horizontal splitter can share a row with other controls if its HorizontalAlignment is set to Stretch, and if its VerticalAlignment is set to Bottom or Top, with this also determining the side upon which the splitter operates. When this is done, the splitter's Height must be explicitly set to make it visible, and this height will cover other controls in the row. Similar rules apply to a vertical splitter that shares a column with grid content.

To prevent controls from being covered, it is preferable to give each splitter its own row or column. Both HorizontalAlignment and VerticalAlignment should be set to Stretch, leaving the splitter thickness to be determined by the row or column size:


  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="40"/>
      <RowDefinition Height="5"/>
      <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="40"/>
      <ColumnDefinition Width="5"/>
      <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    ...
    <GridSplitter Grid.Row="1" Grid.Column="0"
      Grid.ColumnSpan="3"
      HorizontalAlignment="Stretch"
      VerticalAlignment="Stretch"/>
    <GridSplitter Grid.Row="0" Grid.Column="1"
      Grid.RowSpan="3"
      HorizontalAlignment="Stretch"
      VerticalAlignment="Stretch"/>
  </Grid>

Multiple rows or columns can be made to change size together by associating them with a size group. First, the IsSharedSizeScope property in the containing Grid must be set to true, then the SharedSizeGroup property in the row and column definitions must be set to a common string that names the group. All rows or columns in the group will start with the same size, equal to the largest absolute or Auto size in the group; proportional sizes will be ignored. The elements will continue to share the same size, even when one of them is resized. A group can contain both rows and columns, and it can even contain and update elements from different grids.

Controls

Window

Most Window events are associated with virtual handlers in the base class, so they are handled by overriding those functions.

The Activated event is raised when a window is focused, and Deactivated is raised when it loses the focus. The Activate method focuses the window programmatically. To prevent the window from being focused when it is shown, set ShowActivate to false.

OnClosing is raised when an attempt is made to close the window. Setting Cancel within the CancelEventArgs parameter to true keeps the window open.

Assigning a window to the Owner property specifies that window as the parent of this one. When the parent is minimized or closed, its children are minimized or closed as well.

User input

Keyboard input

Key presses in UIElement controls are signaled with the KeyDown and KeyUp events, along with Preview variants of the same. Both events receive a KeyEventArgs parameter that describes the input. This class contains many properties, among which:

  • Key returns a Key enumeration that identifies alphanumeric, editing, navigation, function, OEM, and other key types. Number keys in the number pad are distinguished from numbers in the regular keyboard, but navigation keys in the number pad are not. Some keys (like Alt, or Esc, when Ctrl is down) have special functions in Windows, and these are always identified as Key.System. When this happens, the specific key press can be read from the SystemKey property;
  • IsUp and IsDown describe the key's current state. If a key like Caps Lock has been pressed, IsToggled tells whether it is enabled. The KeyStates property returns the KeyStates enumeration, which combines flags that represent these three states;
  • IsRepeat tells whether the event was produced by key repeat;
  • KeyboardDevice returns a KeyboardDevice instance. This class provides functions like IsKeyUp, IsKeyDown, and IsKeyToggled, which give the state for any key, not just the one producing the event. It also provides the Modifiers property, which tells whether any or all of the Shift, Ctrl, Alt, or Windows keys are down.

A KeyboardDevice instance can also be obtained from the PrimaryDevice property in the Keyboard class, within System.Windows.Input. This can be used at any time, even outside of keyboard events.

By default, any UIElement can receive the keyboard focus, whether by being clicked, or tabbed-into. This can be disabled by setting the Focusable property to false. The IsTabStop attached property in KeyboardNavigation allows a control to be removed from the tab order, but if Focusable is true, it will still be possible to focus it with a click. KeyboardNavigation also allows the tab order to be defined by setting TabIndex.

Mouse input

Mouse movements in UIElement controls are signaled with MouseEnter, MouseMove, and MouseLeave events. Handlers for these events receive a MouseEventArgs parameter, within which:

  • LeftButton, MiddleButton, and RightButton indicate whether the mouse buttons are up or down;
  • GetPosition returns the mouse position relative to a control, or relative to the screen, if null is passed to the function;
  • Timestamp gives the time when the event occurred.

The static Mouse class within System.Windows.Input allows mouse data to be queried outside of mouse events, though the position cannot be obtained this way during drag-and-drop operations.

The MouseDown and MouseUp events signal button input. Handlers for these events receive a MouseButtonEventArgs parameter, which adds the following properties to MouseEventArgs:

  • ChangedButton identifies the button that generated the event;
  • ClickCount gives the number of clicks since the last time the double-click timeout expired.

UIElement also provides MouseLeftButtonDown, MouseLeftButtonUp, MouseRightButtonDown, and MouseRightButtonUp events that duplicate some of the MouseDown and MouseUp functionality. Control instances automatically raise MouseDoubleClick events when the left mouse button is double-clicked.

Mouse wheel scrolling is signaled with the MouseWheel event. Handlers for this event receive a MouseWheelEventArgs parameter. This class adds a Delta property to MouseEventArgs that is positive when the wheel is scrolled up, and negative when it is scrolled down. The magnitude can vary with different devices.

UIElement supports drag-and-drop operations that transfer DataObject instances within or between apps. This is implemented with events like DragEnter, DragOver, DragLeave, QueryContinueDrag, and Drop. A UIElement instance can also capture the mouse with its CaptureMouse function. This causes the UIElement to receive all mouse events, even those raised when the pointer is outside its boundaries, until ReleaseMouseCapture is called.

Preview variants are offered for most mouse events. Note that control areas with null Background, Fill, or Stroke properties do not generate mouse events, though Transparent areas do. Also, Canvas can display controls outside of its own Width and Height (both of which default to zero) but it does not generate mouse events outside this area.

Commands

A command is a user action packaged within an instance that implements ICommand. In this interface:

  • CanExecute indicates whether the action is available;
  • CanExecuteChanged signals changes that affect the CanExecute value. This event must be raised by the code that manages those changes;
  • Execute performs the action.

If the command acts upon a specific element, that element is called the command target. The element that initiates the command is the command source, and this typically implements ICommandSource. Within this interface:

  • Command specifies the command to be executed;
  • CommandTarget specifies the command target, or it can be left null to indicate the focused control. If the command is a routed command, event routing will begin at the target; otherwise, this property will be ignored;
  • CommandParameter stores an arbitrary data object to be passed to CanExecute and Execute.

Setting Command in a control like Button or MenuItem causes it to call Execute when it is invoked:


<Button Command="{x:Static local:tWinMain.sCmdSort}"/>

The control also adds a delegate instance to CanExecuteChanged that enables or disables the control, allowing a set of controls that produce the same action to be updated automatically.

Input gestures include mouse clicks and key presses, in combination with optional modifier keys. Like controls, these gestures can serve as command sources. A keyboard gesture is associated with a command by adding a KeyBinding to the listener's InputBindings collection. The Key and Modifiers can be specified separately:


<Window.InputBindings>
  <KeyBinding Key="S" Modifiers="Shift+Control"
    Command="{x:Static local:tWinMain.sCmdSort}"/>
</Window.InputBindings>

or together, as a Gesture:


<Window.InputBindings>
  <KeyBinding Gesture="Shift+Ctrl+S"
    Command="{x:Static local:tWinMain.sCmdSort}"/>
</Window.InputBindings>

Mouse gestures are specified with a MouseBinding.

Routed commands

The RoutedCommand class implements ICommand, but instead of performing the command itself, it causes CanExecute and Execute to raise routed events that can be handled locally or in a parent element. A routed command is defined like a dependency property:


public static readonly RoutedCommand sCmdRest
  = new RoutedCommand();

Command events are associated with handlers by adding a CommandBinding to the event listener:


<Window.CommandBindings>
  <CommandBinding
    Command="{x:Static local:tWinMain.sCmdRest}"
    CanExecute="eCanExecuteRest" Executed="eExecutedRest"/>
</Window.CommandBindings>

The CanExecute handler has a void return type, so it signals command availability by setting the CanExecute property within its CanExecuteRoutedEventArgs parameter.

The command is then assigned to one or more sources:


<Button Command="{x:Static local:tWinMain.sCmdRest}"
  CommandParameter="REST:AQ380" Content="Restore"/>

Common routed commands are implemented as static RoutedUICommand instances within the ApplicationCommands, NavigationCommand, MediaCommands, ComponentCommands, and EditingCommands classes. RoutedUICommand adds a Text property to RoutedCommand that describes the command, the value of which is automatically localized to match the system language. Many predefined commands have default key bindings that are created automatically when the command is assigned:


<Button Command="ApplicationCommands.Find"/>

An unwanted key binding can be removed by setting its command to NotACommand:


<Window.InputBindings>
  <KeyBinding Key="F" Modifiers="Control"
    Command="NotACommand"/>
</Window.InputBindings>

Sources

WPF 4.5 Unleashed
Adam Nathan
2014, Pearson Education / Sams

MSDN: XAML Services
Retrieved June 2017

MSDN: Dependency properties overview
Retrieved December 2017

MSDN: Attached properties overview
Retrieved December 2017

MSDN: Routed events overview
Retrieved December 2017

MSDN: Commanding Overview
Retrieved December 2017

MSDN: How to: Create a RoutedCommand
Retrieved December 2017