Posts for Category: Controls
When it comes to awesome WPF + DirectX video hacks it's Jeremiah Morrill's world, we're just living in it. One of his great contributions to the WPF ecosystem is the WPF Media Kit which adds DVD playback, webcam playback and a number of other capabilities to WPF. One slight deficiency with the webcam control (VideoCaptureElement) is that it doesn't allow simeltaneous recording and display of webcam content....until now!
This code adds simeltaneous video playback and capture to the VideoCaptureElement. VideoCaptureElement gets a "OutputFileName" property which is used to specify where the webcam output will be saved. If this property isn't set then the control functions as before, just showing webcam playback. Most of the changes are in VideoCapturePlayer (VideoCaptureElement is really only a WPF-ized wrapper around VideoCapturePlayer). I've left a copy of the un-modified version of VideoCapturePlayer.cs in the same directory (called VideoCapturePlayer_old.cs) for those who want to look at the differences for themselves, but the main part is where we check to see if the fileName field has been set and if so set up the relevant properties.
IBaseFilter mux = null;
IFileSinkFilter sink = null;
if (!string.IsNullOrEmpty(this.fileName))
{
hr = graphBuilder.SetOutputFileName(MediaSubType.Asf, this.fileName, out mux, out sink);
DsError.ThrowExceptionForHR(hr);
hr = graphBuilder.RenderStream(PinCategory.Capture, MediaType.Video, m_captureDevice, null, mux);
DsError.ThrowExceptionForHR(hr);
// use the first audio device
var audioDevices = DsDevice.GetDevicesOfCat(FilterCategory.AudioInputDevice);
if (audioDevices.Length > 0)
{
var audioDevice = AddFilterByDevicePath(m_graph,
FilterCategory.AudioInputDevice,
audioDevices[0].DevicePath);
hr = graphBuilder.RenderStream(PinCategory.Capture, MediaType.Audio, audioDevice, null, mux);
DsError.ThrowExceptionForHR(hr);
}
}
hr = graphBuilder.RenderStream(PinCategory.Preview,
MediaType.Video,
m_captureDevice,
null,
m_renderer);
The last call to RenderStream() uses a PinCategory of Preview which allows the display of video, the previous calls to RenderStream() were using the Capture pin category, which causes the media to be written to disk. I've updated the sample application that accompanies WPFMediaKit to default to the "webcam sample" screen, and to save the webcam output as a randomly named .wmv file to the "my documents" folder.
This code is very much proof-of-concept, and I'm concerned about leaks in there, but it does work. One limitation is that the file name can't be set after the graph has been set up, but this seems to be a common limitation with properties on the capture device - to change them you have to re-create the graph.
In any case I hope you find this code useful, and I'll be offering it up to Jeremiah for consideration as a patch to WPFMediaKit.
I keep re-creating this from time to time, so I thought I would put it up for others to use. This is a very simple style for rounded gel-like buttons (shown here magnified 3x, but the style works OK for buttons of various sizes). The left-most one is the "normal" style, the middle one is mouse-over and focused, and the right one is disabled. These are great with a slider to create "zoom" type controls, or for play/pause buttons etc.

Source Xaml
Most controls in WPF are "look-less" and have no default rendering. Instead there are a number of "templates" that are applied by WPF. The control template is chosen by WPF, and can be over-ridden by the developer. In the absence of any developer intervention a default template is loaded from a resource assembly corresponding to the operating system that the application runs on (PresentationFramework.Aero.dll for windows vista styles and control templates, PresentationFramework.Luna.dll for windows XP etc). I was puzzled to learn that FlowDocumentScrollViewer and a number of other "document" type controls like FlowDocumentReader do NOT have control templates in these "theme" dlls. So where are they? It turns out they are stored in another ancilliary WPF dll called PresentationUI.dll in the themes/generic.baml resource. This dll also contains the "progress" download and error templates for XBAP applications.
Some time around 2004 apple began using a "brushed metal" look for some of the windows in OSX. In late 2006 Microsoft released Vista with its Aero Glass window chrome. Going further back we had a chiseled grey look-and-feel in many windowing systems (chiseled and grey implying stone?). Taking into account the obvious trend where window chrome is styled after real-world building materials, we confidenty predict that wood will be the substance of the future for mainstream operating systems. Agonising conversations regarding timber finishes and preferences that had previously been relegated to only the real world can now spill over into the computer one. Would you like a mahogany title bar and rosewood buttons? Would you like us to dove-tail the corners of the windows to make them stronger? The folks over at the WPF SDK blog have already given a great overview of current window chrome approaches. For ultimate window chrome customization I like Pavan Podila's [edit: fixed typo in Pavan's name]Fluidkit which contains the very useful "GlassWindow" style-able window control, and used it to create this beautifully finished window with a mahogany titlebar, trimmed with brushed silver and given a satin finish for the discerning computer user.

Or for the younger users there is this more european window style with a plantation pine title bar.
.
You can download the source here and build more of these windows for yourself. I used Wood Workshop from Spiral Graphics to create the textures. Later this week I'm hoping to bring you another hot tip for window chrome futures - this time with an emphasis on lightweight performance - yes, you guess it! Carbon-fibre window chrome!
Pavan Podilla has added his ElementFlow control to a suite of free open-source tools called FluidKit. ElementFlow is a layout panel that provides several carousel-like layouts, including one quite similar to the "coverflow" from Apple's iTunes application. You can check out some videos of ElementFlow here. My favourite part of ElementFlow is that it has been architected in the "wpf way" - Pavan modified it a while back to be a panel, and it uses templating for specifying how items should appear. In fact in the FluidKit.ShowCase demo that ships with FluidKit, ElementFlow is handling the item layout for an ItemsControl much like I described here. FluidKit also includes a number of other useful classes such as Pavan's drag and drop helpers and an Aero-glass style window.
Component vendor xceed recently released v2 of their WPF Data Grid. It is available in both a free express version and a paid-for professional version. Both new version includes better design time support, and a greatly increased set of editors (like masked text boxes, numeric textboxes etc) for grid items and the pro version includes master-detail binding. [full disclosure: xceed are sponsors of this site].
The ASP.NET repeater is a great control for creating an arbitrary templated look for lists of data in ASP.NET web applications. WPF's databinding bears a great deal of similarity to the templating support found in the repeater, taking the power of templating even further by allowing templates to be defined as resources and re-used in multiple contexts, and in lots of different types of controls. However in spite of this there is no Repeater control in WPF.
Sample Repeater (from MSDN)
<asp:Repeater id="Repeater1" runat="server">
<HeaderTemplate>
<table border="1">
<tr>
<td><b>Company</b></td>
<td><b>Symbol</b></td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td> <%# DataBinder.Eval(Container.DataItem, "Name") %> </td>
<td> <%# DataBinder.Eval(Container.DataItem, "Ticker") %> </td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
How can you replicate the functionality of the repeater in WPF? It turns out this is quite easy using an ItemsControl. ItemsControl is the base type that lots of list-like controls in WPF like ListBox, ComboBox and Menu which inherit from it either directly or indirectly. ItemsControl is also the parent to some list-like controls you might not expect such as the toolbar (which is really just a list of buttons and other controls, usually laid out horizontally) and StatusBar (once again, usually just a kind of horizontal list of controls). In spite of its fairly fundamental nature to lots of the usual suspects in the WPF controls namespace it isn't an abstract type and can be created in markup and bound like this:
ItemsControl Xaml Code
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- apply additional customizations for
the items here in the template -->
<TextBlock Text="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The <DataTemplate> element allows us to specify how we want each item the ItemsControl is bound to, and the ItemsSource property on the ItemsControl populates the bound items from the data context (I'm assuming for the purposes of this demo that the stuff you want to bind to is in there). Another thing you may wish to do with the ItemsControl is to place it inside of a ScrollViewer control. The ASP.NET repeater generates HTML which is hosted in the browser, which handles showing a scroll-bar if the document is longer than can be displayed on a single screen. WPF Windows and Pages don't default to this way of operating, and thus you probably want to add the scroll viewer if more items are added to the ItemsControl than can be displayed inside the Window or Page.
Here is a complete page showing the list of binding modes available in WPF displayed in an ItemsControl. The ItemsControl has an ItemsPanel property that the bound items are layed out in. The default is a StackPanel, which is they type of panel you would want to reproduce the functionality of the ASP.NET Repeater. In this example below we've used the UniformGrid instead to control the layout of the items being bound. You can click here to run this page in your browser.
<Page
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
xmlns:d="clr-namespace:System.Windows.Data;assembly=PresentationFramework"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
>
<Page.Resources>
<ObjectDataProvider MethodName="GetValues" ObjectType="{x:Type sys:Enum}" x:Key="bindingNames">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="d:BindingMode"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Page.Resources>
<ItemsControl ItemsSource="{Binding}" DataContext="{StaticResource bindingNames}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- apply additional customizations for the items here in the template -->
<TextBlock Text="{Binding}" FontSize="22"
Margin="5" Foreground="#feca00" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Page>
WPF’s InkCanvas control provides a quick and easy way to add ink input to your application. By default the strokes in the InkCanvas are thin black lines, which may be too thin or difficult to see in some instances. Fortunately the appearance of the strokes can easily be changed by modifying the DefaultDrawingAttributes property of the InkCanvas which is of type DrawingAttributes.
<InkCanvas>
<InkCanvas.DefaultDrawingAttributes>
<DrawingAttributes Color="Red" Width="5" />
</InkCanvas.DefaultDrawingAttributes>
</InkCanvas>
The DrawingAttributes has a number of other interesting properties, such as the FitToCurve property which makes lines drawn in the canvas smoother. It is set to false by default. This property seems to have less of an effect when using a tablet input device. When “drawing” with the mouse you can notice the difference more easily.

Setting the IsHighlighter property to true causes the strokes to be drawn semi-transparently to allow content behind them to show through.

The IgnorePressure property defaults to false and uses pressure information from devices capable of sending it (like input tablets) to alter the thickness of the line.

You can also set the shape of the stylus tip to rectangular or elliptical by setting the StylusTip property.
One common way of moving the focus around in a user interface on Windows is to use the Tab key to move sequentially between controls. The contents of a WPF tool-bar seem to act like a kind of tabbing black-hole. The focus goes in there but never comes out again. Try pasting this Xaml into XamlPad to see the problem:
Xaml Code (.NET 3.0)
<DockPanel>
<ToolBar DockPanel.Dock="Top">
<Button Content="B"
Command="EditingCommands.ToggleBold" />
<Button Content="U"
Command="EditingCommands.ToggleUnderline" />
<Button Content="I"
Command="EditingCommands.ToggleItalic" />
</ToolBar>
<RichTextBox />
</DockPanel>
Fortunately this can be easily fixed by using the KeyboardNavigation attached property and changing the TabNavigation to "Continue". This allows the tab focus to move out of the toolbar and back to the other elements of the UI.
Xaml Code (.NET 3.0)
<DockPanel>
<ToolBar DockPanel.Dock="Top"
KeyboardNavigation.TabNavigation="Continue">
<Button Content="B"
Command="EditingCommands.ToggleBold" />
<Button Content="U"
Command="EditingCommands.ToggleUnderline" />
<Button Content="I"
Command="EditingCommands.ToggleItalic" />
</ToolBar>
<RichTextBox />
</DockPanel>
The WPF frame control is a content control, capable of displaying both WPF and HTML content, and allowing navigation between different pieces of content. While it is un-questionably useful there are some limitations with it that make it more so in some circumstances than others.
Rendering
The WPF Frame control alters the way it renders based on the type of content it is displaying. When it is displaying WPF Pages or loose XAML it uses WPF’s rendering pipeline. When HTML is displayed the Frame uses Internet Explorer for rendering. Internet Explorer uses traditional GDI-based rendering. Thus when displaying HTML content you will experience many of the rendering issues you also encounter when using a HwndHost, such as not being able to apply transformations or transparency and not being able to use the output as a visual brush or texture.
A striking example of this can be seen by placing identical Frame elements on a form side-by-side. One frame is displaying HTML and the other is displaying Xaml content and both have a RotateTransform applied to them. The Frame displaying Xaml content is rotated, but the HTML frame is not.
Xaml Code (.NET 3.0)
<Window x:Class="LearnWPF.FrameTest.Window1"
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
Title="LearnWPF.FrameTest" Height="300" Width="300"
>
<StackPanel>
<Frame Source="TestPage.xaml">
<Frame.LayoutTransform>
<RotateTransform Angle="25" />
</Frame.LayoutTransform>
</Frame>
<Frame Source="E:\TestHTMLPage.htm">
<Frame.LayoutTransform>
<RotateTransform Angle="25" />
</Frame.LayoutTransform>
</Frame>
</StackPanel>
</Window>
Another example can be seen by setting some properties on the top-level window object. Setting the Window’s AllowTransparency to True (which also necessitates setting the WindowStyle to None) causes the frame displaying HTML to be not shown at all!
Xaml Code (.NET 3.0)
<Window x:Class="LearnWPF.FrameTest.Window1"
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
Title="LearnWPF.FrameTest" Height="300" Width="300"
AllowsTransparency="True"
WindowStyle="None"
>
Content
Another issue which can cause problems when trying to use the WPF Frame control is how to assign the content you wish to display. Although the Frame inherits from ContentControl which has a Content property of type object, you can’t use this to property to place “marked up” content in the Frame. Assigning a string to the content property will cause the Frame to display the assigned content as literal text. Instead you need to use the Source property of type Uri which can point to content on the internet or on disk. Thus if you wish to display a fragment of HTML (perhaps extracted from an RSS feed, or dynamically generated) it has to be written to local disk before it can be rendered.
Depending on how you intend to use the Frame these issues could be non-issues or deal breakers. To get around some of these limitations in applications some people have written code to convert from HTML to WPF FlowDocuments, although the complex and layered nature of HTML and the surrounding specifications like CSS and ECMAScript make exact conversion impossible.
I recently noticed an issue with the rendering of ComboBox controls when they are hosted in WPF XBAP applications. The nature of this issue is such that it will probably (hopefully?) not cause anyone any problems. As the screen-shot below shows, when a ComboBox has a RotateTransform applied to it (as either a RenderTransform or LayoutTransform) the "drop down" part of the combo is not rotated. For comparison I've included a regular WPF ListBox which is also rotated.

In contrast the same Xaml code hosted in XamlPad does not have this same "issue".
WPF FlowDocuments can contain controls like CheckBoxes and
Buttons. When the document is displayed statically and is not editable the
controls are enabled, however in WPF V1 when they are rendered as the content
of a RichTextBox (and the FlowDocument can be edited) the controls are
disabled.
This has not always been the case. During the WPF CTP releases
around beta 1 it was possible
to enable controls inside a FlowDocument contained within a RichTextBox by
setting the IsEnabled property on the FlowDocument to true. As WPF V1
approached the WPF team decided to disable this functionality. Although I don’t
believe I’ve ever seen a reason for this it would be a difficult feature to fully
support.
Fortunately there is a fairly clean work-around, for those
prepared to take the risks associated with using a feature that the product
team has consciously disabled. By inheriting from FlowDocument and over-riding
the protected IsEnabledCore method to return true, and then using this
FlowDocument sub-type inside the RichTextBox you can add enabled controls.
C# Code (WPF V1)
class EnabledFlowDocument : FlowDocument
{
protected override bool IsEnabledCore
{
get
{
return true;
}
}
}
Xaml Code (WPF V1)
<RichTextBox Grid.Column="1">
<!-- sub-class of FlowDocument -->
<my:EnabledFlowDocument FontFamily="Segoe" FontSize="12">
<Paragraph>
This is some text inside a sub-type of flowdocument
</Paragraph>
<BlockUIContainer>
<Button Content="Click Me!" Click="ButtonClicked">
</Button>
</BlockUIContainer>
<Paragraph>
<Hyperlink
NavigateUri="http://learnwpf.com" Click="LinkClicked">
visit LearnWPF.com
</Hyperlink>
</Paragraph>
</my:EnabledFlowDocument>
</RichTextBox>

You can download the source code here
Note: I had to explicitly set the FontFamily and FontSize on
the instance of the sub-type of FlowDocument that I created to match the “default”
appearance of the regular FlowDocument, so a little more work is required to
make this a drop-in replacement for the regular FlowDocument.
I first saw this work-around in
the MSDN forums but have subsequently discovered it has been documented
elsewhere.
Just Because Prince Wanted to Party Like it was 1999 Doesn’t Mean Your App Should
If you’ve ever used WPF’s OpenFileDialog control you have probably noticed that its appearance leaves a great deal to be desired. In particular the blocky, low res icons in the toolbar to the upper right, and the chiselled “classic” looking Open and Cancel buttons and File Name and File Type combo boxes at the bottom of the screen look out of place, especially on Windows Vista.
In contrast here is the appearance of the OpenFileDialog in Windows Forms 2.0 on Vista (shown below). The blocky icons have been replaced with something much nicer, and the buttons and dropdown combo boxes are displayed using the correct windows theme:
The default Windows Forms project template in Visual Studio 2005 includes a call to Application.EnableVisualStyles() which causes controls to adopt the “visual style” of the OS. Removing this call makes windows forms OpenFileDialog look the same as the WPF one – in short, it seems like this is a theme-related issue in a “pure” WPF application!
Microsoftie Bombay Boy (probably not his real name) explains how to work around this in WPF Clickonce applications by including an entry in the application manifest file. This was the way of creating “theme aware” Windows Forms applications for XP in .NET Framework 1.0. In framework 1.1 the EnableVisualStyles method mentioned above was added to the Windows Forms Application object. Although this entry suggests calling EnableVisualStyles as a way of getting around theme-related issues in WPF/WinForms composite apps it does not seem to fix the themeing issues with the OpenFileDialog. EnableVisualStyles seems to have been implicated in a number of subtle bugs also, so the manifest route may be the most robust approach regardless.
What about XBAP applications?
Fortunately since they are hosted inside IE (which essentially has something equivalent to what is in the manifest file, requiring the theme-aware MS Common Controls v6 instead of v5) there is nothing that needs to be done for them to get a more modern-looking OpenFileDialog.
What About Vista?
Although being able to change the appearance of the OpenFileDialog to give it a more modern appearance is a step in the right direction the current OpenFileDialog for both Windows Forms and WPF leave a great deal to be desired on Vista. For Vista, Microsoft made a number of improvements to this “common” dialog control to make it more like a miniature version of Windows Explorer (which also received numerous improvements). Compare and contrast the WPF and WinForms dialogs above to the following OpenFileDialog from none other than Notepad which uses the "system" OpenFileDialog on Vista.
The easiest way to get this open file dialog for your WPF applications running on Vista is to use the sample code provided with the windows SDK as part of the “Vista Bridge” sample – which is in the Cross Technology Samples zip file, which is installed by default to here: C:\Program Files\Microsoft SDKs\Windows\v6.0\Samples\. The VistaBridgeLibrary project includes a CommonOpenFileDialog control which has an API similar to WPF’s built-in Microsoft.Win32.OpenFileDialog, it’s not quite a drop-in replacement but is fairly easy to use and also exposes some of the newer features of this dialog. The catch is that since this is a wrapper around un-managed API calls it will only be available to programs running with full trust (so NOT XBAPs).The Vista Bridge also contains a number of other interesting wrappers around un-managed parts of Vista and should be the first port of call for managed code developers looking to gain access to features of Vista if they are having difficulty.
I think it is a great shame that WPF, a platform that was developed primarily for Vista, could not provide built-in access to the system file dialogs. I hope the WPF team consider addressing this issue in the next release.
Microsoft recently released a new theme for Windows XP to promote the new Zune media player. Unfortunately the WPF team did not create a suite of control styles for this theme. As a result WPF controls running under the Zune theme are rendered using the Windows-9x era "classic" style. Fortunately controls under the Zune theme are quite similar to the "metallic" windows XP theme. Using reflection as shown in this sample application it is possible to fake out WPF to make them think they are running under a different theme.
[disclaimer] This code needs to run while the application is loading. It uses reflection and native inter-op, and thus is unsuited to XBAP applications or other lower-trust environments. Handling theme changes while the application is running is left as an exercise for the reader.
A recent post in the WPF forums asked this very question. Fortunately it is possible to create a border in WPF and specify a different radius for each corner as a comma-separated list, rather than specifying a single corner-radius.
Xaml Code (July CTP)
<Window x:Class="BorderCornerRadius.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="BorderCornerRadius" Height="200" Width="300" Background="#58290A"
>
<Border BorderBrush="#feca00" CornerRadius="0,10,20,40"
BorderThickness="2" Height="100" Width="200" />
</Window>
This produces the following UI
The ComboBox in WPF allows users to select an item from the list, but by default does not allow them to edit the text in the combo box. The ComboBox control in Windows Forms on the other hand by default does allow users to enter "free text" into the combo box, in addition to selecting items from the list. In Windows Forms this behavior can be changed, removing the "free text" facility by setting the confusingly named DropDownStyle property to DropDownList. Can we do the opposite in WPF and allow users to edit the text?
Fortunately you can do this in WPF easily by setting the IsEditable property to True as shown below.
Xaml Code (June 06 CTP)
<Window x:Class="LearnWPF.EditableComboBox.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="LearnWPF.EditableComboBox" Height="300" Width="300"
>
<Window.Resources>
<XmlDataProvider x:Key="items" XPath="//item">
<x:XData>
<items xmlns="">
<item>01</item>
<item>02</item>
<item>03</item>
</items>
</x:XData>
</XmlDataProvider>
</Window.Resources>
<Grid>
<ComboBox IsEditable="True" DataContext="{StaticResource items}"
ItemsSource="{Binding}"/>
</Grid>
</Window>
Its worth noting that while this change may be simple to make in the Xaml it may also require some changes in your C# or VB.NET application code - you may need to handle more than the SelectionChanged event on the combo box (since users may start entering values outside the range of values in the list and the SelectionChanged will cease to fire). You also can no longer rely on the SelectedValue and SelectedItem properties of the combo box having a non-null value.
I was asked today how to create a borderless window in WPF with no window "chrome" and no gray 3-D border. This is desirable for windows like splash-screens. The Window class in WPF has a property WindowStyle, which can be set to either None, SingleBorderWindow, ThreeDBorderWindow or ToolWindow. While the "None" value sounds exactly like what is required, the window still renders with a gray 3-D border as shown below.
To make the window display without this gray border you need to also set the ResizeMode property of the window to NoResize.
Xaml Code (WPF Beta 2/June CTP)
<Window x:Class="LearnWPF.BorderlessWindow.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="LearnWPF.BorderlessWindow" Height="200" Width="200"
Background="White" WindowStyle="None" ResizeMode="NoResize"
>
<Border Padding="5" BorderBrush="#feca00"
BorderThickness="3" Width="150" Height="150">
<TextBlock>Learn WPF!</TextBlock>
</Border>
</Window>
To create irregular-shaped (non-rectangular) semi-transparent borderless windows with the June CTP and later see this posting from Lauren Lavoie.
Windows ships with a number of attractive fonts pre-installed, however there are many other beautiful and distinctive fonts that are not. Rather than limit your UI design to only the default fonts, WPF makes it easy to distribute custom fonts with your application. I saw this technique on Filipe Fortes' Mix06 video, and subsequently learned he's created a screen-cast showing how to add and use custom fonts. Here's a summary:
Including the Font in your Application
Include the font in your application adding it to your visual studio project. The Build Action should default to 'Resource'. You may wish to create a folder to separate custom fonts, images and other visual resources from your Xaml and C# code. For the purposes of this example we're going to assume you created a folder called CustomFonts and added a font called Pericles (which ships with the WPF SDK in a file called peric.ttf) to that folder.
Using the Font in your Application
You can use the custom font by setting the FontFamily property of elements like TextBlock to the font resource. The path to the font resource should include the names of the folder(s) and sub-folders in your project you placed the font in (separated by the / character), followed by a # and the name of the font (which is not necessarily the same name as the file it is in). For example the following code uses the Pericles font in a TextBlock
Xaml Code (Feb 2006 CTP)
<TextBlock FontFamily="CustomFonts/#Pericles" FontSize="20">WPF makes text beautiful</TextBlock>
Fonts are intellectual property (like programs, songs and movies) so ensure you check the license for the fonts you intend to redistribute. For a varied list of free fonts you could start here http://fontleech.com/ .
The rendering of controls in WPF is defined by the template used for the control. In a previous posting we saw how to supply our own template for a control to customize the rendering. It is not un-common to want to make some small modifications to the default template for an existing control, or wonder how a certain effect is achieved in the default template of a given control. Wouldn't it be great if you could somehow extract the Xaml code for default template out of a control and see how it worked? Fortunately in WPF you can, using a technique I first heard about from WPF evangelist Karsten Januszewski. The steps are as follows:
- You add an add an instance of the control you want to "decompile" to your form and give it a name. In this example we've assumed it will be named controlIWantToDecompile.
- Add a window loaded or button click event handler to your form. Inside the event handler add the following snippet of code:
StringBuilder sb = new StringBuilder();
using (TextWriter writer = new StringWriter(sb))
{
System.Windows.Markup.XamlWriter.Save(controlIWantToDecompile.Template, writer);
}
- Write the contents of the string builder to the debug output, or out to a text box. You now have the default template for the control.
You can then add this newly extracted template as a resource, and have control instances to use it, or add it as part of a style. Using the same technique you can also serialize out a control's default style (if it has one). Here is a screen-shot of two check-boxes. The first is a regular xp-themed WPF checkbox. The second one uses a slightly modified version of the normal checkbox template. We "extracted" the default template using the method described above and then made a few small modifications, without having to re-create the mouse-over behavior or the check mark ourselves.
This ability to look at and modify the default templates for controls is so useful that it is built right in to the Expression Interactive Designer (code-named Sparkle). In the March 2006 CTP right-click on a control and select Edit Template > Edit a Copy of the Template (as shown below).
You are then presented with a dialog prompting you for the name of the new template you wish to create, where you wish to locate the resource and what type the template should target. You can then edit the template with the same great graphical designer experience you have for editing regular forms (or edit the Xaml directly is you wish).
This ability to extract and serialize out as Xaml the default template for a control is invaluable when you're trying to create a template for a complex control like a slider or combo box. It is also useful to see some of the techniques used by the WPF team themselves when creating control templates.
In a previous how-to we looked at WPF's styling capabilities. In this example we are going to use styles to change the appearance of an element when the mouse moves over it. This is easily accomplished using a facility in WPF called triggers. Triggers can "watch" certain special object properties in WPF (called dependency properties), and when they change the appearance of the object can be updated. Most properties that WPF UI Elements expose are dependency properties. Triggers can also "fire" based on events, or on changes to data, but for this example we're only going to look at the simple case of an trigger changing as a result of an object property change.
Below is an updated version of the button style we created in a previous how-to. We have added a trigger that is activated when the IsMouseOver property (which the Button object inherits from its parent UIElement) has a value of true.
<Window x:Class="ButtonWithOuterGlow.Window1"
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
Title="LearnWPF - Button with Outer Glow" Height="200" Width="400"
>
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="#58290a" />
<Setter Property="FontFamily" Value="Lucida Console" />
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
<LinearGradientBrush.GradientStops>
<GradientStop Color="#feca00" Offset="0.1"/>
<GradientStop Color="#ffe100" Offset="0.4"/>
<GradientStop Color="#feca00" Offset="0.6"/>
<GradientStop Color="Orange" Offset="0.9"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BitmapEffect">
<Setter.Value>
<OuterGlowBitmapEffect GlowColor="Red" GlowSize="4"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid Margin="20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Content="Field 1" Grid.Column="0" Grid.Row="0"
Height="25"/>
<TextBox Text="Some text goes here" Grid.Column="1"
Grid.Row="0" Height="25"/>
<Button Grid.Column="2" Grid.Row="0" Content="Click me"
Height="25" Margin="5"/>
</Grid>
</Window>
In this example we are changing the appearance of the button by adding an OuterGlowBitmapEffect to the button. This gives the button a colored halo. There are a number of other interesting bitmap effects that can be applied to UI Elements including Blur and DropShadow. The screen-shot below shows the appearance of the button when the mouse moves over it and the trigger is activated.
WPF allows you to specify the appearance of UI elements in a single place, and re-use that "look" in multiple places - either across a form or across your whole application. This mechanism is called styles. They are similar to the styles available in web development in CSS, but more powerful because they can not only be used to change the properties of a UI element (such as a button), they can be used to completely re-define how the control is rendered. They are also declared using XAML, rather than a seperate language like CSS.
The techniques shown below are in no way limited to buttons - you can use styles to change or even completely define the rendering of any WPF controls. The intention with WPF is that controls like button define a behavior, and a default rendering but allow the developer/designer to completely replace the "look" if they wish. For this reason WPF controls are sometimes referred to as "look-less" controls.
Enough theory - how would we use WPF styles to replace the rendering of all the buttons on a form? First lets create a form with two text boxes and two buttons in a grid. We want to define the style for the buttons in a single place and have it automatically applied to the two buttons.
Form XAML Code (Feb 06 CTP):
<Window x:Class="LearnWPF.ButtonStyle.Window1"
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
Title="LearnWPF - Changing Elements with Styles"
Width="350" Height="200">
<Grid Margin="20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="95"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TextBox Text="Foo" Grid.Column="0" Grid.Row="0" />
<Button Content="Click Me.." Grid.Column="1" Grid.Row="0"/>
<TextBox Text="Bar" Grid.Column="0" Grid.Row="1" />
<Button Content="Click Me too.." Grid.Column="1" Grid.Row="1"/>
</Grid>
</Window>
On windows XP with the silver theme the window will appear as shown below:
However on Windows XP under the classic windows theme it appears as shown in this second figure:
We can already see that WPF is capable of changing how controls are rendered to suit the context it is running in. So how do we go about defining our own style for the buttons? The following modified version of the form does just that with a Style added to the Resources collection that exists for the window.
Form XAML Code (Feb 06 CTP):
<Window x:Class="LearnWPF.ButtonStyle.Window1"
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
Title="LearnWPF - Changing Elements with Styles" Width="350" Height="200">
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="#58290a" />
<Setter Property="FontFamily" Value="Lucida Console" />
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
<LinearGradientBrush.GradientStops>
<GradientStop Color="#feca00" Offset="0.1"/>
<GradientStop Color="#ffe100" Offset="0.4"/>
<GradientStop Color="#feca00" Offset="0.6"/>
<GradientStop Color="Orange" Offset="0.9"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Margin="20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="95"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TextBox Text="Foo" Grid.Column="0" Grid.Row="0" />
<Button Content="Click Me.." Grid.Column="1" Grid.Row="0"/>
<TextBox Text="Bar" Grid.Column="0" Grid.Row="1" />
<Button Content="Click Me too.." Grid.Column="1" Grid.Row="1"/>
</Grid>
</Window>
The first attribute of the Style that is set is the TargetType, setting this to {x:Type Button} as is being done in the sample above tells WPF that this style should be used for all elements of type Button on the form unless another style is specified explicitly for them. This style sets the Foreground and FontFamily properties of the button to simple values that can be specified in a single string. It also specifies a linear gradient for the Background of the buttons. Because this cannot be specified as a single string the Property Element syntax of WPF is used - when setting a complex property a nested child element is created and the element is given the name of the parent element followed by the name of the property you wish to set. With this style applied the form renders as shown below:
This style merely sets some properties on the button - which we could have achieved ourselves by painstakingly setting properties on all our button controls in XAML. Clearly this approach with styles is better - it declaratively specifies the look we want for our button in a single place. What about going further and totally replacing the way the button renders? We can also do this in a style, be specifying a new Template for the element we want to replace. The Template defines how the element renders itself out. In this modified version of the form below we define a new template for the buttons on our form, which gives us rounded, gel-like buttons.
Form XAML Code (Feb 06 CTP):
<Window x:Class="LearnWPF.ButtonStyle.Window1"
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
Title="LearnWPF - Changing Elements with Styles" Width="350" Height="200">
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ClipToBounds="False">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<!-- the background for the button -->
<Rectangle RadiusX="15" RadiusY="10" Grid.RowSpan="2">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
<LinearGradientBrush.GradientStops>
<GradientStop Color="#feca00" Offset="0"/>
<GradientStop Color="Orange" Offset="0.9"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<!-- the "gel" hilight at the top of the button -->
<Rectangle RadiusX="7" RadiusY="6" Margin="2">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
<LinearGradientBrush.GradientStops>
<GradientStop Color="#fff399" Offset="0.1"/>
<GradientStop Color="#ffe100" Offset="0.5"/>
<GradientStop Color="#feca00" Offset="0.9"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<!-- place for the content inside the button to be displayed -->
<ContentPresenter Grid.RowSpan="2"
x:Name="PrimaryContent"
HorizontalAlignment="Center" VerticalAlignment="Center"
Margin="{TemplateBinding Padding}"
Content="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}"
/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="#58290a" />
<Setter Property="FontFamily" Value="Lucida Console" />
</Style>
</Window.Resources>
<Grid Margin="20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="95"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TextBox Text="Foo" Grid.Column="0" Grid.Row="0" />
<Button Content="Click Me.." Grid.Column="1" Grid.Row="0"/>
<TextBox Text="Bar" Grid.Column="0" Grid.Row="1" />
<Button Content="Click Me too.." Grid.Column="1" Grid.Row="1"/>
</Grid>
</Window>
The appearance of the form is shown below:
If you run the sample you'll see some unfortunate shortcomings with this template - for example it gives no visual indication when the button is clicked however it does demonstrate the power of styles to fully replace the rendering of a control. For a more fully-featured template example (built against the January CTP) you should study Robert Ingebretsen's Orange Shiny Button template which was recently featured on MSDN TV, and for more aesthetically appealing gel buttons have a look at Valentin Iliescu's WPF Aqua Gel button tutorial. For further information on Styles and control templates you should also read chapter 5 of "Programming Windows Presentation Foundation" - this chapter is available for free download.