3

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?

Joel
  • 4,862
  • 7
  • 46
  • 71

3 Answers3

5

XamlReader class is intened for this purpose. Use its Load method to load the xaml dynamically.

EDIT - This link here might be of your interest.

Rohit Vats
  • 79,502
  • 12
  • 161
  • 185
  • As far as i know XamlReader won't work for event handler (Click="TestCase1_OnClick") – Joel Feb 21 '13 at 13:22
  • 2
    Yeah, you can't create handlers with it since it's not `loose XAML` anymore. Only loose XAML can be loaded with it. – Rohit Vats Feb 21 '13 at 13:33
  • Why do you want to load XAML from the Database. If you explain what you are trying to achieve maybe an alternate implementation can be suggested. For example I would rather have templates built internally , and then get parameters from DB than storing Xaml in the DB. – TYY Feb 21 '13 at 13:33
  • 1
    @Joel - I think you should redesign your code. But however if you want to stick to it, you can use `CodeDom` to create handlers dynamically. Link here http://elegantcode.com/2010/12/21/wpf-dynamically-compile-and-run-event-handlers-within-loose-xaml-using-codedom/ might be of your interest. – Rohit Vats Feb 21 '13 at 13:35
  • There is no Database, i just created a case to point out the problem. The real problem is much more complicated. But well, lets give it a shot here is what i try to achieve: I receive an user interface defined in an XML File and a corresponding c# code behind file from a webservice. The XML file must be parsed to XAML. Then i have to display the generated XAML file somehow. Basically i have to create a user interface out of an xml file at runtime. – Joel Feb 21 '13 at 13:52
  • this might be helpful, i will post some demo code later. http://msdn.microsoft.com/en-us/library/aa970678.aspx – Joel Feb 22 '13 at 08:02
3

This is an old question, but I want to suggest a better way without compiling in runtime.

  1. Replacing the "click" attribute with "Command". Implement your Relay command or other third party libraries ( Eg: MVVM Light toolkit ) . Also ICommand MVVM implementation would help.
  2. Remove x:Class attribute from xaml of Window node.

Now you have a loose xaml and task could be done by XamlReader.

Louis Go
  • 2,213
  • 2
  • 16
  • 29
1

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();
        }
    } 
Community
  • 1
  • 1
Joel
  • 4,862
  • 7
  • 46
  • 71