Posts for Category: Tips
For large hierarchies the TreeView in the Silverlight tool-kit suffers some performance problems, because the elements (once loaded and expanded, such that they could be seen by the user if they scrolled the scroll-bar) aren’t virtualized as aggressively as they could be. The for example the hierarchy shown below: If Child Node 4 is expanded, pushing Child Node 5 outside the limits of the view port of the TreeView then Node 5, as well as any other children of Node 4 that aren’t directly visible, _could_ be virtualized but currently aren’t by the Silverlight Treeview. When the number of invisible-but-loaded nodes is small the performance impact is negligible, but if Node 4 (in the example below) had 10,000 children, each with a complex data template applied to them the performance impact expanding the tree can be quite noticeable.
One approach to get around this is to recognize that a TreeView is just a List with indentation and expand/collapse semantics. By flattening your object hierarchy with an appropriate wrapper you can use the virtualization that is built into the ListBox, which happily does virtualize items that have disappeared off the top or the bottom of the list, by virtue of its use of the VirtualizingStackPanel that was added in Silverlight 3.
The sample provided does just that with two classes TreeViewModel<T> and NodeViewModel<T>. TreeViewModel<T> contains a Nodes collection, an ObservableCollection of NodeViewModel<T> which the list is bound to. The visibility of the expand/collapse arrow is bound to the presence of children. When a NodeViewModel<T> is expanded it calls back to the TreeViewModel<T> to tell the TreeViewModel to create a new set of NodeViewModels based on the children of the node being expanded, and to insert them directly after node being expanded in the current list of nodes. When the TreeViewModel makes new nodes it increments the indentation property, which is used to drive how far in the node is rendered when it is shown. When a NodeViewModel<T> is collapsed it again calls the TreeViewModel<T> and basically tells it to throw away all the nodes that come after me in the Nodes collection, until you reach one that has the same indentation as me (or you reach the end of the list), at which point the TreeViewModel will know that it has removed all the children of the item that was collapsed.
The sample provided is not a complete drop-in replacement for the TreeView control in the Silverlight toolkit. For one thing some of the expand/collapse semantics are a little different to a standard TreeView. For example if all the nodes in the tree are fully expanded, and the root node is collapsed and then re-expanded then the standard tree-view behaviour is for the tree to look the same as before the root node was collapsed, however this tree will only show the direct children of the root node after the root node is collapsed and then re-expanded, regardless of the state of the tree before. This is a side-effect of the way this implementation creates and destroys NodeViewModel<T> instances when a node is collapsed/expanded. When a NodeViewModel<T> is removed from the list of nodes the fact that it was previously expanded is lost. This could be changed by keeping instances of removed nodes around, but would complicate the expand/collapse methods in TreeViewModel<T>. Also some other TreeView standard keyboard short-cuts are missing (for example right arrow to expand a node, and left arrow to contract it) are not wired up. Also it is likely that this technique will become obsolete at some point when the TreeView in the Silverlight Toolkit catches up with the WPF one and implements this trick for itself.
You can download the source code from here. And here is an image of the sample running.
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.
Using attractive typefaces can add individuality to your application, or reinforce your brand. Although we talked about the means to embed WPF fonts in your application a long time ago, the biggest issue with font embedding is the licensing issues associated with fonts, especially if you’re working on a code sample or free utility. Most fonts, even the multitude that you can download for free online, are not licensed to allow redistribution as part of your application.
Fortunately a small group of fonts has started to be released under a much more permissive license – the SIL Open Font License (OFL) - which specifically allows embedding in applications, both commercial, free and open-source (disclaimer: I am not a lawyer, you should read the license yourself and/or seek legal advice before making any decisions regarding font licensing). This is a very generous move on the part of these type designers who spend considerable time and skill on designing beautiful fonts.

Here is a sample application that uses some of my favourite of OFL-licensed fonts from around the web including Titillium, Goudy Bookletter 1911, and Junction. Great resources for finding new fonts with the OFL license is the Open Font Library and the League of Movable Type.
download source [413 KB] (including fonts!)
From a programming point-of-view it pays to remember this trick from the MSDN for finding the names of embedded fonts. Sometimes the name is not apparent from the file name OR from the info you see in the "Install Font" dialog in windows. Instead you can programatically iterate through the installed fonts using this snippet of code.
// iterates over embedded fonts - assumes you have fonts in a sub-directory called "Fonts" in the project in visual studio
foreach (var fontFamily in Fonts.GetFontFamilies(new Uri("pack://application:,,,/"), "./Fonts/"))
{
// do stuff with fonts here
}
.NET Framework 4.0 solves the single biggest problem with WPF development - The size of the .NET framework redistributable. The full redistributable for the x86 version of the .NET framework weighs in at a svelte 37.71 MB, and the cut-down client profile (that should be suitable for many WPF client applications) is just under 31 MB! For 64-bit platforms (x64 and ia64) the full redistributable are about 55 MB each. They both have the x86 bits included also so x86 .NET applications can run in the WoW. You can get all the details here (including what is in and out of the client profile, how to target etc here). Compared to the 230 MB for the full redistributable for .NET Framework 3.5 sp1 this is a welcome relief. Whatever techniques the .NET framework team have learned putting silverlight on an extreme diet have paid off here too. Lets hope this minification of the framework continues into the RTM version.
Have you ever wondered what the difference between DemiBold and SemiBold text is? (the answer: not a whole lot). Have you ever wanted to go beyond "Normal" and "Bold" in your font weight selections? If so then this post is for you.
The following shows the different font weights (both names and numeric values) for the default UI font in Windows 7 and Windows Vista - Segoe UI at 24pt.
We can immediately see that Segoe UI supports a variety of weights, but why does 'Thin' (supposedly at 100-weight) look the same as 'Light' (300-weight)? The answer is the font itself - it only supports 4 different weights. Switching to Calibri we can see that it contains only two different weights.
The moral of the story is that WPF provides great typographic support for different font weights, but not all fonts will support all weights. Here is the XAML code to play around with different font families.
<Page
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
">
<Page.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontFamily" Value="Calibri" />
<Setter Property="Margin" Value="5" />
<Setter Property="FontSize" Value="24" />
</Style>
</Page.Resources>
<StackPanel >
<TextBlock FontWeight="Thin" Text="Jackdaws love my big sphinx of quartz - Thin - 100" />
<TextBlock FontWeight="UltraLight" Text="Jackdaws love my big sphinx of quartz - UltraLight - 200" />
<TextBlock FontWeight="ExtraLight" Text="Jackdaws love my big sphinx of quartz - ExtraLight - 200" />
<TextBlock FontWeight="Light" Text="Jackdaws love my big sphinx of quartz - Light - 300" />
<TextBlock FontWeight="Normal" Text="Jackdaws love my big sphinx of quartz - Normal - 400" />
<TextBlock FontWeight="Regular" Text="Jackdaws love my big sphinx of quartz - Regular - 400" />
<TextBlock FontWeight="Medium" Text="Jackdaws love my big sphinx of quartz - Medium - 500" />
<TextBlock FontWeight="DemiBold" Text="Jackdaws love my big sphinx of quartz - DemiBold - 600" />
<TextBlock FontWeight="SemiBold" Text="Jackdaws love my big sphinx of quartz - SemiBold - 600" />
<TextBlock FontWeight="Bold" Text="Jackdaws love my big sphinx of quartz - Bold - 700" />
<TextBlock FontWeight="UltraBold" Text="Jackdaws love my big sphinx of quartz - UltraBold - 800" />
<TextBlock FontWeight="ExtraBold" Text="Jackdaws love my big sphinx of quartz - ExtraBold - 800" />
<TextBlock FontWeight="Heavy" Text="Jackdaws love my big sphinx of quartz - Heavy - 900" />
<TextBlock FontWeight="Black" Text="Jackdaws love my big sphinx of quartz - Black - 900" />
<TextBlock FontWeight="ExtraBlack" Text="Jackdaws love my big sphinx of quartz - ExtraBlack - 950" />
<TextBlock FontWeight="UltraBlack" Text="Jackdaws love my big sphinx of quartz - UltraBlack - 950" />
</StackPanel>
</Page>
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!
.NET Framework 3.5 SP1 – The most significant thing on the WPF landscape in 2008 was .NET framework 3.5 SP1, released in September 2008. While service packs are traditionally about bug fixes and performance tweaks this release was a little different for the WPF team, as they added new features such as data template recycling, DirectX integration improvements like Direct3DImage, new controls like the WebBrowser control, a slew of performance improvements and changes to the application model and deployment. The most visible change was the addition of programmable pixel-shader support, a replacement for the slow software-rendered BitmapEffects which were often responsible for WPF performance issues. The release of the public symbols for .NET 3.5 SP1 in October 2008 means you can “step thru” the framework code during your debugging session, which is extremely useful.
Silverlight 2.0 and the Rise of Silverlight – Silverlight Version 2, which bears a much stronger similarity to WPF than v1 did, was released in October 2008. At around a 4MB download (including a mini CLR and .NET framework stack) it has only a fraction of the features of its older sibling WPF, but the team did a great job balancing size vs. usefulness, and you can do a heck of a lot with what they put into those 4MB. Due to its small size and cross-platform nature Silverlight has seen a much sharper adoption curve than WPF, and a huge amount of community interest. We are now in the interesting position of having features such as the Silverlight Datagrid and stuff from the Silverlight Control Toolkit being “back-ported” to WPF. Announcement such as this one from Scott Guthrie telling of 3D hardware acceleration coming in Silverlight 3.0 (due in beta some time in 2009), and speculation around additional off-line capabilities to compete with Adobe AIR mean that Silverlight will continue to be very hot for 2009.
MVC, MVP and MVVM – This year the consensus amongst a number of WPF proponents seemed to solidify around the Model-View-ViewModel pattern, a slightly different twist on the Model-View-Controller (MVC) and Model-View-Presenter (MVP) patterns. MVVM (originally described here by then-blend architect John Gossman) leverages WPF’s great data binding support to improve the testability of presentation-specific business logic, and creation of ultra-thin Window and Control-derived classes. Microsoft Patterns and Practices also released their composite guidance for WPF (formerly known as PRISM) to help customers build maintainable WPF applications.
Improved Tooling – in addition to improvements to the “core” WPF development/design tools with service packs for Visual Studio 2008 and Expression Blend 2, 2008 saw a number of tools and utilities from the WPF community come into their own, such as Robbie Ingerbretsen’s Kaxaml, Karl Shifflet’s MOLE and Josh Smith’s Crack and Pixel Shader editing tool Shazzam by Walt Ritscher. Microsoft also released an update of the WPF performance toolkit, and ETW tracing tool XPerf (which can be used for both Silverlight and WPF, in addition to many other things).
The Future – One of the best signs for a continuing bright future for WPF was the announcement that dev10 (Visual Studio 2010) will use WPF for a number of elements in the Visual Studio shell including one of the most key features – the text editor itself. Developer division’s internal “dog-fooding” of WPF will ensure it gets important attention in performance tuning and extensibility areas. PDC 2008 saw some discussion of a roadmap for WPF in the PC46 talk, but I found this one a bit lack-lustre. There were also some announcements regarding further improvements to the XAML language.
Overall I think 2008 was a great year for WPF as a platform, and I can’t wait to see what the future has in store for us. What were your big milestones for WPF in 2008?
I thought I would post a short explanation of a small issue I encountered recently with the XAML compiler, in particular with its treatment of linked XAML files. Linked files can be created for all kinds of project files, most commonly code files like .cs or .vb files but it can be anything else. This is almost always done to when you need the same thing in multiple places but don't want to go to the trouble of managing two copies of it. They are added by changing the option in the open file dialog triggered when you choose "add existing item" in your project from 'add' to 'add as link' as shown below.
XAML files linked in this way will compile fine, but the relative path from the root of the project used as the key when retrieving them is lost when they are linked. Assuming we had a project structure like this. A project Foo contains a folder called resources, which in turn contains a XAML resource dictionary called stuff.xaml. Project Bar also has this same structure, but stuff.xaml has been added as a linked file from the Foo project.
If we open up these two using reflector we see that in Foo the path to the resource dictionary has been specified correctly.
In the decompiled output for Bar we can see that they key to stuff.baml is different.
Because resource keys are so...err...key in locating and using them this will substantially affect most usage scenarios for this resource. One way to work around this problem is to factor these resources out into a library DLL that just contains these resources, however this doesn't work with all resource usage scenarios.
The recent release of .NET 3.5 SP1 adds hardware-accelerated pixelshader effects to WPF. In addition to replacing a number of the slow software-rendered BitmapEffects with shiny, fast hardware-accelerated ones they also added the ability to write your own effects. In order to provide an easy-to-digest package for those who don't want to write shaders themselves, but DO want to add cool effects to their app I created a community shader project, hosted on codeplex at http://www.codeplex.com/fx . If you're a graphics wonk chances are you can write much cooler effects than the ones I've already build, so please feel free to contribute.
Here is an overview of the effects in the 0.1 release:
Also I've been fortunate enough to enlist the help of a _real_ graphics wonk already, who added a nice radial blur effect. Thanks OJ.
WPF architect Greg Schechter was written a series of articles on the new hardware accelerated shaders recently released in beta form, as part of .NET 3.5 SP1 beta. This article includes some sample code that was enough to get me started down the path of writing a very simple shader effect - I've dubbed it "watery" but it's more just "wavy".
In this example the "waviness" of the effect is bound to the value of the slider at the bottom of the screen. One thing that I thought was very nicely done by the WPF team was the mechanism for passing parameters from WPF into the HLSL code using an attached property declaration like this:
public static readonly DependencyProperty WavinessProperty =
DependencyProperty.Register("Waviness", typeof(double), typeof(WaterEffect), new UIPropertyMetadata(0d, PixelShaderConstantCallback(0)));
The last part is the most important bit, which wires up the dependency property that has been defined in our C# WPF code to a constant in the [very crude] pixel shader HLSL code:
sampler2D implicitInput : register(s0);
float waviness : register(C0);
float4 main( float2 Tex : TEXCOORD ) : COLOR
{
float4 Color;
Tex.y = Tex.y + (sin(Tex.x * waviness)* 0.01);
Color = tex2D( implicitInput, Tex.xy);
return Color;
}
At the moment this sample is very much alpha, and sometimes does some very weird things to the contents of the window. Regardless I'm very impressed at how approachable the WPF team have made this stuff.
I ran into this issue when IE8 was first released and it doesn't seem to have gotten much visibility elsewhere. IE8 Beta 1 breaks image downloads for WPF applications when the image has a web uri. From the stack trace this seems to be caused by wininet.dll not returning the internet cache folder correctly. A typical call stack for the error message might look something like this (when the uri specified is a http uri, I haven't checked FTP)
A first chance exception of type 'System.ArgumentException' occurred in PresentationCore.dll
System.ArgumentException: Value does not fall within the expected range.
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at MS.Win32.WinInet.get_InternetCacheFolder()
at System.Windows.Media.Imaging.BitmapDownload.BeginDownload(BitmapDecoder decoder, Uri uri, RequestCachePolicy uriCachePolicy, Stream stream)
at System.Windows.Media.Imaging.LateBoundBitmapDecoder..ctor
(Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy requestCachePolicy)
at System.Windows.Media.Imaging.BitmapDecoder.CreateFromUriOrStream
(Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy, Boolean insertInDecoderCache)
at System.Windows.Media.Imaging.BitmapImage.FinalizeCreation()
at System.Windows.Media.Imaging.BitmapImage.EndInit()
It seems that others have also noticed and reproduced this error on both XP and Vista. In general image downloading seems to be one of the more perilous operations for WPF to perform. I've seen a number of other process-terminating exceptions caused by images with invalid formats etc, so it is best to be cautious. For this problem caused by IE8 "manually" constructing a web request and downloading the image yourself, and then creating a BitmapImage from the downloaded stream works and does not cause the error.
Both Silverlight 2.0 and WPF support the notion of resources, which can be associated with almost any element - in both SL and WPF the widely inherited-from FrameworkElement (and in WPF the FrameworkContentElement) class exposes a Resources property of type ResourceDictionary. The Resources dictionary is a simple collection of key-value pairs. Almost anything can be stored as a resource, from brushes to templates, data sources, converters, dates, strings and any other .NET object which can be represented as XAML. The key used to retrieve resources can be anything too; however strings or static fields are used.
The current (beta 1) implementation of Silverlight 2.0 has some notable differences to WPF however. The first is that resources cannot be factored into separate files and referenced using the <ResourceDictionary.MergedDictionaries> feature of WPF resource dictionaries. This is unfortunate as it means that the App.xaml file will probably become large and unweildy (and potentially an integration pain point between developers and designers) in Silverlight.
Another notable difference is the way resources are retrieved in code. In WPF FrameworkElement and FrameworkContentElement have methods FindResource and TryFindResource which will look for a resource in the current dictionary with the key provided. If no resource is present for this key these methods traverse the logical tree to ancestor nodes looking for the resource that matches the key. Silverlight 2.0 beta 1 does not expose methods like these (possibly due to the de-emphasis of the separation between the visual and logical trees as concepts in its programming model). You can refer to resources on an object by their key, but no tree searching is performed - if the resource doesn't exist in that resource dictionary then null is returned.
As with WPF, resource retrieval in XAML is done using the {StaticResource} markup extension in Silverlight 2.0. When this markup extension is used the tree is traversed looking for a resource with the specified key. There is no {DynamicResource} markup extension in Silverlight 2.0.
In Silverlight 2.0 Resources can either be given a key (by setting the x:Key attribute) or a name (by setting the x:Name attribute) and both will work for item resolution. Attempting to add two elements with the same value (one by setting the key attribute, the other by setting the name attribute) results in a run-time error.
Silverlight 2.0 promises to bring much of the goodness of WPF to the browser. So how does one of my favourite WPF features - data binding - fare in the new Silverlight 2.0 beta 1 world? To help answer this question I drew up the following summary:
| Similarities |
Differences |
|
Binding Object – the Binding object is present in both SL 2.0 and WPF. Many simple binding scenarios will probably work between the two without modification, but if you’re using the more complex properties of the Binding object in WPF you’ll need to revisit that for Silverlight 2.0 code. The INotifyPropertyChanged interface is also used for change notifications on data objects to be propagated back up to the binding infrastructure. |
Binding Object – although the Binding object exists in SL 2.0 and WPF the SL 2.0 version is very pared-back compared to WPF, having only 5 properties: Converter, ConverterCulture, ConverterParameter, Mode and Source, in contrast to the 20 or so relevant properties the WPF binding object exposes or inherits from its base types. One of the most key properties on the WPF binding object – the path isn’t even a property at all on the Silverlight equivalent. Instead it is specified as a constructor property. Also the Path in SL 2 can’t drill down into indexes or attached properties the way it can in WPF, however you can still access sub-properties on objects. Josh has some screen-shots from Reflector showing the difference in size of the two types. |
|
Data Context – the concept of a Data Context for a control which is propagated from a parent control down to child controls is shared between WPF and Silverlight 2 beta 1. As with WPF the FrameworkElement type adds the DataContext property in Silverlight 2.0 beta 1. |
Binding Markup Extension – although syntactically quite similar (albeit with less properties) bindings in Silverlight are quite different internally to their WPF counterparts because they don’t use a managed Binding Markup Extension. In fact, from what we can see of Silverlight 2.0 beta 1 there ARE no markup extensions of any kind. The Silverlight documentation discusses them here but they don’t appear anywhere in the object model. Presumably the implementations of the various markup extensions are hard-wired into the Silverlight runtime. |
|
Converters – One of my favourite extensibility points of WPF has been completely migrated to Silverlight 2.0 beta 1 (possibly due to the simplicity of the IValueConverter interface). A converter and converter parameter can be supplied as part of a binding. |
Binding to Other Elements – one common scenario is to bind two pieces of WPF UI to each-other. For example binding a slider to the “zoom” on a ScaleTransform. This is done by setting the ElementName property to the name of the element in the current name scope. Silverlight 2.0 beta 1 doesn’t support ElementName as a parameter when binding. This limitation can be worked around fairly easily by creating a simple object with a single property of the type you wish to glue together (double is probably the most common scenario) and then implementing INotifyPropertyChanged on that type. This approach is described in more detail here. |
|
Data Templates – Both WPF and Silverlight 2.0 beta 1 share the concept of templates that can be applied to data. One area where SL 2 differs is that in WPF you can specify a template by the type of object the template applies to (by setting the DataType property). If this is set and no key is supplied (and the Datatemplate is appropriately scoped) the template becomes the default appearance for that type of data item in WPF. The DataTemplate type in Silverlight 2.0 beta 1 doesn’t include this property so all data templates are “keyed” in resource dictionaries or entered in-line. |
Data Triggers – Data templates in Silverlight 2.0 beta 1 don’t have data triggers like their WPF counterparts. This can be worked around using a converter, but requires more code than a data trigger would. Given the changes introduced by the new Silverlight control templating model data triggers going missing are the least of your worries. |
|
TemplateBinding – TemplateBinding is fundamental to styling of controls, and thus has also made its way over from WPF to Silverlight. |
|
|
ObservableCollection
- the generic collection with built-in change notification, ObservableCollection has also been included in Silverlight 2.0 beta 1. The INotifyCollectionChanged interface (the collection equivalent of INotifyPropertyChanged) is also present, and implemented by ObservableCollection.
|
|
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].
I've mentioned before my fondness of using SVG content and converting it to XAML. Today I read about the Visio to XAML converter on Saveen's blog and about the approach taken (saving to SVG and then using a third party library XamlTune to convert to XAML). I was fairly interested in reading about this approach because I had tried this myself before (not as an add-in, just saving Visio to SVG and then manually converting) with very limited success.
After a few tests I'm very impressed with XamlTune, and recommend you try it if you need to convert from SVG to XAML. For comparison here is the original SVG file that inspired my glossy radial brushes previously.
Here is the output from XamlTune (using the default settings)
And here is the output from Ab2d SVG Reader (once again, using the default settings)
I recently wanted to convert some nice glossy SVG-based shapes into WPF. Here is the result.
The original brushes came from here on the OpenClipart.org site.
I attempted to convert them using WPF 3D Graphics' SVG-XAML converter, however this proved unsatisfactory for 2 reasons: firstly there was a significant loss of fidelity in the conversion to SVG (which is unusual since the converter is pretty good), and secondly the conversion approach was to create lots of Shape-derived classes absolutely positioned in Canvas elements, so instead I ported and hand-tweaked the brushes. This class uses a serise of GeometryDrawings and should be quite light-weight on tier 2 graphics hardware. On tier 1 the story is a little different, since RadialGradientBrush is NOT hardware accelerated on these platforms. Although the converted brushes are simpler than the SVG originals (for example the slight "ripple" in the reflection line), I'm still pleased with the result as they do look nice.
Download the XAML file with theese brushes in them here
If you’ve written your own custom attached property in WPF you may come across the following error when you try to look at a Window, Page or UserControl that attempts to use the attached property in the WPF designer in Visual Studio 2008:
The Property '[your custom attached property name]' is read-only and cannot be changed
The puzzling thing is that you can get this error message even if your attached property is not defined as read-only, and has a setter and isn’t registered as read-only in the Property Meta-data. Even more confusingly the property can seem fine and working correctly at run-time.
So what’s going on? Although the error message is a little cryptic the error is caused by a type mismatch between the property type of the dependency property (when it is registered) and the second parameter accepted by the “Set[YourPropertyName]” method that is defined when creating an attached property. For example if you’ve registered an attached property like this:
public static readonly DependencyProperty MyCustomProperty =
DependencyProperty.RegisterAttached("MyCustom", typeof(MyOtherType), typeof(MyType));
The property meta-data system expects that when you set this attached property the value will be of MyOtherType (the second type parameter is the type that "owns" the dependency property, usually the type you declare the DP in). Type WPF designer is aware of this, for example if you attempt to assign this property in XAML from a string and no appropriate converter from string to MyOtherType is present it shows an error.
However as part of your attached property declaration you are also obliged to create "Get[PropertyName]" and "Set[PropertyName]" wrapper methods for getting and setting the attached properties.
public static void SetMyCustom(DependencyObject depObj, TheWrongType value)
{
depObj.SetValue(MyCustomProperty, value);
}
If we include the wrong type as the second parameter in one of these declarations – in this case a type that is not assignable from MyOtherType - this isn’t a compile time error (because these methods aren’t part of any interface we’re implementing or generic type) and may work fine at run-time if that code path isn’t called (I believe that XAML bypasses the property wrappers). The designer does pick up on the discrepancy, and considers that type dependency property is read-only because it couldn’t find an appropriately typed "Set[YourPropertyName]” method. Of course this error is easy to resolve once you know the cause, by changing the type of the second parameter to be the same as, or assignable from the type that was used when you declared the dependency property.
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>
When you create a WPF application in Visual Studio the 'entry point' for the process, where the application is created and the first form displayed, is not directly apparent. Instead the start-up behaviour is controlled by setting attributes on the App.xaml, such as which form to start. At compile-time WPF generates the real entry point method using the classic static void Main() method signature in C#, or Public Shared Sub Main() in VB.NET. Inside the Main() method WPF creates an instance of one of your classes which derive from System.Windows.Application, and calls the blocking Run() method on this Application-derived class which keeps the process "alive". Generally this is a useful abstraction, hiding away some of the lower-level details of how the WPF application starts up, however sometimes you want to provide your own version of the Main() method. So how do you tell WPF to use your main method and not its own?
The way WPF knows to create the Main() method for a particular xaml file is through the build action property set for App.xaml - it has a build action of ApplicationDefinition. By changing this to Page WPF won't create the Main method and you can provide your own in a regular class file you add to the project.
It is important to note that when you provide your own Main() method that the method should be adorned with the STAThread attribute, which sets the threading model for the process. You can either look at the current generated Main() method for your project as a starting point (it should be in the obj folder) or use the following code:
[System.STAThreadAttribute()]
static void Main()
{
YourApplicationName app = new YourApplicationName();
app.InitializeComponent();
app.Run();
}
Visual Studio 2008 comes with some useful snippets for WPF development out of the box. In C# type propa followed by two TAB keystrokes inserts the a snippet for an attached dependency property, including a getter, setter and the registration code.
Similarly propdp followed by two TAB keystrokes inserts a snippet for a dependency property. As you would expect the snippet for this is very similar to the attached dependency property, except in this case it calls DependencyProperty.Register() instead of DependencyProperty.RegisterAttached().
Visual Basic in Visual Studio 2008 has even more snippets defined including RoutedCommand and RoutedEvent registration snippets, and a RoutedCommand handler snippet. I'm primarily a C# kind of guy, so I never realized that VB.NET had the two snippets I mentioned above for C# in VS2005 (with the VS Extensions for .NET 3.0 installed). It seems in this case C# is playing catch-up to VB.NET.
You can look at all the snippets (not just the WPF-specific ones) by selecting "Tools->Code Snippet Manager" from the VS menu. You can of course define your own snippets, but it is nice to see these ones built in to VS2008.
One thing people often ask me about WPF is the learning curve - how long will it take for me to be productive, and how long before I know the "whole api". The sad fact is that knowing the "whole" of WPF is a difficult proposition, due to its size. A lot has been written about the learning curve associated with the XAML programming model and the vector-based retained rendering system, but regardless of these factors WPF is going to have a learning curve due just to its size. WPF is big by any standard - with almost 1600 public types in v3.0 and almost 3600 classes it is approximately the same size as Windows Forms and ASP.NET combined. It spans 3 main assemblies PresentationCore.dll, PresentationFramework.dll and WindowsBase.dll (I'm excluding the Aero, Classic, Luna and Royale "theme" assemblies, and the UI automation and ReachFramework in my calculation of the size since they are not WPF specific). The good news is that you don't need to know the "whole api" in order to be productive and start using WPF. Here's a helpful infographic to convey the message:
|
ASP.NET 2.0
|
WinForms 2.0
|
WPF
|
|
|
|
|
|
|
|
1098 public types 1551 classes |
777 public types 1500 classes |
1577
public types 3592 classes |
Visual Studio 2008 and .NET Framework 3.5 were released earlier this week. Visual Studio 2008 is the first version of Visual Studio released since WPF and the rest of .NET Framework 3.0 were released a year ago. VS2008 includes a WPF designer and XAML editor, and allows targeting of .NET 3.0 or .NET 3.5 when developing and compiling solutions.
.NET Framework 3.5 includes a host of new language features for C# 3.0 and VB9. Most of these language features exist primarily to support LINQ, however .NET 3.5 also includes some enhancements to WPF. An overview of the changes in 3.5 can be found here on MSDN. Kevin Moore also recorded an interview for Channel9 before he left Microsoft discussing some of the changes that were coming in 3.5. Dr Tim Sneath also listed off 15 of his favourite enhancements to WPF here.
Some of my favourite enhancements for WPF in .NET 3.5 are supporting XBAP applications in FireFox (on versions of Windows) and the data binding and IErrorProvider enhancements (thanks in part to Paul Stovell).
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.
For some kinds of applications displaying as “maximised” is not enough, either because they want to make use of every single pixel of screen-space possible, or because displaying the task bar and associated window trimmings would be distracting or confusing for users of the application. The PowerPoint slide viewer is a good example of this, starting in “kiosk” mode to create a more immersive presentation and preventing audience members becoming distracted by the task bar and things in the notification area. Fortunately doing this for your own WPF application can be achieved quite easily by setting the WindowStyle and WindowState properties on the Window as shown below.
Xaml Code
<Window x:Class="LearnWPF.KioskMode.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="LearnWPF.KioskMode"
WindowStyle="None"
WindowState="Maximized">
WindowStyle should be set to None (this draws the window without any border, and is the same state you can use to create a WPF splash screen) and the WindowState set to Maximized. These properties can also be set from code. One small issue we have observed is if you try to change the WindowStyle in code after the WindowState has changed to Maximized the resulting window will be drawn without a border, but won’t display over the top of the status bar.
If you're from further down the developer end of the designer-developer spectrum chances are you've been avoiding creating your own images, and on the look-out for some good XAML icons and vector images to include in your application, since bitmap images often look blurry. While we included some XAML image resources in the past there are plenty of other good resources out on the web if you know what to look for. Although no graphic designers are sitting at home churning out XAML code per-se, there are plenty of ways to convert the vector content they produce (like SVG files or Adobe Illustrator .ai files) to XAML. Here we've included two sites to get you started, and approaches for converting other vector formats into XAML.
OpenClipArt.org
is a repository of "open source" vector art stored in SVG format. SVG is an XML-based vector format (and thus you could transform it yourself to XAML if you wished), however there is already an excellent tool from Andrej Benedik from WPF-graphics.com for doing this. You can also use the free and open-source InkScape editor to edit SVG files. Many of the images are licensed in the public domain, meaning you can use them for pretty much any purpose. Shown below is a mac-like SVG image of a new computer by "big red smile" (possibly not their real name) beside the converted XAML version displayed in Kaxaml.
vecteezy.com
is a "free vector art of the day" type site, with nice vector backgrounds and images released under a variety of terms (so make sure you check the license on the image). Most of the files here are Adobe Illustrator .ai files. If you have Illustrator installed you can use Mike Swanson's exporter, however if you're a developer that is less likely. Expression Design (now included in MSDN universal) can import Illustrator .ai files. For some reason it can't open them directly (if you select File->Open and then try to browse for a .ai file it won't let you) so you need to create a new blank file, and then select File->Import and browse for the .au file. Once in Expression Design you select File->Export and select Xaml as the type from the dropdown list. You can then choose how the xaml is formed when you export it.
.NET applications traditionally suffer from slower startup time than native applications, caused by the time to load the .NET framework DLLs, JIT time etc. WPF applications have further things that can slow down cold start-time, the extra PresentationCore, PresentationFramework and WindowBase assemblies that need to be loaded and the start-up of the PresentationFontCache service. Any tips to help improve start time is therefore most welcome.
Doug Stockwell recently released an updated version (v1.6 build 2802) of his free RikReader WPF-based RSS Reader, with considerably-improved start time. I've been using RikReader as my primary RSS reader since it was first released, and in previous versions the start time stood out as a blemish on an otherwise excellent app. The performance characteristics of older versions of RikReader were a little different to usual applications, in that the hot start-times were still quite bad (>10 seconds for my list of a couple of hundred feeds), whereas the new version hot-starts in about 4 seconds (roughly equivalent to Expression Blend, Thoughtex and most other WPF applications I use). Cold start-times for RikReader had also improved noticeably.
I contacted Doug to find out what he'd done to achieve this great improvement, and was pleased to hear of the simple change he had made. Instead of using XAML to declaratively set the DataContext for his main window, which contained all the RSS feed data, he switched to setting it programmatically in the Application.OnActivated. This sounds like a tip that would improve the perceived performance of the app (making the main window display more quickly) without improving the actual performance, however it seems to have also improved the actual start time considerably. As with all things performance-related your mileage may vary, and it pays to measure yourself. Fortunately the simplicity of this change means it will be easy to swap between the two methods of setting the DataContext.
"Floral" patterns seem to be the new gel button, as far as design trends go. Drawing on inspiration centuries old here are two finely crafted Damask brushes based on images from the inspiration gallery. OK, so maybe finely crafted was a bit of an over-statement (after all you can see the tiling lines in one of the brushes, but that makes it feel more "organic" or whatever). Enjoy responsibly here.
I recently had a scenario where I wanted a number of WPF text fields to support a co-ordinated undo/redo capability. The regular undo/redo facility in WPF (and most control tool-kits) applies to a single field. Changes to that field can be undone or re-applied, but each field is very much stand-alone and independent of those around it. I wanted a facility where a number of fields could be logically grouped together for the purposes of undo and redo operations, since I thought this would more closely match the user’s mental model of how the system worked. Changes should be tracked across multiple fields, and undo and redo operations should occur in the order they are made across the different fields. I also wanted the focus to change also as undo/redo operations occurred.
Using it
Using the shared undo/redo scope I created is quite easy, and can be accomplished just using XAML. An “undo manager” is set as an attached property as shown in the code sample below. The SharedUndoRedoScope attached property on the UndoManager type I created is set for a StackPanel. The property is inherited, so it can be set on a parent element (like a panel) and applied to all the children. In the example below the two textboxes share a common “undo/redo manager” because they are both children of the same stackpanel
XAML Fragment
<StackPanel>
<undo:UndoManager.SharedUndoRedoScope>
<undo:UndoManager />
</undo:UndoManager.SharedUndoRedoScope>
<TextBox Text="text box 1" />
<TextBox Text="text box 2" />
</StackPanel>
The sample application (shown below) creates two shared undo/redo scopes, and a third set of elements that don’t have any special undo/redo capabilities for comparison. You can download the sample application with full source from here.
Implementation
The undo manager has two main roles internally – firstly to track changes in the text boxes across all the text boxes it is managing (so they can be un-done in that order), and then actually stepping in at the right time and triggering the undo.
Tracking changes is relatively straightforward - When text changed events are fired for textboxes the undo manager is managing, it pushes the changes onto an internal undo stack. Only changes that are important are added. For example many TextChanged events will fire when someone is typing with the UndoAction of Merge, these aren’t necessary for the UndoManager to track.
Actually triggering Undo and Redo operations is slightly trickier, and I needed to work around some implementation details of the TextBox and RichTextBox controls in WPF. Both of the TextBoxBase-derived controls wire up command handlers on an internal class called the TextEditor inside their constructors. In here amongst a group of internal classes some handlers for ApplicationCommands.Undo and ApplicationCommands.Redo are created. When the WPF TextBox and RichTextBox handle these ApplicationCommands they mark them as “handled” to prevent them bubbling up to your code. While usually this is quite desirable, preventing your application code having to care about undo and redo commands being fired which the TextBox has already taken care of in this instance it causes a problem because the UndoManager needs to step in at this point and potentially do something different. Fortunately with routed events you can optionally choose to “see” routed events that have already been marked as handled, which is what I have done, by calling the AddHandler() method overload that takes a third Boolean parameter for specifying if you want to see the handled events.
C# Code
textBox.AddHandler(CommandManager.PreviewCanExecuteEvent, new CanExecuteRoutedEventHandler(manager.CanExecuteRoutedEventHandler), true);
When the UndoManager sees an Undo or Redo ApplicationCommands it pops an item off its internal undo or redo stack, and calls the Undo() or Redo() method on the text box on the stack as appropriate.
Currently the implementation only tracks changes in TextBox and RichTextBox. You could extend it to handle undo and redo of selection in lists and toggling of checkboxes etc. also.
In implementing this I was conscious of trying to create a programming model that “meshed” with the rest of WPF, in particular I thought choosing an inheritable attached property was an appropriate way to expose this kind of feature. I would be keen to hear any feedback on this aspect of the implementation, in particular how this could have been done better.
Update: Roman was kind enough to point out some embarrassingly obvious flaws in my implementation (see comments below), which are fixed in the source code you can download now.
The team at Vertigo that created the nice Family.Show WPF sample wrote up a summary of their key feedback and take-aways from the project, and they're well worth a read. One tip in particular that I liked was regarding data binding, and default values. They advised setting the default values in bindings to give a better design-time experience in tools like Blend, which uses the defaults when it displays the UI in the designer. The source-code is also quite tractable, consisting of about a dozen xaml files and ~20 classes.
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>
WPF is all about creating great user experiences, but the user experience of your application starts long before your code starts running on the end user’s machine. An important first step is launching your application and getting your code down onto the end user’s machine as quickly and simply as possible. ClickOnce makes great strides in improving this deployment experience for .NET 2 rich client applications and WPF applications, however the default publish page that is generated by visual studio can be greatly improved. In this article we discuss techniques to reduce the amount of decision-making required of the end user, and give them an experience tailored to their operating system version, browser version and system capabilities to minimize the size of the download for clients on sub-broadband network connections.
The broad approach we’ll be trying to take is:
- Identify potential users who can have WPF installed and can start using our application right away.
- Create a hyperlink to launch our application that works for their browser.
- For those users who need to install .NET Framework 3.0 direct them to do so and then give them a link to launch our application.
- For those users who we believe will not be able to run any WPF applications, give them the option of trying to install .NET Framework 3.0 and our application, but warn them that it may not work and why.
Identifying WPF-capable clients via the User Agent String
The user agent string is an optional parameter that can be sent as part of the HTTP headers when making a request to a web server. The user agent string is intended as a means for the client to identify itself to the server, so the server can optionally adjust the response if appropriate. Although the content of the user agent string is entirely free form and does not carry any guarantees regarding accuracy or authenticity, it is still broadly useful for identifying browser versions and client capabilities.
Identifying WPF-capable Clients using Internet Explorer
By default Internet Explorer includes the versions of the .NET framework present on the client in the user agent string like so:
Example Microsoft Internet Explorer 7.0 User Agent String on Windows Vista:
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)
(an exhaustive list of User Agent strings can be found here)
Fortunately the System.Web namespace contains the helpful HttpCapabilitiesBase class which can perform the task of parsing out the available CLR versions on the client. The code below assumes that WPF will be included in versions of .net framework subsequent to version 3.
C# Code
HttpCapabilitiesBase httpCapabilities = HttpCapabilitiesBase.GetConfigCapabilities("system.web/browserCaps", HttpContext.Current.Request);
Version wpfV1FinalVersion = new Version("3.0.04506");
Version[] clrVersions = httpCapabilities.GetClrVersions();
foreach (Version clrVersion in clrVersions)
{
if (clrVersion >= wpfV1FinalVersion)
{
// client has WPF
}
}
For clients that have WPF installed we can simply link directly thru to the .application file generated by ClickOnce, which will prompt the user to install our application. For clients that don’t have the .NET Framework 3.0 or better installed we can direct them to where it can be downloaded, and then prompt them to launch our application. Alternatively we could launch the ClickOnce bootstrap setup.exe which can install pre-requisites on the client’s machine.
Identifying WPF-capable Clients using Mozilla Firefox
Unlike Internet Explorer, Mozilla does not include the .NET CLR versions installed on the client computer as part of the user agent string. However since Windows Vista comes with WPF pre-installed on it we can check the Mozilla user agent string for the Windows version, and for Vista users assume they have WPF. For Windows XP and Server 2003 users we can indicate that WPF is supported, that we can’t tell if they have it or not and provide them with a two-step installation process (step 1 being to install .NET Framework 3.0, and step 2 to install our application).
Example Mozilla Firefox 2.0 User Agent String on Windows Vista
Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1.2) Gecko/20070219 Firefox/2.0.0.2
For users that don’t appear to be using Windows, or who are using an older version of windows we revert to our fallback position – tell them that we believe they won’t be able to run our application but they are welcome to try with the two steps listed above.
Serving the Right Files to Launch our Application in Mozilla Firefox
Unfortunately identifying WPF-capable clients using Firefox is only the beginning, because the .application file used to launch your ClickOnce application does not work in Firefox (although there are some add-ins for Firefox that allow them to work). Cross-browser support was de-scoped from ClickOnce between Beta 2 and RTM, but luckily there is a work-around where Mozilla users running windows can be served a .appref-ms file. A .appref-ms is a kind of ClickOnce version of a desktop shortcut, which will then correctly launch your application for them (if they have .NET Framework 3.0 installed). The easiest way to get your hands on one of these files for your application is to deploy it using IE and ClickOnce, and then copy the shortcut that is created in the start menu – that is a .appref-ms file. You will need to deploy this file yourself. If your application is being served up by IIS you may need to add a MIME type mapping for .appref-ms files also.
Why not just direct users to the ClickOnce bootstrap setup.exe program?
The ClickOnce bootstrap executable (setup.exe) is a useful way to launch your application installation in some cases, however there are some issues with it. Setup.exe doesn’t work with Mozilla Firefox if it is the default web browser on the client system. Also Firefox users on Vista see a moderately concerning prompt about running executables downloaded from the internet. For users who already have the pre-requisites necessary to run your application on slower network connections the 400K download of setup.exe is time and bandwidth wasted. For users on XP SP2 running IE who do not have .NET 3.0 installed setup.exe is a good choice.
A sample user control and code for detecting clients with WPF installed can be downloaded from here.
Developers new to WPF are sometimes a little confused when the Width and Height properties of elements report as ‘NaN’ (Not a Number) in code, when these very same elements are clearly visible and do have a width and height. What’s going on?
The short answer is you can interpret ‘NaN’ to mean “not set”. The Width and Height properties of Buttons, TextBoxes and most other controls are inherited from the FrameworkElement superclass. These normal .NET properties in turn backs on to a DependencyProperty (the WidthProperty in the case of Width) that has been registered for FrameworkElement in the class constructor with a default value of ‘NaN’. Until you set that WidthProperty to something, or set the Width .NET property in code or Xaml the Width will stay as ‘NaN’. If you wish to know what the on-screen size for an object is you should instead consult the ActualWidth and ActualHeight properties, which always reflects the actual width and height of the object on screen. These properties are sometimes a little hard to find given the number of public properties and methods on many objects in WPF.
The query-string portion of the URL in a HTTP request is a useful place for passing state between pages in web applications. It is therefore not unexpected that developers would also want to use this common idiom for passing information to WPF XBAPs (Xaml Browser Applications) that they wish to integrate with their site.
Fortunately this is not too difficult, except there is one trick you need to know. The ActivationUri property of the ApplicationDeployment object gives you the Uri that launched the XBAP. To get access to this you need to add a reference to System.Deployment.dll. To get hold of the current ApplicationDeployment object for your XBAP use the static CurrentDeployment property of the ApplicationDeployment object like so.
Uri launchUri = System.Deployment.Application.ApplicationDeployment.CurrentDeployment.ActivationUri;
If you launch this code from inside Visual Studio to debug it won’t work. The way it is launched means when you press F5 means that the CurrentDeployment property will be null and thus cause a NullReferenceException. You can use the static IsNetworkDeployed property of the ApplicationDeployment ojbect to check if you’ve been deployed over the network (for an XBAP application hosted in the browser this should be true).
if (ApplicationDeployment.IsNetworkDeployed)
{
Uri launchUri = ApplicationDeployment.CurrentDeployment.ActivationUri;
}
So far so good, and now for the trick. In order for the deployment infrastructure to actually pass anything through to you via this API you need to set a property in your manifest. Fortunately Visual Studio 2005 gives you a convenient way to do this. Select project properties in Visual Studio 2005 and then go to the publish tab, as shown in figure below.

Now click the Options button and in the options dialog check the box that says “Allow URL parameters to be passed to application”. Click OK and re-build your application.

Once you’ve verified that the URI is being passed through you can use the Query property of the URI to return the query-string portion, and parse out any information you need from there.
The golden rule of WPF multi-threading is "Thou shalt only update UI using code that runs on the same thread as the UI." If you happen to be running on another thread you use the Dispatcher property of any of your UI elements to get back on to the UI thread. This raises the question - how do I know when I'm on the UI thread? It turns out the Dispatcher has two methods you can use - CheckAccess and VerifyAccess. CheckAccess returns true if you are on the UI thread. VerifyAccess is a more aggressive version of this method and throws an exception if you are not running on the UI thread. Strangely CheckAccess is marked with attributes to prevent it appearing in intellisense in Visual Studio. This CheckAccess method is the WPF equivalent of the InvokeRequired property of the Control object in Windows Forms, which had similar thread-affinity rules to WPF.
On a related note the Arbel Network Blog has an excellent tip that uses reflection inside of a method to call back in to itself (but on the UI thread) if necessary. They also have a link to Nick Kramer's threading white paper.
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.
WPF enthusiast Gill Cleeren emailed me recently to let me know about his article on WPF Styles and Triggers that recently went live on the MSDN Belgium site. It provides an excellent starting point for those starting out with styles, and shows enough code to be useful without being overwhelming.
Also noteworthy from MSDN Belgium last year was Jelle Druyts' article on WPF Commands.
WPF’s animation system is used for changing properties such as width, position or color of an element over time. When animations finish they can either return the property being animated to its original value, or “hold” their final value. This behavior is controlled by the FillBehavior property of the animation. After animating properties where the animation holds the final value developers can run into problems when they try to change those very same properties via code. The changes seem to have no effect!
This strange behavior is a side-effect of the WPF dependency property system. The dependency property system is a special property system built by the WPF team. It allows element properties to be inherited from their container element, animated, data bound and set via regular .NET code. One side-effect of this kind of property system is that a single property on an element could get its value from a number of potential sources. In the regular .NET property system property changes are fairly transparent, with more recent changes over-writing previous ones. The more declarative nature of WPF makes this temporal-based property system less useful. Instead the value an element property has is based on a precedence system which is detailed here. Animations have a higher precedence than everything else, so changes made by them always over-ride other possible property values. While this seems sensible and in most cases gives us the outcome we would expect it can be a little confusing for animations that hold their value after they complete. When the animation ends it holds the property at a certain value, and because animations takes precedence over other ways properties can be set it prevents the property being changed through normal .NET code.
It is possible to change this using a little code to remove the animation when it completes, however a better approach is to not use the HoldEnd fill behavior if you need to subsequently change a value in code. Instead you should use the Stop FillBehavior and “fake” the HoldEnd by handling the completed event for the animation and setting the property value to the value you wish to finish at.
The following code shows a text block that has its FontSize animated to 35 pixels when it loads.
<TextBlock Grid.Row="0" Text="WPF is great" FontFamily="Lucida Sans" Foreground="#feca00" HorizontalAlignment="Center" Name="windowHeading">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<EventTrigger.Actions>
<BeginStoryboard Name="windowHeadingStoryboard">
<Storyboard>
<DoubleAnimation To="35" Storyboard.TargetName="windowHeading"
Storyboard.TargetProperty="FontSize" Duration="0:0:5"
Completed="FinishedAnimation" FillBehavior="Stop" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
The very simple C# code to simulate the HoldEnd behavior is shown here in the Completed event handler for the animation.
void FinishedAnimation(object sender, EventArgs e)
{
windowHeading.FontSize = 35;
}
The obvious drawback with this approach is the fairly tight coupling between the markup and application code that is required for it to work.
In film editing a “wipe” is a technique for transitioning from one scene to another. We can create a similar effect in WPF by overlapping two items (for instance by placing them both in a single-cell grid) and specifying a LinearGradientBrush as the OpacityMask on the top-most item. Then by animating the Offset property of the GradientStops of the brush we can create the wipe effect. By modifying the start time of the second DoubleAnimation you can modify the width of the “in-between” area. An earlier start-time will give a narrower region that shows pieces of both items.
Xaml Code (RC1)
<Window x:Class="LearnWPF.WipeEffect.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="LearnWPF.WipeEffect" Height="500" Width="700" Name="mainWindow"
>
<Grid>
<Image Source="Images/UFO.jpg" />
<Image Source="Images/ladder.jpg">
<Image.OpacityMask>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Offset="0" Color="Black" x:Name="BlackStop"/>
<GradientStop Offset="0" Color="Transparent" x:Name="TransparentStop"/>
</LinearGradientBrush>
</Image.OpacityMask>
</Image>
</Grid>
<Window.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="TransparentStop"
Storyboard.TargetProperty="Offset" By="1" Duration="0:0:2" />
<DoubleAnimation Storyboard.TargetName="BlackStop"
Storyboard.TargetProperty="Offset" By="1" Duration="0:0:2"
BeginTime="0:0:0.05" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Window.Triggers>
</Window>
| A BeginTime of 50ms for the second animation produces a "tighter" wipe |
A BeginTime of 1 second for the second animation produces a more gradual transition |
|
|
Reflecting another visual in WPF is typically done using a VisualBrush and a ScaleTransform to “flip” the content upside down, as shown in this nice example on xamlog. In order to perform the “flip” the ScaleTransform needs the centre about which this should occur. Unfortunately the centre position of an element is not directly exposed via a property, so the net result of this is that for simple reflections (where you know the size of the source) you typically end up hard-coding the centre of the transform in your XAML . A recent poster in the WPF forums asked if it was possible to avoid this hard-coding. One approach which I’m going to explore here is to use data binding and a fairly generalized “arithmetic converter” capable of multiplying, dividing, adding to or subtracting from values as part of the binding operation . In order to obtain the center for our ScaleTransform we bind the CenterX property of the transform to the ActualWidth of the element we want to reflect, and multiply by 0.5. Similarly we can bind the CentreY property of the transform to the ActualHeight * 0.5. The arithmetic operation is specified to the converter using the ConverterParameter property of the binding. In order to use the converter we first need to add a namespace alias to the CLR namespace that the converter is contained in.
<Window x:Class="LearnWPF.ReflectionWithoutHardCodingWidth.Window1"
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
xmlns:cvrt="clr-namespace:LearnWPF.Converters"
Title="LearnWPF.ReflectionWithoutHardCodingWidth" Height="300" Width="300"
>
Then we can use the converter like so to perform the binding.
<ScaleTransform ScaleX="1" ScaleY="-1"
CenterX="{Binding ElementName=PhotoBorder,
Path=ActualWidth,
Converter={StaticResource arithConverter},
ConverterParameter=*0.5}"
CenterY="{Binding ElementName=PhotoBorder,
Path=ActualHeight,
Converter={StaticResource arithConverter},
ConverterParameter=*0.5}" />
This generalized arithmetic converter is clearly over-engineered for the task of finding the centre of an element, but it has other applications when using bindings to control layout in WPF.
I’ve created a complete project, using the sample from Xamlog as a starting point which you can download here. Below are screen-shots of the reflection in action with images of different sizes and aspect ratios.
Creating WPF animations in Xaml requires a fair amount of mark-up, and can be somewhat daunting to write by hand. Fortunately animations done in code can be created much more simply (however you do lose the "declarative" way of specifying when animations fire that markup allows). Here is a simple window which contains a single button child element. The hilighted C# code which is executed when the window has loaded animates the height of the button from 25 units to 150 units in 1 second.
Xaml Code (RC1)
<Window x:Class="LearnWPF.AnimationInCode.Window1"
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
Title="LearnWPF.AnimationInCode"
Height="300" Width="300" Loaded="WindowLoaded"
>
<Button Name="myButton" Content="Hello" />
</Window>
C# (RC1)
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
namespace LearnWPF.AnimationInCode
{
public partial class Window1 : System.Windows.Window
{
public Window1()
{
InitializeComponent();
}
void WindowLoaded(object sender, RoutedEventArgs e)
{
myButton.BeginAnimation(Button.HeightProperty,
new DoubleAnimation(25, 150, new Duration(new TimeSpan(0, 0, 1))));
}
}
}
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
In a comment on our previous post on creating glossy-looking items in WPF Jason asks how to emulate the glossy "now playing" section in apple's iTunes MP3 player. Although we don't want to condone the use of Macs, or any apple software on Windows we were prepared to grant Jason's request. Using these iTunes screen-shots as our starting point this is what we came up with.
| iTunes "now playing" |
WPF |
|
|
Xaml Source (July CTP):
<Page xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
Background="#785429" >
<Grid Width="200" Height="50">
<Rectangle Stroke="#56584c" StrokeThickness="1.5"
RadiusX="3" RadiusY="3" Fill="#d6dbbf"/>
<Rectangle VerticalAlignment="Top" Height="22" Margin="2,2,2,0"
Stroke="{x:Null}" RadiusX="2" RadiusY="2"
SnapsToDevicePixels="True">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#EEF0E5" Offset="0" />
<GradientStop Color="#E4E7D5" Offset="0.9" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</Page>
Glossy, highly reflective-looking UIs are currently quite popular. While there are many ways you can suggest a reflective surface using gradients and hilights, some of the simpler effects are often the best. Here is a simple approach that uses two rectangles placed on top of eachother inside a grid, which gives a nice reflective appearance.
Xaml Code (Jul 2006 CTP)
<Page xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
" Background="#785429" >
<Grid Width="200" Height="70">
<Rectangle Stroke="#FFe1e1e1" RadiusX="10" RadiusY="10"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Width="Auto" Height="Auto" x:Name="Rectangle">
<Rectangle.BitmapEffect>
<DropShadowBitmapEffect ShadowDepth="2"/>
</Rectangle.BitmapEffect>
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FFF9F9F9" Offset="0"/>
<GradientStop Color="#FFd9d9d9" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle Stroke="{x:Null}" Margin="3,14,3,3"
RadiusX="14" RadiusY="10" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Width="Auto" Height="Auto">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FFE8E8E8" Offset="0"/>
<GradientStop Color="#FFE4E4E4" Offset="0.2"/>
<GradientStop Color="#FFbdbdbd" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
<Rectangle.BitmapEffect>
<BlurBitmapEffect Radius="1" KernelType="Box"/>
</Rectangle.BitmapEffect>
</Rectangle>
</Grid>
</Page>
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.
In a previous post Steve asks "is it possible to bind an animation using xaml?". The example below shows how, binding the duration of an animation to a value in a text box. The animation, which is triggered when the button is clicked, changes the button's background color from orange to brown. The duration of the animation is bound to the value entered in the text box using the same {Binding} markup extension used throughout WPF. Although in this example the animation is bound to another element it could be bound to other sources like data from a relational database or a value in an Xml file.
Xaml Code (WPF Beta 2/June CTP)
<Window x:Class="LearnWPF.BoundAnimation.Window1"
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
Title="LearnWPF.BoundAnimation" Height="200" Width="350"
>
<StackPanel Margin="2">
<Button Width="200" Height="50" Name="animateMe"
Content="Click The Button to See Animation">
<Button.Background>
<SolidColorBrush x:Name="buttonBrush" />
</Button.Background>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard Storyboard.TargetName="buttonBrush"
Storyboard.TargetProperty="Color" >
<ColorAnimation To="#58290A" From="#feca00"
Duration="{Binding ElementName=durationText, Path=Text}" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
</Button>
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock VerticalAlignment="Center">Animation Time (hours:minutes:seconds)</TextBlock>
<TextBox Name="durationText" Text="0:0:1" />
</StackPanel>
</StackPanel>
</Window>
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.
Xaml Browser Apps (XBAPs - formerly called WBAs) are an a light-weight way of deploying rich WPF applications to users via their web browser. In addition to all the enhanced display features and stateful programming model that WPF apps have over HTML-based internet applications, XBAPs can also touch local system resources like reading from the file system. Allowing applications that run in the browser direct access to the file system would be a huge security risk. XBAPs ensure that the potentially malicious browser-hosted application can only ever read files that the user has selected for it to read. The XBAP author creates an OpenFileDialog (from the Microsoft.Win32 namespace) and displays it to the user by calling ShowDialog(). If the user selects a file and clicks OK the application can then read the file by calling the OpenFile() method on an OpenFileDialog instance. This returns a read-only stream for loading the data. The example code shown below demonstrates how to do this:
C# Code (WPF Beta 2)
private void OpenFileClick(object sender, RoutedEventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
bool? result = dlg.ShowDialog();
if (result == true)
{
using (Stream s = dlg.OpenFile())
{
TextReader reader = new StreamReader(s);
contentsText.Text = reader.ReadToEnd();
}
}
}
The important distinction here is that the un-trusted XBAP can't just open any file it likes, but rather it has to give the user total control of the file selected. The useful thing for developers is that this feature is turned on in even the most restricted permission set, so even XBAPs launched from the internet and not signed with a trusted key will still be able to do this. You can read more about XBAPs and the sandboxed set of features they must operate in here. More general information about XBAPs can be found here.
The OpenFileDialog resides in the Microsoft.Win32 namespace in PresentationFramework.dll, and is very similar to the OpenFileDialog in the System.Windows.Forms namespace (right down to the name of the class), so make sure you're using the correct one.
In this code sample I've created a re-usable WPF data template for displaying time information using an analog clock. This demo combines some of the WPF features I've talked about before - DataTemplate and converters, and combines them together for easy display of time information. Here is an example of the DataTemplate in action, used inside the ItemTemplate of a ListBox. It is displaying a list of simple ChatEntry objects - the ChatEntry has one field which is a DateTime.
To display the clock showing the time value inside the list's ItemTemplate required the following Xaml. The Content is bound to the DateTime field of the current ChatInfo. It is not apparent in the client code that any special kind of display will be used at all.
Xaml Fragment (WPF Beta 2)
<ContentControl Content="{Binding Path=DateTime}"
Height="60" Grid.Column="0"
Grid.Row="0" Grid.RowSpan="2"/>
With the abundance of high-quality clock implementations for XAML around it was hard to pick a starting point for the clock DataTemplate. Eventually I chose Nathan Dunlap's attractive chrome-less clock. After stripping it back, removing the animations which made it "go" and the second hand I added it inside a DataTemplate inside my App.Xaml file. This will allow my DataTemplate to be used throughout the application. I wanted my DataTemplate to be used any time a DateTime needs displaying. In order to do this I needed to set the DataType attribute to DateTime for my DataTemplate. The DateTime class that resides in the System namespace in the mscorlib assembly. So that I could specify the DateTime type as the DataType for my template I needed to import the System namespace as shown below:
Xaml Fragment (WPF Beta 2)
<Application x:Class="TimeConverter.App"
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
StartupUri="Window1.xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:l="clr-namespace:TimeConverter"
>
<Application.Resources>
<l:TimeToClockHandAngleConverter x:Key="timeToAngleConverter" />
<DataTemplate DataType="{x:Type sys:DateTime}">
<!-- stripped back clock from Nathan Dunlap in here -->
</DataTemplate>
</Application.Resources>
</Application>
As you can see above I also imported the TimeConverter namespace from the current assembly. This is because I needed to write a custom converter for my template, so I could bind the angle of the hands to the number of minutes or hours in the DateTime instance the template was binding against. I wrote a single converter for converting hours and minutes, and passed a parameter to the converter to specify if it should return the angle of the hour hand, or the angle of the minute hand for the supplied time. Inside the DataTemplate the converter was used like this:
Xaml Fragment (WPF Beta 2)
<Rectangle x:Name="HourHand" ... >
<Rectangle.RenderTransform>
<RotateTransform Angle="{Binding Converter={StaticResource timeToAngleConverter},
ConverterParameter=HourHand}" />
</Rectangle.RenderTransform>
Thanks and kudos to Nathan Dunlap for creating a nice and easily subverted clock. You can download a complete sample including the template here.
When creating UI elements in code it is often necessary to programmatically bind them to data. Fortunately this is relatively straightforward as the following code-snippet using the Binding object from the System.Windows.Data namespace shows:
C# Source (WPF Beta 2)
TextBox nameTextBox = new TextBox();
Binding nameTextBinding = new Binding("Name");
nameTextBinding.Source = person;
nameTextBox.SetBinding(TextBox.TextProperty, nameTextBinding);
// set style on text box, add text box to container etc
This sample assumes you have a type called Person, and an instance called person created elsewhere. Person has a public property called name. First we create our text box (called nameTextBox) and a Binding object, setting the path string for the binding in the constructor. Then we set the data source to the person instance. Alternatively the nameTextBox could get its data through its DataContext or the DataContext of one of its ancestors. If this was the case setting the Source property on the binding would not be necessary.
Next we call the SetBinding() method on the nameTextBox instance, specifying a DependencyProperty and the binding we just created. The example above binds the TextProperty DependencyProperty of nameTextBox to the Name property of the person. In this case the DependencyProperty is a member of the TextBox itself, but it doesn't have to be. For example we can also bind attached properties that might pertain to the container the nameTextBox is placed in.
This example below binds (rather pointlessly) the TopProperty DependencyProperty of the Canvas to the current number of minutes that have elapsed in the current hour for a newly created textbox instance. If the textbox is then added to a canvas (the example assumes a canvas called mainCanvas) the binding will take effect, controlling the top of the textbox in the canvas.
C# Source (WPF Beta 2)
TextBox positionedTextBox = new TextBox();
Binding positionBinding = new Binding("Minute");
positionBinding.Source = System.DateTime.Now;
positionedTextBox.SetBinding(Canvas.TopProperty, positionBinding);
mainCanvas.Children.Add(positionedTextBox);
WPF is a great platform for creating attractive UI. After working with it for some time you may wish to include vector-based graphics in your WPF application, where in the past you might have used icons or a bitmaps. Using vector-based images in your WPF application allows your images to change size in the same way the rest of your WPF application can, without becoming blurry or blocky.
Recently I came across some nice, crisp-looking free vector images created by N.Design Studio in PDF format. I wanted to see how easy it would be to use them in a WPF application. In this article I'm going to describe the process of converting the images to Xaml, and how Xaml vector images can be packaged up for easy re-use. Thanks to the generosity of Nick at N.Design I'll also post all the Xaml source for the images, which can be freely used in your own open-source and commercial WPF applications.
Converting the Images
To embed images as vectors in a WPF application they need to be in Xaml. Currently there are a number utilities and plug-ins available for converting different file formats to Xaml. Most of these are developed by the community, and are at different stages of completeness and currency. As WPF improves in popularity exporting to Xaml from your favorite tool will become easier and more prevalent. I chose to use Mike Swanson'sAdobe Illustrator > Xaml exporter. After installing the Illustrator CS trial version and following the simple install instructions I was able to open the PDFs in illustrator and export them directly to Xaml. Although this conversion was not perfect, it was more than satisfactory for my purposes.
Adding the Images to a Project
I wanted using the images to be as simple as adding a single file to a project, and knowing the names of the individual images. I added all the images as separate resources with their own keys. Use the technique described in this article to include the external resource file in your main application resources.
Using the Images in WPF
To use one of the image resources use the {StaticResource} markup extension, and the name of the image resource. Here is an example that displays an image resource named "Credit-Card" inside a button.
Xaml Code (WPF Beta 2)
<Button Height="75" Width="220" Padding="2">
<StackPanel Orientation="Horizontal">
<ContentControl Template="{StaticResource Credit-Card}" />
<TextBlock FontSize="16" VerticalAlignment="Center" Text="Pay Now!" Margin="3" />
</StackPanel>
</Button>
The button will look like this:
The image resources will resize to fit the available space, and still retain the correct aspect ratio. Here are a number of buttons of different heights with the same image inside them:
This re-sizing requires no additional code or properties to be set by users of the image, and only minimal effort when creating the resource. To achieve the auto-sizing I nested the "raw" image Xaml (produced by Mike Swanson's converter) inside a Viewbox element with the Stretch attribute set to Uniform:
Xaml Code (WPF Beta 2)
<Viewbox Stretch="Uniform">
... <!-- "raw" Xaml - canvases, paths etc go here -->
</Viewbox>
To allow the image resources to be used multiple times the ViewBox (which allowed the images to resize to fit their container) had to be nested inside a ControlTemplate. Objects which derive from FrameworkElement in WPF must normally have a single parent in the tree of WPF objects. Re-using an object deriving from FrameworkElement without first placing it inside a ControlTemplate would result in an error like the following:
Error in markup file 'somefilename.xaml' : Specified element is already the logical child of another element. Disconnect it first.
ControlTemplate allows you to overcome this problem - it has to so that the templates you define for controls like buttons and list boxes etc to be re-used more than once. This technique is described in Chris Sells and Ian Griffiths excellent book Programming Windows Presentation Foundation - p210. After wrapping everything in a ControlTemplate the overall format of all the resources was like the following:
Xaml Code (WPF Beta 2)
<ControlTemplate x:Key="Resource Name">
<Viewbox Stretch="Uniform">
... <!-- "raw" Xaml - canvases, paths etc go here -->
</Viewbox>
</ControlTemplate>
Here are the images and their names (for easy inclusion in your own WPF projects)
Here is the file containing the Xaml code - you are free to use these images created by N.Design Studio in your own projects (both free and commercial) but you are not permitted to re-distribute the Xaml file.
WPF allows you to define pieces of Xaml as resources, and use them in multiple places in your application. This allows us to share things like styles and brushes across our whole application, making it look more consistent and easier to maintain (since changes need only be made in a single place). As applications grow the size of these resource sections can make them more difficult to work with and create contention amongst the development team as different people try to work on the same large resource file. Also we may wish to re-use some resources across different WPF projects. For the same reason it is desirable to split our C# or VB.NET applications into multiple files it is also desirable to do factor out our Xaml resources into multiple files. How can this be accomplished?
Separation of resources across multiple files can be fairly easily done using this technique which I first saw used in Robert Ingebretsen's "SimpleStyles" sample. The technique involves adding external resources to an existing resource (like your application resources, or the resources for a window or pannel) using the MergedDictionaries property of the ResourceDictionary.
Lets walk through a simple example that assumes we have a text block style we want to factor out into a separate file from our application resources. First we create a new Xaml file (in this case called TextStyle.xaml) so that our project structure is as shown
The content of the file is just a ResourceDictionary - and because the ResourceDictionary is the root element in the Xml file we need to define the default namespaces, which you would probably be more used to seeing on a new root-level Window or Page when you create a new Xaml file in Visual Studio. The contents of the resource dictionary is a perfectly normal WPF style.
Xaml Code (WPF Beta 2)
<ResourceDictionary
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
" >
<Style TargetType="{x:Type TextBlock}" x:Key="TextStyle">
<Setter Property="FontFamily" Value="Lucida Sans" />
<Setter Property="FontSize" Value="22" />
<Setter Property="Foreground" Value="#58290A" />
</Style>
</ResourceDictionary>
The second step is to merge this resource stored in an external file into one of the resource dictionaries in our application. For this example we'll merge it in to the Application.Resouces collection (located in the App.xaml file). This is done by adding the elements highlighted below which point to the TextStyle.xaml file we created.
Xaml Code (WPF Beta 2)
<Application x:Class="LearnWPF.FactoringResources.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml"
>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="TextStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
<!-- other styles can appear in the resource dictionary too -->
<Style TargetType="{x:Type Button}" x:Key="ButtonStyle">
<Setter Property="Background" Value="#feca00" />
</Style>
</Application.Resources>
</Application>
We can now use the TextBlock style in the TextStyle.xaml file just like any other style in our Application.Resources dictionary
Xaml Snippet (WPF Beta 2)
<StackPanel>
<TextBlock Style="{StaticResource TextStyle}" Text="Sample Text" />
<Button Style="{StaticResource ButtonStyle}" Content="Button" />
</StackPanel>
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/ .
It is often desirable to control the format of dates, times, money and percentages when they are displayed. The example screen-shot below shows a list of 3 sale records. The sale information includes an Id, the sale amount, the percentage of sales tax paid and the time of the sale. Although the information displayed to the user is correct, the unformatted data might be difficult or confusing for a user to read.
The .NET framework includes a small formatting mini-language using strings like "{0:C}" to format a parameter as currency. WPF data binding does not have any facility built-in to allow this formatting, however it is easy to add by extending the built-in binding infrastructure. This technique I originally came across here in the WPF forums, but it has been extended here to allow formatting of all data types.
Implementing a custom ValueConverter for Formatting
To extend the binding infrastructure to allow formatting I've created a ValueConverter to convert between a .NET object and a string. Value converters implement the IValueConverter interface in the System.Windows.Data namespace. This interface has two methods, Convert and ConvertBack for converting between two types. To convert the data from a .NET object to a formatted string we're only going to implement the Convert() method. The formatting string (like "{0:MM-dd-yyyy}" if we wished to format a date) is passed to the Convert() method of the ValueConverter via the aptly named parameter parameter. Fortunately the Convert() method is also passed a CultureInfo parameter which we can use to perform our formatting in a culture-specific way. Formatting should be culture aware, as different countries use different symbols for currency, have different date time formats and different symbols to separate the whole and fractional parts of non-integer real numbers. The code for the ValueConverter is shown below:
C# Code (Feb 2006 CTP)
using System;
using System.Windows.Data;
namespace LearnWPF.FormattingAndDataBinding
{
[ValueConversion(typeof(object),typeof(string))]
public class FormattingConverter: IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
string formatString = parameter as string;
if (formatString != null)
{
return string.Format(culture, formatString, value);
}
else
{
return value.ToString();
}
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
// we don't intend this to ever be called
return null;
}
}
}
Using the Converter from our WPF Application
To use the converter in our WPF application requires 3 steps:
1 - Create an Xml namespace prefix for the CLR namespace that contains the ValueConverter. This is necessary any time you want to use one of your own types in mark-up. The example below tells the Xaml run-time that any time it sees a "my" prefix on an element it should look in the LearnWPF.FormattingAndDataBinding namespace in the current assembly. More details of this can be found here in the Windows SDK.
xmlns:my="clr-namespace:LearnWPF.FormattingAndDataBinding"
2 - Create an instance of our formatter as a resource*. In this sample I've chosen to place it in the resources dictionary for the window. A key is required so we can retrieve it later.
<my:FormattingConverter x:Key="formatter" />
3 - Use the Converter when binding. We do this by specifying a Converter for the binding (the converter we created in step 2 as a resource), and also setting the ConverterParameter. The ConverterParameter is passed to the IValuConverter's Convert() method as the (aptly named) parameter parameter. The example below passes a custom date format string {0:dd-MMM-yyy hh:mm}.
<TextBlock Text="{Binding Path=TimeOfSale,
Converter={StaticResource formatter},
ConverterParameter=' \{0:dd-MMM-yyyy hh:mm\}'}" />
Note that in the format string the curly brackets are escaped using the backslash character like this \{ and this \}. This is necessary because the curly brackets are used in Xaml for specifying markup extensions like that {Binding} and {StaticResource} extensions seen in this example. We need to differentiate our formatting string from those.
Here is the Xaml code containing all three steps
Xaml Code (Feb 2006 CTP)
<Window x:Class="LearnWPF.FormattingAndDataBinding.Window1"
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
xmlns:my="clr-namespace:LearnWPF.FormattingAndDataBinding"
Title="LearnWPF.FormattingAndDataBinding" Height="300" Width="300"
Loaded="windowLoaded"
>
<Window.Resources>
<ObjectDataProvider x:Key="sales" />
<my:FormattingConverter x:Key="formatter" />
</Window.Resources>
<ListBox DataContext="{StaticResource sales}" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="#feca00" BorderThickness="2"
CornerRadius="3" Padding="5" Margin="2">
<StackPanel Orientation="Horizontal">
<!-- formatting the Id -->
<TextBlock Text="{Binding Path=Id,
Converter={StaticResource formatter},
ConverterParameter='Sale No:\{0\} '}" />
<!-- formatting the Amount -->
<TextBlock Text="{Binding Path=Amount,
Converter={StaticResource formatter},
ConverterParameter=' \{0:C\}'}"
FontWeight="Bold" />
<!-- formatting the SalesTaxPercentage -->
<TextBlock Text="{Binding Path=SalesTaxPercentage,
Converter={StaticResource formatter},
ConverterParameter=' (\{0:P\} Sales Tax)'}" />
<!-- formatting the date -->
<TextBlock Text="{Binding Path=TimeOfSale,
Converter={StaticResource formatter},
ConverterParameter=' \{0:dd-MMM-yyyy hh:mm\}'}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
Here is the resulting window with nicely formatted data
The formatting converter shown above only performs a one-way conversion - from the .NET object to a string. Converting back to the underlying data type from a string might be possible by inspecting the targetType passed as an argument to the ConvertBack() method, or you could write a type-specific converter with a tailored ConvertBack() implementation. Since the formatting converter only converts in one direction it is really only useful for formatting data for display in a read-only manner.
You can download the complete source code for this example here.
* Note: It is not absolutely necessary to create our Converter as a resource, since we could use the less-compact property element syntax for the binding and create an instance of the Converter each time we wanted to use it. Defining the converter as a resource and re-using it is recommended.
Splitters are a useful UI feature where the width or height of a control on the form can be modified to show more or less information. An Example of this is the splitter between the folder tree-view in and the file and subfolder detail view in Windows Explorer, allowing the size of the folder tree-view on the left-hand side of the form to be sized up to 50% of the form width. Windows Forms 1.1 included a Splitter control, and Windows Forms 2.0 included a SplitContainer. WPF does not have equivalent versions of either of these controls; however it is possible to add splitters to your WPF application using the WPF Grid, and a GridSplitter. The example code below allows the two columns in the grid to be re-sized. This is done by adding a GridSplitter as a child item of the Grid.
Xaml Code (Feb 2006 CTP)
<Window x:Class="LearnWPF.Splitter.Window1"
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
Title="LearnWPF.Splitter" Height="300" Width="300"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Background="#feca00" Grid.Column="0">
<TextBlock FontSize="35" Foreground="#58290A"
TextWrapping="Wrap">Left Hand Side</TextBlock>
</StackPanel>
<GridSplitter/>
<Border CornerRadius="10" BorderBrush="#58290A"
BorderThickness="5" Grid.Column="1">
<TextBlock FontSize="25" Margin="20" Foreground="#FECA00"
TextWrapping="Wrap">Right Hand Side</TextBlock>
</Border>
</Grid>
</Window>
This gives the following UI behavior:
The splitter appears "docked" on the right edge of the cell that contains it. Like other items contained in a grid, the splitter can be positioned in a column/row other than the first one by setting the Grid.Column and Grid.Row properties. In the above example it defaults to row and column 0 because these have not been explicitly set.
To allow the GridSplitter to resize cells vertically is somewhat non-intuitive as described by Charles Petzold here. The GridSplitter has a ResizeDirection which defaults to Columns but can be set to Rows to allow the splitter to change the height of a row in the grid. To enable the height of row to be resized correctly by the splitter you also need to set the HorizontalAlignment and VerticalAlignment for to align it to the bottom, and across the width of the containing cells. You can also set the Grid.ColumnSpan for the splitter to span across all the cells in the row that it resizes, otherwise the splitter will only fill a single cell.
This example below creates a 2x2 grid of cells, with a splitter spanning both columns and allowing the height of the rows to be changed.
Xaml Code (Feb 2006 CTP)
<Window x:Class="LearnWPF.Splitter.Window1"
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
Title="LearnWPF.Splitter" Height="300" Width="300"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Background="#feca00" Grid.Column="0" Grid.Row="0">
<TextBlock FontSize="35" Foreground="#58290A"
TextWrapping="Wrap">Left Hand Side</TextBlock>
</StackPanel>
<GridSplitter ResizeDirection="Rows"
Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"/>
<Border CornerRadius="10" BorderBrush="#58290A" Grid.Column="1" Grid.Row="1"
BorderThickness="5">
<TextBlock FontSize="25" Margin="20" Foreground="#FECA00"
TextWrapping="Wrap">Right Hand Side</TextBlock>
</Border>
</Grid>
</Window>
A draft set of design guidelines for WPF has been published on the MSDN, to help developers and designers make appropriate design choices when creating user interfaces. In conjunction with the UX guidelines for Windows Vista this should help weilding the enormous power of WPF to create great UIs. The guidelines help developers/designers to consider their UI based on the type of application they intend to create: productiviy application, kiosk app, utility etc. Then it offers prescriptive advice per feature set of WPF detailing what is good, and what to avoid.
[via Andrew Stopford]
WPF's data binding facilities are extremely powerful - sometimes when using this power you may experience BamlParseException with an error message stating that the was a problem converting from a Binding object to some other type. A typical error message might look like this:
Error at element 'RuntimePropertyInfo' in markup file 'Window1.xaml' : Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.String'
This problem can be confusing at first because Elements with the same DataContext in your Xaml file may be able to bind to the same piece of data without any problems.
<Window.Resources>
<XmlDataProvider x:Key="data" XPath="//item">
<x:XData>
<items xmlns="">
<item val="foo"/><item val="bar" />
</items>
</x:XData>
</XmlDataProvider>
</Window.Resources>
<StackPanel>
<!-- this works -->
<TextBlock Text="{Binding
XPath=@val
}" />
<!-- this does not -->
<TextBlock>
<Bold Text="{Binding
XPath=@val}"/
>
</TextBlock>
</StackPanel>
If this is the case it can be indicative of a subtle problem not with the source of the data but with the property you're trying to bind the data to. WPF data binding makes use of the dependency property system built into WPF, and the eventual target of the {Binding} markup extension must be a dependency property. Dependency properties are not the same as CLR properties, which are a syntactic convenience which allows a pair of get and set methods for a member to be treated from a code perspective like a member field. Dependency properties on the other hand are objects of type DependencyProperty. Most of the UI-related properties of controls in WPF eventually call dependency properties internally, so it is easy to forget this rule.
It is not easy to know at a glance if a CLR property does indeed call a dependency property or not. The current WPF documentation does not explicity indicate this. The documentation does list the public fields of an object, which typically includes all the dependency properties as static fields. If the property you're trying to bind to does not have a static DependencyProperty member variable with the name of the CLR property, followed by 'Property' (e.g. TextProperty) then the CLR property is unlikely to be using the dependency property system underneath, and thus is not a suitable target for binding. Using Reflector to look inside the property getter and setter methods it is easy to spot the difference between the two.
CLR Property which calls a DependencyProperty (TextBlock.Text)
public string Text
{
get
{
return (string) base.GetValue(TextBlock.TextProperty);
}
set
{
base.SetValue(TextBlock.TextProperty, value);
}
}
CLR Property which does not call a DependencyProperty (Bold.Text), which we cannot bind to.
public string Text
{
get
{
return TextRangeBase.GetTextInternal(base.ContentStart, base.ContentEnd);
}
set
{
if (value == null)
{
value = string.Empty;
}
TextContainer container1 = base.TextContainer;
container1.BeginChange();
try
{
TextPointer pointer1 = base.ContentStart;
if (!base.IsEmpty)
{
container1.DeleteContentInternal(pointer1, base.ContentEnd);
}
Run run1 = Inline.CreateImplicitRun(this);
pointer1.InsertTextElement(run1);
run1.Text = value;
}
finally
{
container1.EndChange();
}
}
}
In some cases it is possible to work around this problem - if you are using data binding to keep two things synchronized and the other thing is a dependency property then swapping the source and target of the binding can solve this problem.
Occasionally when developing WPF applications you may experience an inexplicable error regarding duplicate keys in a ResourceDictionary. The wording for the error will be something like the following:
Error at element 'ResourceDictionary' in markup file 'Window1.xaml' : Item has already been added. Key in dictionary: 'MyItemKey' Key being added: 'MyItemKey'.
This is a perfectly valid error message if you are actually adding a second item to a dictionary with a key that has already been used. What makes this error confusing is that it can occur in simple applications when there are obviously no duplicates in the dictionary in question. The error occurs at run-time, but seems to be caused by some bug in the WPF build process. The problem can be worked around in a number of cumbersome ways such as re-naming the single resource in question, or copying files into a new project. The recommended way to solve this problem is to select 'Clean' from the build menu and then re-build your application. This problem has been noted in versions of WPF including the current Feb 2006 CTP. Hopefully this issue will be resolved as WPF approaches its final release.
Not all of your application's window is actually drawn by WPF - In order to integrate with the underlying operating system the border and title bar of WPF windows are still drawn in GDI like other windows. This means that some operations that can be performed on most elements in WPF (like LayoutTransforms and RenderTransforms) cannot be applied to the top-level window element. This also means the rendering of these areas cannot be customized in WPF in the way that normal elements can be using styles or templates. In order to create customized title bars for your windows you have to create a borderless window, and create a title bar yourself in Xaml.
This gets slightly more complicated on Windows Vista, where rendering of form borders and title bars is handled by the Desktop Windows Manager (DWM) which uses a component called MILCore (MIL stands for Media Integration Layer), which calls into DirectX. It turns out WPF uses this same graphics stack to render. WPF's PresentationCore calls into MILCore (which is unmanaged) which then calls into DirectX. While this implementation detail is interesting to know from a programming point of view the outcome is the same on Vista as XP and Windows 2003 - you can't change the appearance of the title bar or scale, skew or rotate your whole windows from WPF using transforms.
Another implication of this is controls on your WPF forms no longer have a window handle (hWnd), since they are not rendered by GDI. Unless you are hosting controls from Windows Forms or MFC the only hWnd in your typical WPF application will be the hWnd of the top-level window. This means UI automation and testing technologies that rely on hWnds to programmatically drive your UI will nolonger work. Fortunately WPF comes with its own UI automation API.
The other region of your WPF windows that will not actually be rendered by WPF are any controls from other presentation frameworks like WindowsForms or MFC that are being hosting inside an HwndHost - they are still rendered by GDI. This also means you can't scale, skew, translate or rotate them using transforms.
WPF attempts to bring the best of rich-client technologies like Windows Forms, and layout techniques from the browser to give a high degree of flexibility when positioning elements on forms.
Rich client technologies like VB6 forms, Access Forms etc were based on absolute positioning. As a developer you specified the top and left values for a control to position it on the form. This allows for a fine degree of control, but often required a large amout of code to handle form re-sizing. This was an imperative approach to layout. Controls like Labels and Panels had no awareness of the size of the content inside them - for example if the content of a label was larger than what could be displayed in the label then the content is clipped, and not shown.
Web UI rendered in the browser typically require no re-sizing code. Instead HTML gives a declarative approach to the layout of elements. You specify a serise of containers like <div> elements (or <table>, <tr> and <td> elements if you haven't become convinced of the benefits of positional CSS-based layouts) and position those by either floating them Left or Right, or positioning them absolutely in your page. Elements like <div> are aware of the size of their contents and will stretch to accomodate their content.
Both these approaches could be equally difficult to achieve the desired layout, although the declarative approach had the advantage of requiring much less code to do so. Recently windows forms has added concepts like Docking and Anchoring, adding a more declarative approach to rich-client development. WPF continues this trend with a declarative approach to layout based on the concept of panels.
Most UI elements in WPF can only contain a single child element - for example the following Xaml code fails to compile with the following error: "The 'Button' object already has a child and cannot add 'CheckBox'. 'Button' can accept only one child."
Xaml Syntax (does not compile)
<Button>
<TextBlock>Some Text...</TextBlock>
<CheckBox />
<ComboBox />
</Button>
If the button allowed these two elements to be nested inside it it would be responsible for laying them out somehow. Since there are many possible ways these items could be layed out the button would have to become a much more complicated control to accomodate them all, and would now have two "responsibilities" - allowing the user to click on it, and laying out content. Not only would the button become much more complicated but all the controls in WPF would become more complicated. Instead of taking this approach the WPF team decided to factor out the responsibility for layout to a different control - the panels. There are a number of different types of panels in WPF and each enables a different kind of layout. Panels can be nested wherever regular content can be placed, allowing a fine degree of control over the layout of items. For example to correct the problem in the example above we could nest a StackPanel inside the button, and position the elements inside the StackPanel
<Button>
<StackPanel>
<TextBlock>Some Text...</TextBlock>
<CheckBox />
<ComboBox />
</StackPanel>
</Button>
This give the following layout:
Having briefly introduced the important role panels play in controlling layout in WPF let's look at the most commonly used panel types and their characteristics.
StackPanel
The StackPanel lays out child elements by stacking them one after the other. Elements are "stacked" in the order they appear in the Xaml file (document order in XML terms). Items can either be stacked vertically (the default) or horizontally.
Xaml Syntax for Stacking Vertically (Feb 2006 CTP):
<StackPanel>
<TextBlock FontSize="16"
Foreground="#58290A">
Items inside a StackPanel</TextBlock>
<Button>Item 2</Button>
<Border BorderBrush="#feca00" BorderThickness="2">
<TextBlock>Item 3</TextBlock>
</Border>
</StackPanel>
Xaml Syntax for Stacking Horizontally (Feb 2006 CTP):
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="16"
Foreground="#58290A">
Items inside a StackPanel</TextBlock>
<Button>Item 2</Button>
<Border BorderBrush="#feca00" BorderThickness="2">
<TextBlock>Item 3</TextBlock>
</Border>
</StackPanel>
WrapPanel
The wrap panel lays out items from left to right. When a row of items has filled the horizontal space available to them the panel wraps the next item around onto the next line (in a similar way to how text is layed out).
Xaml Syntax (Feb 2006 CTP):
<WrapPanel>
<TextBlock FontSize="16"
Foreground="#58290A">
Items inside a WrapPanel</TextBlock>
<Button>Item 2</Button>
<Border BorderBrush="#feca00" BorderThickness="2">
<TextBlock>Item 3</TextBlock>
</Border>
</WrapPanel>
DockPanel
The DockPanel allows elements to be docked to the edges of the panel's container, similar to Windows Forms docking. Items are docked in the order they appear in the Xaml file (document order). The last Border element fills all the remaining space since no DockPanel.Dock attribute is specified for it.
Xaml Syntax (Feb 2006 CTP):
<DockPanel>
<TextBlock FontSize="16" DockPanel.Dock="Top"
Foreground="#58290A">
Items inside a DockPanel</TextBlock>
<Button DockPanel.Dock="Left">Item 2</Button>
<Border BorderBrush="#feca00" BorderThickness="2">
<TextBlock>Item 3</TextBlock>
</Border>
</DockPanel>
Canvas
The Canvas panel is similar to the way old rich-client layout worked where you control the absolute position of things by setting a Top and Left property for them. Additionally, instead of setting the Left to position an item you can instead set the Right, or instead of setting the Top you can set the Bottom. If you specify a Left and a Right the Right value is ignored, the element does not change its size to make both these values correct, and similarly Top takes precedence over Bottom. Elements that are declared earlier in the Xaml file can appear behind elements that are declared later (if their positions over-lap).
Xaml Syntax (Feb 2006 CTP):
<Canvas>
<TextBlock FontSize="16" Canvas.Top="10" Canvas.Left="20"
Foreground="#58290A">
Items inside a Canvas</TextBlock>
<Button Canvas.Bottom="25" Canvas.Right="50">Item 2</Button>
<Border BorderBrush="#feca00" BorderThickness="2"
Canvas.Top="20" Canvas.Left="50">
<TextBlock>Item 3</TextBlock>
</Border>
</Canvas>
Grid
The Grid panel is an extremely flexible panel, and can be used to achieve almost everything that can be done with the other panel controls (although often not with the same ease). The grid panel allows you to define rows and columns in the grid using Xaml, and then place controls in calls in the grid using grid-specific attributes. Elements can span multiple rows or columns. The grid will automatically make all its rows and columns the same size (based on the size of the content) but you can specify a proportional size for rows and columns using a star notation, or specify an absolute width or height. The star notation can be seen in the example code below where one of the columns is twice the width of the other column by setting the Width attribute to "2*". The example below also shows the height of one row being set to an absolute value. The differences these introduce can more readily be seen when re-sizing the form containing the grid, as the grid will expand by default to fill the space available to it. The example below also has the ShowGridLines attribute of the grid set to True to help you visualize the space each row and column is taking up. A broader range of layout tricks for the grid panel is beyond the scope of this introduction, but hopefully we'll be able to re-visit this soon in a future article.
Xaml Syntax (Feb 2006 CTP):
<Grid Margin="10" ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition />
<RowDefinition Height="2*"/>
</Grid.RowDefinitions>
<TextBlock FontSize="16"
Foreground="#58290A"
Grid.Column="0" Grid.Row="0"
Grid.ColumnSpan="2"
>
Items inside a Grid</TextBlock>
<Button Grid.Column="0" Grid.Row="1">
Item 2</Button>
<Border BorderBrush="#feca00" BorderThickness="2"
Grid.Column="1" Grid.Row="2">
<TextBlock>Item 3</TextBlock>
</Border>
</Grid>
We can see from the different examples above that the different Panel controls not only influence the position of the elements they're laying out, but also their size.
Implementing a Custom Panel
The WPF layout system is not limited to the layouts that are included by default. If you need a special layout that can't easily be achieved with the built-in panels you can implement your own. WPF Program Manager Kevin Moore has implemented two custom panels - an animating tile panel and a TreeMapPanel . The source for these built with the February CTP (the most recent at the time of writing) is available here.
Defining something in a single place for re-use in multiple places is a common goal for developers. In UI frameworks like ASP.NET or windows forms re-using pieces of UI is typically done by creating user controls or custom controls. Unfortunately the places where you can easily use these is often limited. For example if you create a UserControl in ASP.NET to display an employee there is no easy way to re-use that control when displaying a list of customers in a combo box. WPF allows the display of business objects and other data to be templated like controls, and because of WPF's rich content model these data templates can be used in many different places.
Lets look at the steps we would go through to create a simple data template for displaying employee data. First lets assume that our employee type has the following public properties, all of which are strings:
- Department
- EmailAddress
- ImagePath
- JobDescription
- Name
- Title
We might wish to lay out our employee data as shown below: an image of the employee on the left followed by with the employee name, email address and other details layed out as shown.
In order to create the DataTemplate we'll first examine the regular Xaml to display the layout shown above statically (without any data binding) and then add a little data binding code to turn this into a DataTemplate we can use anywhere we want to show employees. The Xaml markup below creates the static layout we want, with appropriate text placeholders for the fields we'll eventually be exposing from the Employee object. To achieve this layout we used a Grid panel, one of the most flexible layout panels in WPF. We created a 3x3 grid of rows and columns by adding the appropriate <ColumnDefinition/> and <RowDefinition/> elements to the Grid. Then we add the actual placehoders to the Grid - TextBlock elements and a single Image element. We control the placement of these elements in the Grid by setting the Grid.Row and Grid.Column attributes. This notation in the form of ParentElement.PropertyName is called Property-Element syntax. This Xaml is actually setting properties on the parent Grid element, placing child elements in the appropriate rows and columns.
<Grid ShowGridLines="True" Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="55" />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Image Width="50" Stretch="Uniform"
Source="pack://application:,,/Media/head-male-sillouette.gif"
Grid.Column="0" Grid.Row="0" Grid.RowSpan="2"/>
<TextBlock FontSize="20" Grid.Column="1" Grid.Row="0"
Foreground="Orange" TextDecorations="Underline"
Text="Data Bound: Name"/>
<TextBlock FontSize="12" Grid.Column="1" Grid.Row="1"
Foreground="Blue" TextDecorations="Underline"
Text="Data Bound: EmailAddress" />
<TextBlock FontSize="16" Margin="10,0,10,0" Grid.Column="2"
Grid.Row="0" Foreground="Black"
Text="Data Bound: Title" />
<TextBlock FontSize="16" Margin="10,0,10,0" Grid.Column="2"
Grid.Row="1" Foreground="Black"
Text="Data Bound: Department" />
<TextBlock FontSize="12" Grid.Column="0" Grid.ColumnSpan="3"
Grid.Row="2" Foreground="Black"
Text="Data Bound: JobDescription (this one can span multiple columns)"
TextWrapping="Wrap" />
</Grid>
To Convert this Xaml into a DataTemplate, all that is required is to change the currently static TextBlock text to something bound to the employee object. This is done with the Binding markup extension {Binding Path=PropertyName}, using the name of the property you wish to mind to in the Path portion of the binding statement. The image source is also just a string, so we can bind the source of the image to that to. We also need to place our Xaml in a DataTemplate element and assign it a DataType. By placing the DataTemplate in the Application resource dictionary in MyApp.xaml we can use this template anywhere in our application we are binding to employee data. The finished data template is shown below. The text in orange shows the changes necessary to convert from static Xaml to the DataTemplate.
<DataTemplate DataType="{x:Type l:Employee}">
<Grid Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="55" />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Image Width="50" Stretch="Uniform"
Source="{Binding Path=ImagePath}"
Grid.Column="0" Grid.Row="0" Grid.RowSpan="2"/>
<TextBlock FontSize="20" Grid.Column="1" Grid.Row="0"
Foreground="Orange" TextDecorations="Underline"
Text="{Binding Path=Name}"/>
<TextBlock FontSize="12" Grid.Column="1" Grid.Row="1"
Foreground="Blue" TextDecorations="Underline"
Text="{Binding Path=EmailAddress}" />
<TextBlock FontSize="16" Margin="10,0,10,0" Grid.Column="2"
Grid.Row="0" Foreground="Black" Text="{Binding Path=Title}"/>
<TextBlock FontSize="16" Margin="10,0,10,0" Grid.Column="2"
Grid.Row="1" Foreground="Black" Text="{Binding Path=Department}"/>
<TextBlock FontSize="12" Grid.Column="0" Grid.ColumnSpan="3"
Grid.Row="2" Foreground="Black" Text="{Binding Path=JobDescription}"
TextWrapping="Wrap" />
</Grid>
</DataTemplate>
We don't need to do anything special in our application to use the DataTemplate. Any time an element is bound to an employee, or list of employees the DataTemplate will be used. For this example we added an ObjectDataProvider to the Application resource dictionary (in MyApp.xaml) which returns a collection containing 4 employee objects. We then set it as the DataContext for all the items in our main window. Below is the Xaml for binding the contents of a button to a single employee, and for binding a ComboBox to the list of employees. You can download the full source for this sample here.
<StackPanel Margin="10" DataContext="{StaticResource Employees}">
...
<Button Content="{Binding Path=[0]}" />
<ComboBox ItemsSource="{Binding}" SelectedIndex="0" />
</StackPanel>
WPF has good built-in support for binding to different kinds of data, including XML. In this very simple example we're going to declare a small "island" of XML data in our UI, and bind some text properties to parts of the XML document. Then we'll look at binding to XML documents loaded from other sources.
We declare the XML data island by adding an XmlDataProvider to the resources collection of our window. Assigning a key to the resource is necessary so we can access it later, and setting an XPath expression specifies the list of nodes the XmlDataProvider will expose. The built-in support for using XPath when binding to XML data is a powerful feature of XML data binding in WPF, and should feel fairly natural for those familiar with XSLT. In this case the expression says to return all the Employee nodes located under a root Employees node. Inside the XmlDataProvider we add an XData node (this is a requirement for XML data islands). Inside the XData node we place any XML content we wish. For this example we've defined a simple set of XML data - a list of employees with one item in the list, Microsoft CEO Steve Ballmer.
<Window.Resources>
<XmlDataProvider x:Key="EmployeeData" XPath="/Employees/Employee">
<x:XData>
<!-- xml data island -->
<Employees xmlns="">
<Employee Name="Steven Ballmer" DOB="1-Mar-1956">
<Title>CEO</Title>
</Employee>
</Employees>
</x:XData>
</XmlDataProvider>
</Window.Resources>
To bind to this XML in our application code we use a binding syntax similar to the one used to bind to objects. First we set the DataContext for the panel so that all the child Xaml elements are binding to the same data. This is done using the {StaticResource} markup extension with a key, the key we previously gave our XmlDataProvider. Inside the panel there are two text blocks that will be bound to parts of the Employees XML document. Once again the syntax for binding is similat to the syntax used to bind to objects. We use the {Binding} markup extension, but instead of setting a Path, we specify an XPath expression. To bind to the Name attribute on an employee element the expression is @Name, but more complex espressions are possible such as the second example which binds to the inner text of the title element nested under the employee element.
<!-- binding to data island -->
<StackPanel DataContext="{StaticResource EmployeeData}">
<TextBlock Text="{Binding XPath=@Name}" FontSize="30" />
<TextBlock Text="{Binding XPath=Title/text()}" FontSize="30" />
</StackPanel>
In the real world it is more likely that XML data will be retrieved from an external source rather than imbedded in the UI. This is also easy to achieve using the XmlDataProvider and setting the Source. This example shows how to retrieve the RSS feed from LearnWPF, but XML can also be loaded from the file system or imbedded XML documents.
<XmlDataProvider x:Key="LearnWPFFeed"
Source="
http://learnwpf.com/Rss.ashx
"
XPath="//item" />
Binding to nodes retrieved from an external source is the same as when we bound to local XML data.
<!-- binding to RSS feed -->
<StackPanel DataContext="{StaticResource LearnWPFFeed}">
<TextBlock Text="{Binding XPath=title/text()}" FontSize="30" />
<TextBlock Text="{Binding XPath=link/text()}" FontSize="12" />
</StackPanel>
The full source for this window with both the data sources is here.
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.
A few colleagues and posters to the WPF Forum have reported problems with the Feb CTP of the Windows SDK installing. The error message they've given is something like:
Windows SDK Setup (failed): Installation of the "Microsoft Windows Software Development Kit" product has reported the following error: Fatal error during installation.
The Windows SDK can be installed in two different ways - by downloading a 217 KB setup executable that then downloads and installs the necessary components, or by downloading a 1089 MB img (CD image) of the SDK and installing from there. These problems seem to be present more often when installing using the 217 KB setup file. We recommend downloading and installing from the .img (assuming you have enough bandwidth to download this large file) if you experience problems installing the Windows SDK using the setup executable.
If you have problems installing from the .img file this can be caused corruption of the image during download. You can run a Cyclic Redundancy Check (CRC) to verify its correctness as described in this posting by Jason Sacks, the Program Manager for the Windows SDK installer.
The Expression Graphic Designer (code-named Acrylic) is capable of saving out assets to Xaml using the File > Export > Xaml Export from the main menu. The assets can be exported as elements in a canvas, or as a resource (suitable for embedding in your application).
Unfortunately unlike the Expression Interactive Designer (code-named Sparkle) Xaml is not the "native format" of Acrylic, so the export process is one-way. The representation in Xaml is quite good however, even for assets that contain both vector and raster (bitmap) layers.
The March CTP of the Expression Graphic Designer can be downloaded from here.
Aero Glass is a new theme being launched in Windows Vista. Under Aero Glass the borders and title bar of the form take on a translucent, glass-like appearance. When your WPF applications run on Windows Vista (and the Aero Glass theme is enabled) your windows will automatically be drawn this way. Is it possible to use WPF styles so that windows look like the Aero Glass theme, even when they are not running on Vista?
The answer is yes, as Unni from the Expression team at Microsoft shows here. Although this is a good rendition of Aero Glass there are a few subtle differences. For example I belive the real Aero Glass theme adds a kind of blur effect to window title bars and borders, suggesting a more translucent than transparent glass. Also allowing the form to be dragged on the specially drawn title bar have not been implemented in this demo. None the less this is a pretty cool example. Just to re-iterate the example below is running on Windows XP, as the XP-themed explorer window in the background hopefully shows.
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.
WPFs data binding capabilities prevent developers having to write the brittle, hard-to-maintain code otherwise required to keep changes in the UI synchronized with changes in their own objects. It also handles conversion between an object’s data types and types that can be displayed in our UI. This example will show the most basic kind of data binding – binding text values for simple UI elements to properties of a .NET object. Further examples will cover binding to lists of objects, binding to non-text properties and the like. This example will lay the conceptual foundations for further understanding WPF data binding.
The object we will be binding to is an Employee object and is extremely simple – it has two read/write properties (Name and Department) and a third read-only Id property (a GUID). The UI to display this employee in is equally simple. We have two text boxes to allow the Name and Department properties to be edited, and a third Label control for displaying the ID. All these display fields are labeled accordingly.
In order for us to perform the binding we’re going to need the following things
-
an Employee instance for the UI to bind to (the data)
-
Some way of telling our UI elements to bind to that employee instance
-
Notation for specifying to the UI elements which properties of the employee to display
Creating a new employee instance (#1) is simple – it is just a plain old .NET object, which can be created in any number of ways. The way we point our UI markup (XAML) code to that object instance (#2) is by adding an ObjectDataProvider to our Window’s list of resources. When we define it we give it a key so we can refer to it later. This will be the point of indirection that we will use to plug our Employee object created at run-time into our UI.
<Window.Resources>
<ObjectDataProvider x:Key="employeeData"/>
Once we’ve defined the ObjectDataProvider we still haven’t quite covered off point #2 yet. We’ve created a place where the Employee object can be “provided”, but we still need to tell our text boxes and labels to use that data provider. Now we could go through and connect each control to the data provider but as our Employee object gets more complicated, or we start to want to show data from different objects on a single form this approach breaks down as we have to maintain the same value in many places. It would be desirable if controls that are grouped together on the screen could somehow share a kind of data context, so that each of them pointed to the same underlying data automatically. Fortunately in WPF there is – it is called the DataContext, and it works with the hierarchical nature of WPF element. Child elements automatically inherit the DataContext of their parent element.
In the sample application all the elements we want to display data in are children of the Grid used for controlling the layout, so we will specify the DataContext for the Grid like so:
<Grid DataContext="{StaticResource employeeData}">
The {StaticResource} MarkupExtension looks up the resource we created in the Window.Resources section. It find it using the key we supply, and assigns that to the data context for the grid and its child elements.
The next step is to specify to the UI elements which properties they should display. We do this using another MarkupExtension, the {Binding} markup extension which allows us to specify which properties we want to display using the following syntax:
<TextBox Text="{Binding Path=Name}"/>
This is all that we need to do in the XAML markup to bind the Employee to the various fields on the form. The last thing we need to do is to create an Employee instance in our application code, and associate it with the ObjectDataProvider. This is done in the example using the following VB.NET code (_ceo is an Employee instance we have created):
Dim employeeProvider As ObjectDataProvider = _
CType(FindResource("employeeData"), ObjectDataProvider)
employeeProvider.ObjectInstance = _ceo
In order to verify that changes to the UI were also reflected in the underlying Employee object we’ve also added a button. When clicked the button displays a message box showing the output of the ToString method of the Employee. We overrode the base ToString method to write out the Name, Department and Id for the Employee object.
Hopefully this has shown you how simple WPF data-binding can be. When combined with other features of the platform such as templating it provides a great way to visualize and manipulate your data. Stay tuned for more involved data binding topics.
The WPF samples (along with all the other WinFX samples) in the windows SDK are installed as a zipped up file called "allwinfxsamples.zip" that is located at "C:\Program Files\Microsoft SDKs\Windows\v1.0\samples" (assuming you installed the SDK to its default location). There are lots of useful samples organized by topic, with sample code in VB.NET/C# and XAML.
If you need to find out how to do something with the latest release these samples are often your best source of information because they should all work against that version of WPF you have installed. Unfortunately because WPF is still being refined samples published on the internet can quickly become broken as new versions of WPF are released. Although in many cases the changes required to "forward port" samples to the new version are not difficult the SDK samples have a distinct advantage here because they target a specific release.
WPF's content model is usually described as either rich or flexible - but what does the term "content model" mean in this context? Legacy windows programming platforms (MFC,ATL,VB6,Windows Forms etc) had many different types of controls that could be added to UI - buttons, labels, pictures, text boxes, lists etc. Each one of these types of controls usually had a fairly fixed set of things they could display - buttons, labels and text boxes could contain text, picture controls could contain images, lists contained a number of list items, each of which in turn contained text. To go outside this usually involved some effort - either writing a control from scratch that could contain a different type of content, or taking over part of the rendering process and performing some "owner drawing", which was often somewhat involved.
In contrast WPF doesn't make nearly as many assumptions about the types of content you would like to display inside a given control. Content of different kinds can be nested freely inside other elements. The following XAML snippet defines a button. Inside the button is contained some text, an ellipse, a text box and a combo box. Inside the combo box is more text and another ellipse.
XAML (Feb 2006 CTP):
<Button Width="200">
<StackPanel>
<TextBlock>Learn WPF!</TextBlock>
<Ellipse Width="100" Height="40" Fill="#feca00" />
<TextBox Text="this is a text box"></TextBox>
<ComboBox>
<ComboBoxItem>
More text...
</ComboBoxItem>
<ComboBoxItem>
<Ellipse Width="80" Height="20" Fill="#58290A" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
</Button>
Although this example is not the type of functionality that has been required for in any application that I have ever written, it is this flexible way that content can be nested and composed that underpins many of the powerful things that can be done in WPF.
In a
previous how-to
we looked at how to use a ScaleTransform to allow the user to re-size a region of a form using a slider control. When the value of the slider changed some C# code in the event handler created a new ScaleTransform, set its ScaleX and ScaleY values based on the value of the slider, and then assigned the ScaleTransform to the LayoutTransform of the item we wanted to re-size. It is also possible to do this without writing any "code" by creating a ScaleTransform in XAML and binding the ScaleX and ScaleY properites of the transform to the value of the slider.
WPF has some powerful data binding capabilities - usually people think of data binding as binding the property of a control to some underling data-structure - either relational data, XML or some "business object" from the problem domain. Binding can also be used to bind the value of two UIElements together. In the sample code (shown in full below) we've defined a slider in the same way as the previous example, but without specifying a ValueChanged handler.
<Slider Name="UISizer" Minimum="0.1" Maximum="5" Value="1">
Then for the grid we wish to re-size we add a child LayoutTransform element, and inside that we nest our ScaleTransform. The values for the ScaleX and ScaleY properties are bound to the value of the slider using the Binding markup extension. Markup extensions are defined inside the curly braces, and look similar to the way XPath functions can be used in XSLT. This binding expression tells WPF that the ScaleX and ScaleY properties should be bound to the Value property of the UISizer element.
<Grid Name="ItemToResize" Background="#feca00" Width="500">
<Grid.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=UISizer, Path=Value}"
ScaleY="{Binding ElementName=UISizer, Path=Value}" />
</Grid.LayoutTransform>
The complete XAML (below) does all that the previous demo did, without requiring any C# or VB.NET code.
XAML (For Feb CTP):
<Window x:Class="ScaleTest.Window1"
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
"
Title="ScaleTest" Height="300" Width="300"
>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="12">Smaller</TextBlock>
<Slider Name="UISizer" Minimum="0.1" Maximum="5" Value="1">
</Slider>
<TextBlock FontSize="20">Bigger</TextBlock>
</StackPanel>
<Border BorderThickness="3" BorderBrush="#58290A">
<Grid Name="ItemToResize" Background="#feca00" Width="500">
<Grid.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=UISizer, Path=Value}"
ScaleY="{Binding ElementName=UISizer, Path=Value}" />
</Grid.LayoutTransform>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" FontSize="15">Foo:</TextBlock>
<TextBlock Grid.Column="0" Grid.Row="1" FontSize="15">Bar:</TextBlock>
<TextBox Grid.Column="1" Grid.Row="1" Padding="3"></TextBox>
<ComboBox Grid.Column="1" Grid.Row="0" Padding="3">
<ListBoxItem>
<TextBlock>Value 1</TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock>Value 2</TextBlock>
</ListBoxItem>
<ListBoxItem>
<Ellipse Width="50" Height="35" Fill="Orange" />
</ListBoxItem>
<ListBoxItem>
<TextBlock>Value 4</TextBlock>
</ListBoxItem>
</ComboBox>
</Grid>
</Border>
<TextBlock FontSize="20">This item will not change its size...</TextBlock>
</StackPanel>
</Window>
Many WPF applications allow a region of the form to be re-sized by the user by moving a slider control. This is a great feature for users with poor vision, who can resize regions of the form to a size that suits them, and also shows WPF's vector-based graphics system off to great effect. So how is this achieved? Sizing the UI relies on the ScaleTransform class in WPF. As the name suggests a ScaleTransform allows a specific UIElement and its children to be re-sized, either at the layout or at the render stage.
To apply a ScaleTransform to a UIElement you create a new ScaleTransform object, set the ScaleX and ScaleY properties and then assign the newly created ScaleTransform to the UIElements LayoutTransform or RenderTransform property. If you assign the new ScaleTransform to the LayoutTransform property the scaling happens before the sizes of all the controls have been measured, and the UIElements around the one you are scaling will re-size in accordance with the new size of the UIElement you scaled. If you assign the new ScaleTransform to the RenderTransform property then the scaling happens after the sizes of all the UIElements have been measured, and the new size of the UIElement you are changing won't be taken into account.
In the example application we create a slider, and attach a method to the ValueChanged event of the slider. In the ValueChanged event we create a new ScaleTransform, and assign it to the LayoutTransform of the UIElement we wish to size (in the sample application it is a grid with a text box, combo box and some text blocks nested inside it).
XAML:
<Slider Name="UISizer" Minimum="0.1" Maximum="5" Value="1" ValueChanged="UISizerValueChanged">
C#:
public void UISizerValueChanged(object sender, RoutedEventArgs e)
{
if (ItemToResize != null && UISizer != null)
{
// we could use ItemToResize.RenderTransform here too
ItemToResize.LayoutTransform =
new ScaleTransform(UISizer.Value, UISizer.Value);
}
}
Tip: Don't scale the slider (or other control) that the user will click on to re-size the region of the window. Doing so will cause the slider to change in size, which interferes with (usually magnifying) the changes made by the user. Also set sensible maximum and minimum values for the slider.