![]() |
Platforms, Frameworks & Libraries »
Windows Presentation Foundation »
XAML
Beginner
License: The Code Project Open License (CPOL)
WPF : A Beginners guide part 2 of nBy Sacha BarberAn introduction into XAML / code and WPF resources |
C# (C# 3.0), .NET (.NET 3.0, .NET 3.5), WPF, Architect, Dev
|
||||||||||
|
Advanced Search |
|
|
|
||||||||||||||||
Ok ok, so it seems that from the response of my WPF : A Beginners guide part 1 of n article, that there are enough people interested in a "beginners guide to WPF" to warrant me carrying on with this series, so I will. I have however, come to a decision, and that is, that this article, will actually encompass the following topics :
But what exactly am I going to talk about in this article? This is a bit of a hard one to nail down actually, it could come across as being a bit vague, but what I'm aiming to cover is a brief introduction into the following:
Basically in a nutshell that's what this article will be about. I have provided a simple project, which doesnt do anything actaully, apart from show you the syntax. I have decided to give you a really dumb demo app on purpose, I dont want to confuse you with Events/Commands/Databinding and Templates/Styles at this stage. So the demo app is intentionally dumb. If you have a beef with me supplying something has does nothing, sue me. I have an excellent lawyer.
>So without futher ado, let's start shall we.Caveat : This is only a recommendation, you can do what you want, this is just the way I like to do things.
Generally WPF could be considered a quasi Model View Controller (MVC) type pattern. Ok the model and the controller are joined together, but the XAML / Code behind, does give you a nice segregation from View to code behind logic, in the same way that ASP .NET does. Its not the classic MVC, but the view is definately seperate, thanks to XAML.
I see this as a good thing, and as such I would recommend doing the following in XAML :
Wow that was a short list wasn't it. And to my mind that is how it should stay. There will obviously be times where exceptions have to made, but to be honest I think that by abiding by this guideline, you will acheive a quite clean/segregated design.
Caveat : This is only a recommendation, you can do what you want, this is just the way I like to do things.
The way I see it, the code behind file (C#|VB .NET) should carry out all the event handling and cutsom logic to drive the UI. The UI should be fairly dumb. There are a couple of exceptions where it may be useful to do things in XAML that are just a ball ache to do in code behind, and of course if this turns out to be the case you should use XAML. There is no right answer to this part, you have to make your own best call. But anyway that said, this is what I would recommend:
Basically the UI (XAML) should just present data, and anything else should be in code behind.
Josh Smith recently published a true MVC pattern in WPF at codeproject which is available right here, if you really can't wait and just want to crack on.
But be warned, it's fairly advanced stuff, but you know, I have high hopes for you lot...and hope that by the end of this series you will have mastered enough to look at Josh's code and at least get the basics, and go oh yeah there a binding, theres a resource cool..I get it.
It took me about 1/2 hour to get what he was up to, and I've been messing with this stuff a little while now.One thing that you will definately do at some point (if you wish to develop anything but, a single Window application) is reference your own classes/controls and maybe even classes/controls from a different .NET assembly, from XAML. Now in code behind this is a snitch right, we just write a
using System.Windows.Controls
//or VB .NET
imports System.Windows.Controls
And bada bing, we can referenec all the lovely objects in the System.Windows.Controls namespace. Nice and easy in code behind, but we are talkng XAML, hows all that work in XAML. Well the same rules apply, if we want to use a control from the current namespace we need someway in the XAML of stating we wish to use a controls from the current namespace. This is done with a namespace directive at the top of the XAML file where you wish to use the control/class. Yep that right classes can also be instantiated directly in XAML code, provided they are referenced using the correct namespace.
Lets see some examples of this. i'll cover 4 options here:
Window Window Window Window The code that accompanies this article has comment in the Window1.xaml that you can use to check these Assembly control references with. I've tried to make it easy for you.
So without further ado lets carry on shall we.
This is the easist of the 4 methods to reference classes within XAML. As the class we wish to reference is within the same namespace we can simlpy include a xmlns: namespace declaration within the opening tag of our Window (note I am using Window but this could be Frame or any other hosting control for WPF.
Lets say the current application I am working with has a namespace of "WPF_Tour_Beginners_Part_2" and that this is also the name of the generated Assembly. And that we have a UserControl called UserConrtrol2, in this namespace:

We can simply use the following xmlns: namespace declaration within the opening tag of our Window.
xmlns:local="clr-namespace:WPF_Tour_Beginners_Part_2;assembly="
This then enables us to now use the UserConrtrol2 UserControl in the XAML markup as follows:
<!-- Assembly Ref 1 : This is how we declare a class that is not part of the standard Microsoft namespace.
The xmlns: at the top of this file may be used to reference local (thus the name local:
same namepspace), or entirely different Dll. Its all down to how the xmlns: is defined.
If the class is in a different assembly, the assembly part of the xmlns: is required, otherwise
only the namespace is required. Note the clr-namespace/assembly string IS CASE SENSITIVE
This control is in the current namespace/assembly
-->
<local:UserControl2/>
You may why I use the word local, in the xmlns: declaration at the within the Window. Well the answer is that you can use whatevery name you want, but local: is used in lots of books and has sort of become the defacto convention for same namespace objects. I guess this stems from the fact that they are indeed local to the current namespace/Assembly
This is pretty much the same steps as we just followed for a local UserControl. The only difference is that a UserControl is a visual object in WPF (basically inherits from Visual or Visual3D) which means it can be used as part of the UI. Classes however may or may not inherit from Visual or Visual3D, so may or may not be UI type objects. If they do not inherit from Visual or Visual3D they will not be able to be used in UI markup, but can still be used as Resources. Now I know we haven't discussed Resources yet, so i'm sorry about that, but for now you are just going to have to gloss over this and accept that we can instantiate a class by putting a reference to it in a Resource section or Resource file of a XAML file
Lets say the current application I am working with has a namespace of "WPF_Tour_Beginners_Part_2" and that this is also the name of the generated Assembly. And that we have a custom class called LocalClass, in this namespace:

As before can simply use the following xmlns: namespace declaration within the opening tag of our Window.
xmlns:local="clr-namespace:WPF_Tour_Beginners_Part_2;assembly="
This then enables us to now use the custom class LocalClass in the XAML markup, as a Resource as follows:
<StackPanel x:Name="sp1" Orientation="Vertical">
<!-- I have added this ust to show you that you can add classes to XAML. The ones i've used
can't be part of the actual UI, as they aren't UI controls (dont inherit from Visual).
So I've put them into a resource Dictionary.
-->
<StackPanel.Resources>
<!-- Assembly Ref 2 : we can also use local (same namespace) classes in XAML, providing the
xmlns: at the top of this file is correct -->
<local:LocalClass x:Key="localClass1" AnIntProp="5"/>
</StackPanel.Resources>
</StackPanel>
So what about that. We've instatiated a LocalClass object in XAML now, and set its AnIntProp property to 5. Note that in order to be able to construct a class in XAML you will need to have a parameterless constructor on the source class being used.
So if we wanted to we could use this instantaited instance of this LocalClass class in code behind, we can simply grab the Resource and use the instantaited object as if it had been created and added to the heap in code behind using the new keyword. Let's see this, again its a Resource thing (sorry for jumping ahead), but I think it's a fairly useful thing to demonstrate.
This is pretty much the same steps as we just followed for a local UserControl. The only difference this time is that the UserControl we are trying to use in the current Window actually lives in a different Assembly (the demo app uses an Assembly called "SeperateWPFUserControl_Dll"). So we obviously still need to reference this "SeperateWPFUserControl_Dll" assembly. But how do we reference and use it in XAML.
Well it's not that different from what we saw earlier, the only addition to the xmlns: namespace declaration, is the inclusion of the Assembly name
Lets say the current application I am working with has a namespace of "WPF_Tour_Beginners_Part_2" and that we are trying to reference a 3rd party Assembly ("SeperateWPFUserControl_Dll" in the demo app) and that the 3rd party Assembly contains a UserControl called "UserControl1":

As before can simply use the following xmlns: namespace declaration within the opening tag of our Window, but this time it's fully qualified and included the Assembly name also
xmlns:SeperateWPFUserControlDll="clr-namespace:SeperateWPFUserControl_Dll;assembly=SeperateWPFUserControl_Dll"
This then enables us to now use the UserConrtrol1 UserControl in the XAML markup as follows:
<!-- Assembly Ref 3 : This is how we declare a class that is not part of the standard Microsoft namespace.
The xmlns: at the top of this file may be used to reference local (thus the name local:
same namepspace), or entirely different Dll. Its all down to how the xmlns: is defined.
If the class is in a different assembly, the assembly part of the xmlns: is required, otherwise
only the namespace is required. Note the clr-namespace/assembly string IS CASE SENSITIVE
This control is in the SeperateWPFUserControl_Dll namespace and SeperateWPFUserControl_Dll.dll
-->
<SeperateWPFUserControlDll:UserControl1/>
As before you are free to choose whatever name you like to the namespace, though its probably a good idea to call it the same name as the source Assembly where the item being used resides. This may be easier to fault find should something go wrong.
This is pretty much the same steps as we just followed for a local Class. The only difference here is that we will be using classes from some of the core .NET Assemblies, but the principle is the same.
Let say we want to use the following classes in our XAML
As before can simply use the following xmlns: namespace declaration(s) within the opening tag of our Window.
xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
This then enables us to now use these classes in the XAML markup, as a Resource as follows:
<StackPanel x:Name="sp1" Orientation="Vertical">
<!-- I have added this ust to show you that you can add classes to XAML. The ones i've used
can't be part of the actual UI, as they aren't UI controls (dont inherit from Visual).
So I've put them into a resource Dictionary.
-->
<StackPanel.Resources>
<!-- Assembly Ref 4 : we can even use standard System namespace classes in XAML, providing the
xmlns: at the top of this file is correct -->
<collections:Hashtable x:Key="ht1">
<sys:Int32 x:Key="key1">1</sys:Int32>
<sys:Int32 x:Key="key2">2</sys:Int32>
</collections:Hashtable>
</StackPanel.Resources>
</StackPanel>
That's pretty much the same as we previously saw with the LocalClass, example.
Markup extentions enable you to extend the expressability of XAML. Where the markup extension string can be evaluated at run time and produce the appropriate object based on a string. Markup extensions are invoked in XAML with explicit and consistent manner.
When ever an attribute value is enclosed in {} braces, the XAML compiler/parser treats it as a markup extension value rather than a literal string.
For example lets consider the following Rectangle, which simply sets the Background property to null.
<Rectangle Fill="{x:Null}" Stroke="Black" StrokeThickness="2" Height="20"/>
As the compiler/parser sees the {} enclosed string, it will know this is a markup extension. However this is just a parlor trick that the XAML parser knows how to perform. We can declare MarkUp extensions in the same way as we used classes, previously described, directly in the XAML. This is achieved by using property-element syntax.
Property-element syntax
Property-element syntax, allows use to expand out any property into a full element of the XAML tree, which allows us to add child elements to it. To best understand this perhaps an example is needed.
We can imagine that we could declare a markup extension like this
<Binding RelativeSource="{RelativeSource modeEnumValue}" .../>
Or possibly like this
<object mode="{Binding RelativeSource={RelativeSource modeEnumValue} ...}" .../>
Now using Property element syntax, we are able to treat the mode property as a element in the tree that can have its own child elements, such as this example
<Binding>
<Binding.RelativeSource>
<RelativeSource Mode="modeEnumValue"/>
</Binding.RelativeSource>
</Binding>
//or a more complex example
<Binding>
<Binding.RelativeSource>
<RelativeSource
Mode="FindAncestor"
AncestorType="{x:Type typeName}"
AncestorLevel="2"
/>
</Binding.RelativeSource>
</Binding>
See we can do quite a lot in XAML, remember all these objects are just classes so there is no problem, us declaring them like this. The {} braces are really syntactic sugar for whats going on behind the scenes, which is, that a whole slew of classes (as just demonstrated) are being created behind the scenes. This Property element syntax, can be applied to anything. For example here is another property, being used as an element, for a Button Background property
<Button Width="auto" Content="Button">
<Button.Background>
<SolidColorBrush Color="Blue"/>
</Button.Background>
</Button>
System.Windows.Markup.MarkupExtension is the base class that all markup extensions inherit from. There are the following markup extension classes available as standard
The most common markup extensions used in WPF programming are those that support resource references (StaticResource and DynamicResource), and those that support data binding (Binding), note the abscence of the "Extension" suffix. This is implied by the compiler as the {} indicates a markup extension the "Extension" suffix is NOT required.
StaticResource provides a value for a XAML property by substituting the value of an already defined resource. For details, see StaticResource Markup Extension.
DynamicResource provides a value for a XAML property by deferring that value to be a run-time reference to a resource. A dynamic resource reference forces a new lookup each time that such a resource is accessed. For details, see DynamicResource Markup Extension.
Binding provides a data bound value for a property, per the data context that applies to the element. This markup extension is relatively complex, because it enables a substantial inline syntax for specifying a data binding. For details, see Binding Markup Extension.
RelativeSource provides source information for a Binding that can navigate several possible relationships in the run-time element tree. This provides specialized sourcing for bindings that are created in multi-use templates or created in code without full knowledge of the surrounding element tree. For details, see RelativeSource MarkupExtension.
There are also several other markup extensions (as shown above) that are not specific to the WPF application of XAML, but are instead part of the specification and namespace of XAML as a language. These are typically identifiable by the x: prefix. The WPF implementation for these uses the same MarkupExtension base class to provide the implementation.
x:Type supplies the Type object for the named type. This is used most frequently in styles and templates. For details, see x:Type Markup Extension.
x:Static produces static values from value-type code entities that are not directly the type of a property's value, but can be evaluated to that type.
x:Null specifies null as a value for a XAML property.
x:Array provides support for creation of general arrays in XAML syntax, for cases where the collection support provided by base elements and control models is deliberately not used.
Don't worry if you think the Binding and RelativeSource extensions look weird, we will be revisiting these in the binding article.
NOTE : You can also create your own MarkUp extensions, for example you can check out Tomer Shamam, excellent post WPF Control State Persistency where he creates a markup extension to persist WPF elements state to file.
I would recommend you also look at this MSDN article, Markup Extensions and XAML which goes into some more detail about markup extensions.
Resources are re-usable objects that may be declared in various places:
Resources property of the current Window (assuming we are working with a Window). Which means that the declared resource will have Window level scope. All UI elements that are part of the resource declaring Window, will have access to the declared resource. Resources property of any FrameworkElement or FrameworkContentElement. You can think of a resource as a single object that stored in a Dctionary of resource objects. And as such each resource MUST have a key, that enables the safe and unique retrieval of the resource from the ResourceDictionary.
When you define resources in markup, you assign the unique key through the x:Key Attribute. Typically, the key is a string; however, you can also set it to other object types by using the appropriate markup extensions. Nonstring keys for resources are used by certain feature areas in WPF, notably for styles, component resources, and data styling. But we'll see more on this in subsequent articles in this article series.
Every framework-level element (FrameworkElement or FrameworkContentElement) has a Resources property, which is the property that contains the resources (as a ResourceDictionary) that a resource defines. You can define resources on any element. However, resources are most commonly defined on the root element, which would normal be Window.
So once you declare a resource you are free to use it on a element. For example the following XAML declares a new SolidColorBrush resource on a Window, which is then used for the Background of 2 Button objects within the Window.
<Window x:Class="WPF_Tour_Beginners_Part_2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:SeperateWPFUserControlDll="clr-namespace:SeperateWPFUserControl_Dll;assembly=SeperateWPFUserControl_Dll"
xmlns:local="clr-namespace:WPF_Tour_Beginners_Part_2;assembly="
xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="Window1" Height="300" Width="600"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<SolidColorBrush x:Key="windowLevelResourceBlueBrush" Color="Blue"/>
</Window.Resources>
<StackPanel x:Name="sp1" Orientation="Vertical">
<!-- Declare 2 Buttons that use the Window.windowLevelResourceBlueBrush resource -->
<Button Width="auto" Content="1st Button : I use the resourceBrushBlue Window Resource"
Background="{StaticResource windowLevelResourceBlueBrush}"/>
<Button Width="auto" Content="2nd Button : I use the resourceBrushBlue Window Resource"
Background="{StaticResource windowLevelResourceBlueBrush}"/>
</StackPanel>
</Window>
This simple use of resources means that the 2 Button objects, will have a Blue SolidColorBrush used for their Backgrounds. (Note : As we say nothing about how to deal with what the button looks like when the mouse is over ism the Blue SolidColorBrush used for their Background, only applies when the mouse is not over the Button. We will be covering this, in the Styles/Templates article in this series).
One thing to note is that we are actually using the StaticResourceExtension (Remember we can ommit the "Extension" suffix), but what the heck is a StaticResourceExtension, and what's it do for us. Well there are actually 2 types of resources that we can use.
A resource can be referenced as either a static resource or a dynamic resource. This is done by using either the StaticResourceExtension Markup Extension or the DynamicResourceExtension Markup Extension markup extension.
According to the MSDN Resources (WPF) section these are the guidelines as an when to use static/dynamic resources.
Static resource references work best for the following circumstances:
DependancyObject or a Freezable (That's immutable object really) Dynamic resources work best for the following circumstances:
ResourceDictionary during an application lifetime. Ok so that was a brief (yes there is a whole lot more to resource, as with all this stuff, I cant tell you everything, you'll have to read around these topics) introduction into Resources.
What we are going to do now is see how to define some of these different resources. Lets say the following, types of resources:
Window level resource FrameworkElement level resource To create a global application scope resource, we simply add a Resources section to the App.xaml file (or whatever you application file is called). Lets see an example.
<Application x:Class="WPF_Tour_Beginners_Part_2.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml">
<Application.Resources>
<SolidColorBrush x:Key="appLevelResourceGreenBrush" Color="Green"/>
</Application.Resources>
</Application>
This means that any object within the present application can now use this resource. In the attached demo application I have created a single Button within the Window1.xaml that uses this application level resource.
<!-- Declare 1 Button that use the Application level appLevelResourceGreenBrush resource -->
<Button Width="auto" Content="1st Button : I use the appLevelResourceGreenBrush Application level Resource"
Background="{StaticResource appLevelResourceGreenBrush}"/>
<Window x:Class="WPF_Tour_Beginners_Part_2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:SeperateWPFUserControlDll="clr-namespace:SeperateWPFUserControl_Dll;assembly=SeperateWPFUserControl_Dll"
xmlns:local="clr-namespace:WPF_Tour_Beginners_Part_2;assembly="
xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="Window1" Height="300" Width="600"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<SolidColorBrush x:Key="windowLevelResourceBlueBrush" Color="Blue"/>
</Window.Resources>
<StackPanel x:Name="sp1" Orientation="Vertical">
<!-- Declare 2 Buttons that use the Window.windowLevelResourceBlueBrush resource -->
<Button Width="auto" Content="1st Button : I use the resourceBrushBlue Window Resource"
Background="{StaticResource windowLevelResourceBlueBrush}"/>
<Button Width="auto" Content="2nd Button : I use the resourceBrushBlue Window Resource"
Background="{StaticResource windowLevelResourceBlueBrush}"/>
</StackPanel>
</Window>
I simply uses the resource "windowLevelResourceBlueBrush" on the 2 Button objects
As I stated earlier each (FrameworkElement or FrameworkContentElement) has a Resources property. Which means that you can create a local (think only available to the current element and its children) resource.
<Window x:Class="WPF_Tour_Beginners_Part_2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:SeperateWPFUserControlDll="clr-namespace:SeperateWPFUserControl_Dll;assembly=SeperateWPFUserControl_Dll"
xmlns:local="clr-namespace:WPF_Tour_Beginners_Part_2;assembly="
xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="Window1" Height="300" Width="600"
WindowStartupLocation="CenterScreen">
.....
.....
<StackPanel x:Name="sp1" Orientation="Vertical">
<!-- I have added this ust to show you that you can add classes to XAML. The ones i've used
can't be part of the actual UI, as they aren't UI controls (dont inherit from Visual).
So I've put them into a resource Dictionary.
-->
<StackPanel.Resources>
<SolidColorBrush x:Key="parentLevelResourceOrangeBrush" Color="Orange"/>
</StackPanel.Resources>
......
......
<!-- Declare 1 Button that use the parent (the StackPanel) level parentLevelResourceOrangeBrush resource -->
<Button Width="auto" Content="Button : I use the parentLevelResourceOrangeBrush parent (the StackPanel) level Resource"
Background="{StaticResource parentLevelResourceOrangeBrush}"/>
</StackPanel>
</Window>
This means that any object that the actual FrameworkElement that declares the resource or its children are free to use the resource. Any attempt to use a resource declared in this manner will cause an Exception to be thrown.
Windows Presentation Foundation (WPF) resources support a merged resource dictionary feature. This feature provides a way to define the resources portion of a WPF application outside of the compiled XAML application. Resources can then be shared across applications and are also more conveniently isolated for localization.
Note that the ResourceDictionary element does not have an x:Key Attribute, which is generally required for all items in a resource collection. But another ResourceDictionary reference within the MergedDictionaries collection is a special case, reserved for this merged resource dictionary scenario. The ResourceDictionary that introduces a merged resource dictionary cannot have an x:Key Attribute. Typically, each ResourceDictionary within the MergedDictionaries collection specifies a Source attribute. The value of Source should be a uniform resource identifier (URI) that resolves to the location of the resources file to be merged. The destination of that URI must be another XAML file, with ResourceDictionary as its root element. Lets see an example of this shall we:
Lets start with how to define the actual loose XAML file.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="seperateResourceFilePinkBrush" Color="Pink"/>
</ResourceDictionary>
Thats enough. The only other thing to ensure that the "Build Action" is set to Resource or Page in Visual Studio
So how to do we then use this. Lets say we want to use this loose XAML file on a Button resources property, we can do something like the following
<!-- Declare 1 Button that uses a seperate loose XAML level Resource, namely the seperateResourceFilePinkBrush resource -->
<Button Width="auto" Content="Button : Uses a seperate loose XAML level Resource, namely the seperateResourceFilePinkBrush resource">
<Button.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="SeperateResourceDictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Button.Resources>
<Button.Background>
<StaticResourceExtension ResourceKey="seperateResourceFilePinkBrush"/>
</Button.Background>
</Button>
In this example as the Button declares the MergedDictionaries, we can the use ALL of the resources declared in the referenced loose XAML file, as if they were locally (local to the Button) declared resources.
As I mentioned earlier, I dont think this particular article of the series is that bad, not too much to digest here was there. Rest assured from here on in, it does get harder though.
18/01/08 : Initial Issue
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 11 Mar 2008 Editor: |
Copyright 2008 by Sacha Barber Everything else Copyright © CodeProject, 1999-2009 Web11 | Advertise on the Code Project |