|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Services
Chapters
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionOk 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.What To Do In XAMLCaveat : 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. What To Do In CodeCaveat : 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.How To Reference Classes/Assemblies From XAMLOne 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 Lets see some examples of this. i'll cover 4 options here:
The code that accompanies this article has comment in the So without further ado lets carry on shall we. Assembly Ref 1 : Using A Local (Same Namespace) Control In A WindowThis 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 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
We can simply use the following xmlns: namespace declaration within the opening tag of our xmlns:local="clr-namespace:WPF_Tour_Beginners_Part_2;assembly="
This then enables us to now use the UserConrtrol2 <!-- 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 Assembly Ref 2 : Using A Local (Same Namespace) Class In A WindowThis is pretty much the same steps as we just followed for a local 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 xmlns:local="clr-namespace:WPF_Tour_Beginners_Part_2;assembly="
This then enables us to now use the custom class <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 So if we wanted to we could use this instantaited instance of this
Assembly Ref 3 : Using A External (Different Namespace/Assembly) Control In A WindowThis is pretty much the same steps as we just followed for a local 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
As before can simply use the following xmlns: namespace declaration within the opening tag of our xmlns:SeperateWPFUserControlDll="clr-namespace:SeperateWPFUserControl_Dll;assembly=SeperateWPFUserControl_Dll"
This then enables us to now use the UserConrtrol1 <!-- 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. Assembly Ref 4 : Using A External (Different Namespace/Assembly) Control In A WindowThis is pretty much the same steps as we just followed for a local 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 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 Markup ExtensionsMarkup 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 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 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. What Are Resources, And How Can They Help MeResources are re-usable objects that may be declared in various places:
You can think of a resource as a single object that stored in a When you define resources in markup, you assign the unique key through the Every framework-level element ( So once you declare a resource you are free to use it on a element. For example the following XAML declares a new <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 One thing to note is that we are actually using the A resource can be referenced as either a static resource or a dynamic resource. This is done by using either the According to the MSDN Resources (WPF) section these are the guidelines as an when to use static/dynamic resources. Static ResourcesStatic resource references work best for the following circumstances:
Dynamic ResourcesDynamic resources work best for the following circumstances:
Your Resource OptionsOk 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:
Application Level ResourceTo 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 <!-- 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 Level 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">
<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 Framework Element Level ResourceAs I stated earlier each ( <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 Seperate Loose XAML ResourcesWindows 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 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 <!-- 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 We're DoneAs 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. History18/01/08 : Initial Issue
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||