Monday 6 October 2008

Compiling a Hello World WPF App

Getting to know the Binary Application Markup Language and the ‘.g.cs ‘files

XAML code gets compiled into a combination of  C# file (.g.cs) and a BAML (.baml). The BAML file is an optimized, pre-tokenized version of the elements that were specified in the .xaml file. This is injected into the .Net Assembly (PE/DLL).

Notice that the static main method() gets generated in the App.g.cs file which points to an instance of WPF core application object (System.Windows.Application)

/// <summary>
        /// Application Entry Point.
        /// </summary>
        [System.STAThreadAttribute()]
        [System.Diagnostics.DebuggerNonUserCodeAttribute()]
        public static void Main() {
            HelloWorld.App app = new HelloWorld.App();
            app.InitializeComponent();
            app.Run();
        }

The MS build project file has the <ApplicationDefinition> tag which is used to specify the xaml file that defines the application class.(app.xaml) Below is a cut and paste from my HelloWorld.csproject file.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="
http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>9.0.21022</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{15622594-2CE3-40D5-9D03-8A3F8AE7C3AB}</ProjectGuid>
    <OutputType>WinExe</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>HelloWorld</RootNamespace>
    <AssemblyName>HelloWorld</AssemblyName>
    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <WarningLevel>4</WarningLevel>
    <StartupObject>HelloWorld.App</StartupObject>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Xml.Linq">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Data.DataSetExtensions">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
    <Reference Include="WindowsBase" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
  </ItemGroup>
  <ItemGroup>
    <ApplicationDefinition Include="App.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </ApplicationDefinition>
    <Page Include="Window1.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Compile Include="App.xaml.cs">
      <DependentUpon>App.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="Window1.xaml.cs">
      <DependentUpon>Window1.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs">
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="Properties\Resources.Designer.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <Compile Include="Properties\Settings.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
    <Compile Include="Test.cs" />
    <EmbeddedResource Include="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
      <SubType>Designer</SubType>
    </EmbeddedResource>
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
    <AppDesigner Include="Properties\" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
</Project>

Now, the combination of <ApplicationDefinition> element and the <Import> tag ‘Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets"’ will create our application entry point.

 

My ‘App.xaml’ file

<Application x:Class="HelloWorld.App"
    xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Startup="Application_Startup"
    StartupUri="Window1.xaml">   
    <Application.Resources>
    </Application.Resources>
</Application>

 

Notice the StartUp tag here. The connection between XAML and the code behind can be found in the file ‘App.g.cs’

 

/// <summary>
/// InitializeComponent
/// </summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public void InitializeComponent() {
    #line 4 "..\..\App.xaml"
    this.Startup += new System.Windows.StartupEventHandler(this.Application_Startup);
    #line default
    #line hidden
    #line 5 "..\..\App.xaml"
    this.StartupUri = new System.Uri("Window1.xaml", System.UriKind.Relative);
    #line default
    #line hidden
}

Now, we are sure that ‘App.g.cs’ has a significant role in bridging the event handlers specified in the XAML file to the code behind files (c# files). Since ‘App.g.cs’ is a partical class, may be visual studio uses it for Intellisense.  This is not all ?, Where does the XAML file go after the compilation process ?. Notice that if you navigate to .\obj\debug or .obj\release folders you find a Window1.baml file. Now, as i’ve already said before, BAML is an optimized version of XAML.

I found this explanation on the web [1] , dumping the content here.

What tools are used to generate the .G.CS classes when a XAML project is created? Also is there an API for this so I can do it at runtime?

The answer:

In order to compile xaml files, we have declared a few MsBuild tasks in PresentationBuildTasks.dll. Projects that contain <Page Include="Page1.xaml" /> and that import MicrosoftWinFX.targets will be markup compiled.There are 2 "Targets" defined in that targets file that do the work for markup compilation. MarkupCompilePass1 – creates a .g.cs file for each Page.  If the xaml doesn't refer to any classes defined in the assembly being created during the build, a baml file is created. This baml file is a binary (pre-tokenized) version of the hierarchy of objects and property sets from the xaml file. MarkupCompilePass2 – creates all baml files that weren't able to be created in Pass1

The only APIs that WPF exposes to do this markup compilation are those msbuild based tasks in PresentationBuildTasks.dll.MsBuild can be called at runtime (from full trust apps).

 

Check out [2] for more on BAML and .net assembly. If you are thinking of writing your own UI representation like YAML etc, then you might need the BAML Reader/Writer [3].

 

Some Links to understand what’s going on beneath the hood.

1. Rrelyes, MS

2. A nice little anatomy of a wpf compilation here

3. BAML Reader/Writer

No comments:

Post a Comment