After having read part 2 of this tutorial you know how to create a very simple Silverlight application in XAML, displaying some text in the browser. Let's see now how XAML actually works together with C#.

After having read this part you will:

  • know how to add content to your application both in XAML and in C# code,
  • understand how XAML works together with C#,
  • know when to use XAML and when C# in your application.

Let's get started!

One thing you will immediately notice during your Silverlight adventure is that you can very often do the same thing in many different ways and using different tools. This is great news because everyone have their habits and preferred programming methods, and it is important the tools allow everyone do their job the way they like.

XAML vs C#

Remember our "Hello World" application from the the previous part? You didn't write a single line of C# code, everything was written in XAML. Open the Page.xaml file. The code looks like this:

<UserControl x:Class="HelloWorld.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="Yellow">
        <TextBlock Text="Hello World!"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center" />
    </Grid>
</UserControl>

Remove the TextBlock from XAML now:

<UserControl x:Class="HelloWorld.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="Yellow"> 
    </Grid>
</UserControl>

You'll notice that the "Hello World!" text is now gone in the designer preview in Visual Studio. And indeed, it is gone when you run the application. Switch to the Page.xaml.cs file now, and put the following code in the Page constructor:

public Page()
{
    InitializeComponent();
    TextBlock text = new TextBlock()
    {
        Text = "Hello World!",
        HorizontalAlignment = HorizontalAlignment.Center,
        VerticalAlignment = VerticalAlignment.Center
    };
    LayoutRoot.Children.Add(text);

}

You just replaced some XAML with some equivalent C# code. You still cannot see the "Hello World!" text in the preview of the Page.xaml file, but when you hit F5, you can see the text is displayed as it was before. That's because the designer preview only parses XAML code and displays its preview. It doesn't execute the entire constructor of your control. But how is XAML actually parsed? Let's have a closer look at this.

XAML behind the scenes

Whenever you're dealing with a class that has both XAML and C# code, there's the InitializeComponent method that should be called in the very first line of the class's constructor. And indeed, it is there for you if you create a new class with the Visual Studio wizard. It is automatically generated for you by Visual Studio whenever you modify the XAML code. If you want to see where exactly this method is defined, turn on the Show All Files option in your project. In the obj/Debug subdirectory of the project you can find these two files: App.g.cs and Page.g.cs.

All files of the HelloWorld project, including the hidden ones

These are auto generated files, one for each class that contains XAML. If you open Page.g.cs, you will see it contains partial definition of the Page class. The Page class contains the definition of the InitializeComponent method. It also has a field for every element that has the x:Name attribute in the Page.xaml file. For example, it contains an internal field called LayoutRoot that is of type Grid, as defined in XAML:

<UserControl x:Class="HelloWorld.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="Yellow">
    </Grid>
</UserControl>

What InitializeComponent does is: it takes all controls defined in XAML, creates instances of them and sets all the properties you set in XAML. It does all the same you would do if you wrote the C# code manually. In our example when an instance of the Page class is created, InitializeComponent sets Page's Width and Height properties. It also initializes the LayoutRoot field with a new instance of the Grid class, and sets its background to yellow. And because the Grid control is inside of the Page control in XAML, the Grid control is set as the Content property of the Page control.

Now try commenting out the InitializeComponent method call in Page's constructor (in the Page.xaml.cs file). When you run the application it crashes with a NullReferenceException at this line:

LayoutRoot.Children.Add(text);

If you debug the code you can see it crashes because LayoutRoot is null. LayoutRoot has never been initialized because InitializeComponent has never been called.

If you want to get rid of all the XAML code and the InitializeComponent method call in your Page control, you can translate all your XAML to C#. You can do it in the Page.xaml.cs file like this:

public class Page : UserControl
{  
    internal Grid LayoutRoot;

    public Page()
    {  
        Width = 400;
        Height = 300;

        LayoutRoot = new Grid()
        {
            Background = new SolidColorBrush(Colors.Yellow)
        };
        Content = LayoutRoot;
    

        TextBlock text = new TextBlock()
        {
            Text = "Hello World!",
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment = VerticalAlignment.Center
        };
        LayoutRoot.Children.Add(text);
    }
}

Now everything is in C# and we don't use XAML at all (the content from the Page.xaml file is never instantiated). Notice now the entire class is defined just in one place and is not marked as partial anymore. When you used XAML, the class had to be defined as partial, because the other part of the class was defined in the auto generated Page.g.cs file.

If you want the above code to work in your project, you have to also comment out the partial Page class definition in the Page.g.cs file. Otherwise Visual Studio will complain the class is defined twice. Of course, if you modify the Page.xaml file, the Page.g.cs file will get regenerated and your code will stop working again. If you want to get rid of XAML permanently, you should remove the Page.xaml file from the project while keeping the Page.xaml.cs file. If you try to do it in Visual Studio, you'll notice these two files are "grouped" together and you cannot just remove the *.xaml file. To "ungroup" them, you have to open the project file HelloWorld.csproj in a text editor, find this part:

<Compile Include="Page.xaml.cs">
  <DependentUpon>Page.xaml</DependentUpon>
</Compile>

and delete the <DependentUpon> element:

<Compile Include="Page.xaml.cs">
</Compile>

Now you should be able to reload the project in Visual Studio and delete the Page.xaml file while keeping the Page.xaml.cs file.

As you probably noticed, some controls have Content property (in our example Page) and can only have one element set as their child. Other controls have Children property (in our example Grid) and can have entire collection of child controls. When you nest controls inside each other in XAML, the XAML parser acts smart and either sets the inner control as the Content of the outer control, or adds the inner control to the Children collection of the outer control. It all depends on which property is available in the outer control.

Another thing you probably noticed is that the Background property of Grid is of type Brush but in XAML it is initialized simply by color name. This is a shortcut.

This XAML code:

<Grid x:Name="LayoutRoot" Background="Yellow"> 
</Grid>

is equivalent to this one:

<Grid x:Name="LayoutRoot">
    <Grid.Background>
        <SolidColorBrush Color="Yellow" />
    </Grid.Background>
</Grid>

which in turn translates to this C# code:

LayoutRoot = new Grid();
LayoutRoot.Background = new SolidColorBrush(Colors.Yellow);

There are more shortcuts like this that make XAML code more compact than C# code :)

Which way to go?

At this point you are probably confused and don't know when you should use C# and when XAML. Remember, everything you write in XAML, you can write in C# too. The code above is an example. You removed the TextBlock control from XAML and instantiated it in C# instead, giving it the same properties as previously in XAML. The final result is exactly the same. There are however some differences:

  • With XAML you usually write more compact code than with C# (approx. 90 characters vs approx. 180 characters in the example above).
  • With XAML you can see the results in the designer preview without running the code and you cannot see the preview of the C# code.
  • XAML is for static code only, you cannot create controls dynamically (for example create a random number of buttons) but you can do it with C#.
  • XAML is parsed at runtime, so not all errors are caught when the code is compiled. For example, if you try to nest two Grid controls in one Page control, the code will compile and run, and generate a runtime exception: XmlParserException with some obfuscated, not user friendly message. In C# it is simply not possible to make such a mistake. This makes XAML code more difficult to debug than C# code.

As you see, in some more advanced cases you need to write C# code, because you cannot do everything with XAML. But sometimes you have choice and it's up to you how you write your code: in XAML or C#. I think it is important you learn both. Of course, you can act like an old school programmer and stick with the "real" C# code, ignoring XAML completely, and you can write entire applications this way. However XAML usually allows you to code faster, making you more productive. If that doesn't convince you, your UI designer should convince you. Whoever they are, they won't be able to read your C# code. But they can open your XAML code in Expression Blend, and make your application's UI look cool with a few clicks. I will explain how to do that later on.

Silverlight project build process - the big picture

I mentioned the mysterious XAP file before so let's have one last look at what happens when you rebuild your project. You have a bunch of C# files (both written by you and automatically generated for you by Visual Studio), and some XAML files. They are all compiled into a DLL file and the AppManifest.xaml file is generated. You can find them in the Bin/Debug subdirectory of your project. These files are then gathered together and compressed to one ZIP file.. that has the XAP extension. Yes, a ZIP file. If you take the HelloWorld.xap file and rename it to HelloWorld.zip, you can open it by double clicking and see its contents. The TestPage.html file is also generated. In our case the local machine acts as the server that contains TestPage.html and HelloWorld.xap, but you can copy these two files to a real remote HTTP server. Now that you have your Silverlight application published, either locally or on a real server, you can request the TestPage.html from a browser. It references the HelloWorld.xap file so the browser will run the Silverlight plugin which will download the XAP file. When the file is downloaded, Silverlight will extract it and read the AppManifest.xaml file. It contains information which DLL Silverlight needs to load and which class to instantiate first (by default it's the App class). Once an instance of the App class is created, the Silverlight application starts to execute.

Summary

At this point you should feel comfortable with XAML and C#, and using both in your Silverlight projects. You also have the big picture of what happens when you hit F5 - from compiling your code to executing it by the Silverlight plugin.

The source code of the modified "Hello World" application (written in C# without using XAML): HelloWorldWithoutXaml.zip