Search code examples
c#wpfxamlruntimecode-generation

Compile/Execute XAML during program runtime


I would like to create an WPF Application which retrieves XAML Code from a Database and displays the retrieved code.

Lets say the database return the following code:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="mainGrid">
        <Button Content="test case 1" 
                HorizontalAlignment="Left" 
                Margin="10,10,0,0" 
                VerticalAlignment="Top" 
                Width="100" 
                Click="TestCase1_OnClick" 
                Height="29"/>
    </Grid>
</Window>

How do i execute this code (or maybe just the content of the mainGrid) during runtime?


Solution

  • This is how i solved the problem:

    I have 3 files MainWindow.xaml, MainWindow.xaml.cs and wpftest.csproj.

    MainWindow.xaml:

    <Window x:Class="wpftest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Button Content="Button" HorizontalAlignment="Left" Margin="122,75,0,0" VerticalAlignment="Top" Width="75" Click="ButtonBase_OnClick"/>
        </Grid>
    </Window>
    

    MainWindow.xaml.cs

    namespace wpftest
    {
        using System.Windows;
    
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
            {
                MessageBox.Show("Hello world", "does it work?");
            }
        }
    }
    

    wpftest.csproj (Note: this file is very large and the most of the stuff is not necessary. You could create a much more simplified solution by following this guide: Walkthrough: Creating an MSBuild Project File from Scratch )

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
      <PropertyGroup>
        <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
        <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
        <ProjectGuid>{E3FBAE41-AF7E-4C7E-A69E-ADAEAEE76FA2}</ProjectGuid>
        <OutputType>Library</OutputType>
        <AppDesignerFolder>Properties</AppDesignerFolder>
        <RootNamespace>wpftest</RootNamespace>
        <AssemblyName>wpftest</AssemblyName>
        <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
        <FileAlignment>512</FileAlignment>
        <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
        <WarningLevel>4</WarningLevel>
      </PropertyGroup>
      <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
        <PlatformTarget>AnyCPU</PlatformTarget>
        <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' ">
        <PlatformTarget>AnyCPU</PlatformTarget>
        <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.Data" />
        <Reference Include="System.Xml" />
        <Reference Include="Microsoft.CSharp" />
        <Reference Include="System.Core" />
        <Reference Include="System.Xml.Linq" />
        <Reference Include="System.Data.DataSetExtensions" />
        <Reference Include="System.Xaml">
          <RequiredTargetFramework>4.0</RequiredTargetFramework>
        </Reference>
        <Reference Include="WindowsBase" />
        <Reference Include="PresentationCore" />
        <Reference Include="PresentationFramework" />
      </ItemGroup>
      <ItemGroup>
        <Page Include="MainWindow.xaml">
          <Generator>MSBuild:Compile</Generator>
          <SubType>Designer</SubType>
        </Page>
        <Compile Include="MainWindow.xaml.cs">
          <DependentUpon>MainWindow.xaml</DependentUpon>
          <SubType>Code</SubType>
        </Compile>
      </ItemGroup>
      <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
    </Project>
    

    Solution:

    The following code snippet will compile the code, created the dll file and use the created dll via reflection. This happens (all) during the application runtime.

    Special thanks to doug for this stackoverflow question

        private void TestCase4_OnClick(object sender, RoutedEventArgs e)
        {
            var globalProperties = new Dictionary<string, string>();
            var buildRequest = new BuildRequestData(@"C:\Users\jbu\wpftest\wpftest.csproj", globalProperties, null, new string[] { "Build" }, null);
            var pc = new ProjectCollection();
    
            var result = BuildManager.DefaultBuildManager.Build(new BuildParameters(pc), buildRequest);
    
            Assembly assembly = Assembly.LoadFrom(@"C:\Users\jbu\wpftest\bin\Debug\wpftest.dll");
            var instance = assembly.CreateInstance("wpftest.MainWindow") as Window;
    
            if (instance != null)
            {
                instance.Show();
            }
        }