Monday, July 28, 2008

Getting started with Windows Presentation Foundation

The following post describes the first section of my future talk on "Windows Presentation Foundation". Given below is the list of topics/concepts this post covers:




  1. Lifecycle of a WPF application


  2. What are the WPF-like applications possible today?


  3. How to load XAML at runtime?


  4. Working with objects loaded from XAML at runtime?


  5. How to obtain XAML from objects?


  6. Some tools of trade.



Lifecycle of a WPF application 



Leaving the long stories of how great WPF is and what WPF is useful for, we directly jump into the lifecycle of a WPF application with the assumption that readers knows what WPF is mainly used for - generating data-driven windows application with great User Experience that could easily be achieved.



As we create a new WPF application project from Visual Studio, we get a bunch of different files. The article at http://wpfwonderland.wordpress.com/2008/07/16/understanding-the-xclass-attribute-and-visual-studio-2008/ provides a details description of what happens at compile time during a wpf application build. To summarize it in my own way, let us first look at the files generated with an empty WPF application project.



 



empty wpf project



The App.xaml is the entry point file whose contents are like shown below





   1: <Application x:Class="EmptyWPF.App"


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


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


   4:     StartupUri="Window1.xaml">


   5:     <Application.Resources>


   6:          


   7:     </Application.Resources>


   8: </Application>




Just like in the ASPX where the markup has a corresponding code-behind, WPF applications uses a similar partial-classes strategy where a code-behind file is linked to a XAML using x:Class attribute. The StartupUri attribute indicates the page the application should first show as the application is launched. Now if we look at the Window1.xaml (it has a corresponding Window1.xaml.cs)



An empty Window1.xaml is added to the application project whose XAML is shown below.





   1: <Window x:Class="EmptyWPF.Window1"


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


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


   4:     Title="Window1" Height="300" Width="300">


   5:     <Grid>


   6:         


   7:     </Grid>


   8: </Window>




Again, notice the x:Class attribute pointing to the  code behind file. Now let us add a button that displays Hello World! and to the click event, we display another message box that shows your name.



A button in XAML is as shown.





   1: <Button x:Name="btnDemo">This is the buttons' content</Button>




There are numerous ways in which a button's content is set. Shown below is a couple of other ways.





   1: <StackPanel>


   2:         <Button x:Name="btnDirect">This is the buttons' content</Button>


   3:         <Button x:Name="btnContentProp" Content="Set from Content Property"/>


   4:         <Button x:Name="btnSetUsingAttachedProperty">


   5:             <Button.Content>Set using Attached Property</Button.Content>


   6:         </Button>


   7:         <Button x:Name="btnFullyQualifiedButtonProperty" Button.Content="Fully Qualified Setter"/>


   8: </StackPanel>




Also shown is a Layout which specifies the way the buttons are to be displayed. In layman terms, layout is a container with a specification on how its contents are laid out. Some of the layouts we commonly see are StackPanel, Grid, DockPanel. We look at layouts in more detail in the next segment of the talk.



Now when we compile the application project, the XAML file is compiled into a BAML (Binary Application Markup), stored as a resource inside the assembly. Also added is a .g.cs file which contains a partial class for that xaml file code-behind which hooks up the code with the XAML. There is a IConnect() method inside the generated .g.cs file which performs this form of binding the code with markup, thereby allowing the objects to react to events. These intermediate files can be found in the "obj" directory of the project folder. Note that the msbuild uses PresentationBuildTasks.dll to automate this codegeneration and XAML to BAML conversion process.



As a proof that the BAML file is embedded into the assembly as a resource, we use Reflector to examine the assembly. Note that the Reflector does not diassemble the BAML and instead we can use a BAMLViewer like add-on to view the BAML as XAML.



image



 



Notice the EmptyWPF project contains the BAML files as resources. Also shows the BAML viewer in use.



When the application is run, the CLR delegates the control to the PresentationFramework which creates an appropriate host window which renders the UI that is taken from the BAML. In order to do this, the Presentation Framework generates an object graph from the BAML (also the visual and logical trees, covered later). Please observe a portion of the call-stack for the EmptyWPF application we created shown below.





   1: PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window) Line 1901 + 0x18 bytes    C#


   2: PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window window) Line 260 + 0x9 bytes    C#


   3: PresentationFramework.dll!System.Windows.Application.Run() Line 222 + 0x9 bytes    C#


   4: EmptyWPF.exe!EmptyWPF.App.Main() + 0x4c bytes    C#




It is the PresentationFramework that takes care of creating the Window and running the application with a certain lifecycle (Like raising the Startup events, etc.)



WPF application types and wpf-ish applications



XBAP - Xaml Browser Applications, also known as XBAPs are full-fledged WPF applications that are hosted inside a browser. As with the case of other such applications (like flash and applets), XBAPS runs in a restricted sandbox environment, thereby are limited in certain functionality. When an XBAP is built and is ready to be deployed, the .exe, .manifest and .xbap files should be copied into the directory we wish to publish the application in. It is similar to a Click-Once application except that the .xbap is used instead of .application. Using Visual Studio publish wizard simplifies the deployment issues and we could later copy the files published to a different location. Few configuration settings are to be made to the server hosting the application - application mime-types shown below are to be registered within the server.





   1: MIME Type                    Extension 


   2: ication/manifest             .manifest 


   3: ication/x-ms-xbap            .xbap 


   4: ication/octet-stream         .deploy 


   5: ication/x-ms-application     .application 


   6: ication/vnd.ms-xpsdocument   .xps 


   7: ication/xaml+xml             .xaml 




One could set the trust levels of an XBAP application using visual studio project properties window. Some other restrictions in XBAP is that they cannot create objects of type Window or NavigationWindow (thereby no pop-ups or dialog boxes). They have the HostInBrowser property set to true inside the msbuild file.



A Xaml file could also be viewed directly inside a browser. When you open IE and open the XAML file, it renders the output on the IE window. The presentation framework, in this case would not create a NavigationWindow or a Window to host the XAML content instead uses the browser as a window.



Other WPF-ish application are the Silverlight applications. As of now, the talk might not constitute Silverlight content and instead if time permits, few sections of Silverlight would be posted on my blog.



Loading XAML at runtime



We now look at how XAML could be loaded at runtime. As an example to demonstrate the api and its usage, a simple XamlPad like application is developed the source of which would be posted online here. In the last section of this post (Tools of Trade), we present few tools that helps learn WPF and until then just understand that XAML pad is a XAML editor that can be used to write/learn to write XAML. You can access XAMLPad by typing "xamlpad" in Visual Studio Command Prompt.



So the requirement for our own xaml pad is that the application should be able to first read XAML written by the user, validate it and then convert it into BAML and show the output on a component inside the window. Consider the case where the user has entered just the XAML for a Label which contains a button whose content instead contains a checkbox (the motto of WPF was to enable a content element to contain anything include other controls, media, what not!). The Xaml would look as





   1: <Label   Name="lblWithButtonAsContent"


   2:          Width="80"


   3:          Height="50"


   4:          Background="Red"


   5:          Padding="10">


   6:     <Button Name="btnWithCheckBoxContent">


   7:         <CheckBox>Wow!!</CheckBox>


   8:     </Button>


   9: </Label>




At the time we render this XAML, we first need to specify the loader class we use about the schema that the XAML entered uses. So we need to use .NET XML API to add namespaces. We could add namespace to each of the elements in the XAML or just add it to the root element. Shown below is the code that adds the following namespaces





   1: private string xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";


   2: private string xmlns_x = http://schemas.microsoft.com/winfx/2006/xaml;




The code shown below creates an XmlDocument from the XAML entered and then it adds the namespaces if required.





   1: private XmlDocument GetXamlDoc()


   2: {


   3:            ///Xaml is XML. So we load the XAML as an XmlDocument.


   4:            /// The reason XmlDocument is used instead of XDocument is that we would like to add namespaces


   5:            /// so that the parsing is performed without issues. 


   6:            XmlDocument xdoc = new XmlDocument ( );


   7:            xdoc.LoadXml ( txtCode.Text ); //txtCode.Text contains the XAML


   8:  


   9:            ///We need to add the presentation framework namespace as well as the XAML namespace.


  10:            ///This way a <Button> like WPF objects would be properly identified.


  11:            if (string.IsNullOrEmpty ( xdoc.DocumentElement.GetAttribute ( "xmlns" ) )) xdoc.DocumentElement.SetAttribute ( "xmlns", xmlns );


  12:            if (string.IsNullOrEmpty ( xdoc.DocumentElement.GetAttribute ( "xmlns:x" ) )) xdoc.DocumentElement.SetAttribute ( "xmlns:x", xmlns_x );


  13:            return xdoc;


  14: }




Once a valid XAML document is ready, we now convert this Xaml into object and set it to the Content Property of a Content Element (a button or a frame). In the sample application, we use a frame.





   1: private object GetUIFromXaml ( )


   2: {


   3:             XmlDocument xdoc = GetXamlDoc();


   4:  


   5:             ///Now we are done with modifications, so we need an XmlReader. In this case, we use a XmlTextReader


   6:             /// We build a StringReader on the updated xml.


   7:             XmlTextReader xmlReader = new XmlTextReader ( new StringReader ( xdoc.OuterXml ) );


   8:  


   9:             ///The above code is all the ground work needed to successfully load Xaml at runtime.


  10:             ///The  XamlReader.Load() does the trick here. It compiles the Xaml into BAML and then builds the Object graph.


  11:             /// At the sametime, both the Visual Tree as well as the Logical Tree is constructed.


  12:             return XamlReader.Load ( xmlReader );


  13: }




The key is the XamlReader.Load() method which is given the XmlReader object (to the Xaml Code). It returns an object which could then be set to the content property.



Working with objects loaded at runtime from a Xaml file



Now that we are ready to load UI from a XAML file at runtime, we might want to see if we can attach events on these objects. Yes, we could attach events such as Click (for button) to these objects but is not as straight-forward. The code shown below does exactly what we are talking about.





   1: private void TestEvents ( DependencyObject dependencyObject )


   2: {


   3:     ( dependencyObject as FrameworkElement ).MouseMove += 


   4:                     new MouseEventHandler ( Page1_MouseMove );


   5: }


   6:  


   7: //invoked from Parse() method in the sample


   8: private void Parse()


   9: {


  10:     //code missing ...


  11:                object obj = GetUIFromXaml ( );


  12:                TestEvents ( obj as DependencyObject );


  13:     //code missing ...


  14: }


  15:  




Getting XAML from an object



XamlReader is used to read Xaml and convert the XAML into objects. There is a XamlWriter which does exactly the opposite. Given an object, XamlWriter can generate the corresponding XAML for that object. Shown below is the code that does that. Note that this does not work in the XBAP due to limitations on Reflection (considered as a security breach).





   1: public void SetXamlToTheCenterElement ( )


   2: {


   3:         lblCenter.Content = new TextBox ( )


   4:                                 {


   5:                                      Text = XamlWriter.Save ( this ),


   6:                                      Width = 200,


   7:                                      Height = 200,


   8:                                      TextWrapping = TextWrapping.Wrap


   9:                                  };


  10: }




The output looks like the one shown below.



image 



The XAML for the above window is simple and is shown below. Also note that, the call to the SetXamlToTheCenterElement ( ) is made after the InitializeComponent() from the constructor.





   1: <Window x:Class="EmptyWPF.Window1"


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


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


   4:     Title="Window1" Height="300" Width="300">


   5:     <DockPanel>


   6:         <Label DockPanel.Dock="Bottom" Background="Yellow">Bottom</Label>


   7:         <Label DockPanel.Dock="Top" Background="Red">Top</Label>


   8:         <Label DockPanel.Dock="Left" Background="Green">Left</Label>


   9:         <Label DockPanel.Dock="Right" Background="Maroon">Right</Label>


  10:         <Label HorizontalAlignment="Center" VerticalAlignment="Center" Name="lblCenter">Everything!</Label>


  11:     </DockPanel>


  12: </Window>




With the information that I shared until now, we could see that designing a WPF Form Designer is not as tough as it is in case of Windows Forms or other GUI technologies. In the subsequent posts, we look at the differences between the WinForms and WPF applications with respect to architecture as well as development.



Tools of trade



Blend, Visual Studio - Until now, for a Microsoft platform, Visual Studio has been the ultimate tool required. Now with WPF, Microsoft Blend appears to replace Visual Studio for Designers and for developers its Visual Studio again. VS 2008 Cider (WPF designer) is pretty neat and uses the same engine as the Blend. But the functionality that Blend provides is way ahead than what VS provides. Though this is a developer talk, we look at using Blend in the WPF animation demo.



Mole, Snoop - While Mole is a Visual Studio Debug Visualizer, Snoop is a stand alone tool. Mole allows us visualize WPF elements at runtime, change the properties and do a lot more stuff. For a professional application development, this is a handy tool and a must have in the kit.



KaXaml - A light-weight XAML editor and a tool that looks more like Blend with intellisense, syntax highlight, color picker, xml crunching, etc. kaXaml is a good replacement tool for XamlPad. Another alternative tool is the XamlPadX which supports add-ins and also has a visual tree visualizer, which kaxaml misses out.



Books, Blogs



Programming WPF - The treasure is always the information and not the tools. Firstly, the best book you can lay your hands upon is Programming WPF the book written by Chris Sells and Ian Griffiths, both of whom are excellent resources for anything that is .NET. If you wish to use WPF, you should buy this book.



Applications = Code + Markup - As always, Petzold book on WPF is the bible for a technology he writes on. Serious cons with this book is that it makes you sleep in no time given the bad appearance of MS Press books. But content, it undoubtedly gives a solid expertise.



Blogs - Tim Sneath has a list of WPF blogs that one must follow at this page. Personally I like the set of WPF gurus who are now at wpfdisciples.com. The amazing thing is that most of the stuff the blog about is mostly discussed in the Programming WPF book!



In the next post, we look at Visual Trees and Logical Trees in a WPF application.



No comments: