Click here to Skip to main content
15,942,710 members
Please Sign up or sign in to vote.
5.00/5 (2 votes)
See more:
Hi. I would like to load an external XAML file (including the code behind) during runtime. I was able to load the content of the XAML file:
C#
try
{
    FileStream Fs = new FileStream(@"C:/XAML Files/ucMain.xaml", FileMode.Open);
    Page grdToLoad = new Page();
    grdToLoad.Height = 210;
    grdToLoad.Width = 400;
    grdToLoad = System.Windows.Markup.XamlReader.Load(Fs) as Page;
    gridMain.Children.Add(grdToLoad);
    Fs.Close();
}
catch (Exception ex)
{
    MessageBox.Show(ex.Message);
}

This works perfect, but I can't seem to load the code behind. I have tried using x:Code, but I keep on getting an error:
"Failed to create a 'Click' from the text 'Clicked'.' Line number '4' and line position '26'."

The XAML file:
XML
<Grid
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <Button Name="butMain" Click="Clicked">Click Me!</Button>
  <x:Code><![CDATA[
    void Clicked(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Hello World!");
    }
  </x:Code>
</Grid>


If I remove the x:Code and the Click event, everything seems to work fine. Would it be possible to load the .cs file during runtime instead of using x:Code? If not, how would I be able to fix this error?

Thanks in advance.
Posted
Updated 1-Jan-17 9:27am
Comments
Snark Boojum 25-May-24 14:29pm    
Although solutions 1&2 is better proposals for architecture, the original question was about dynamic loading of XAML with integrated in CDATA code-behind.
Regardless of missing closing tag for the CDATA section, this XAML of yours will NEVER work because of nature of CDATA section. It({x:Code} with CDATA section) require specificaition of automatically generated class at the root tag. Only msbuild can create and compile you code from CDATA into real class and only after that XAML can be loaded successfully and binded to already existing method of that class.
In you case XamlRead begin to load your XAML and when it get to {Button Click="Clicked"} tag, it tries to bind button's event "Click" to not existing on root object(in your case - Grid) method "Clicked". Therefore it throws exception that contains another inner exception about incompatibility of signature of delegate(i.e. method "Clicked" not found).
So, forget your madness or research dynamic compilation from source code.

Why don't you try creating a UserControl that contains your xaml. That way, you get all the benefits of having the code behind and can instantiate the code in your calling method:

C#
this.gridMain.Children.Add(new MyUserControl());


That way, you maintain proper compartmentalization, and can hook the button click event either in your user control, or in the parent window/control.
 
Share this answer
 
v2
XML
<Grid
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <Button Name="butMain" Click="Clicked">Click Me!</Button>
  <x:Code><![CDATA[
    void Clicked(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Hello World!");
    }
  ]]> <!-- Forgot the closing tag -->
  </x:Code>
</Grid>

You forgot the closing tag for the CDATA section. I'm guessing from your example you're using a site-of-origin file but I'd highly recommend re-thinking that if this is your XAML file and not something intrinsically external. You can set the Build Action to "Page" which will compile the XAML into a BAML file embedded in the assembly. Not only does it reduce the number of individual files in your release but also allows easy loading with:
C#
Page page = Application.LoadComponent(new Uri("/Page1.xaml", UriKind.Relative)) as Page;
if (page != null)
  gridMain.Children.Add(page);

This will work just fine but you will be warned that a Page isn't intended to be the child of anything other than a Window or Frame. That could be easily remedied by just wrapping the loaded Page in a Frame and adding that as a child to the Grid.
Example:
C#
Uri uri = new Uri("/Page1.xaml", UriKind.Relative);
Page page = Application.LoadComponent(uri) as Page;
Frame frame = new Frame();
frame.Content = page;
//Or frame.Source = uri; This lets WPF control the loading for you.
gridMain.Children.Add(frame);


My full working code:
XML
<!-- MainWindow.xaml -->
<Window x:Class="Testing_xClass_and_xCode.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Testing_xClass_and_xCode"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid Name="gridMain" />
</Window>
<!-- Page1.xaml -->
<Page x:Class="Testing_xClass_and_xCode.Page1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:Testing_xClass_and_xCode"
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"
      Title="Page1">
    <Grid
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
        <Button Name="butMain" Click="Clicked">Click Me!</Button>
        <x:Code>
            <![CDATA[
            void Clicked(object sender, RoutedEventArgs e)
            {
                MessageBox.Show("Hello World!");
            }]]>
        </x:Code>
    </Grid>
</Page>


C#
//MainWindow.xaml.cs
namespace Testing_xClass_and_xCode
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            try
            {
                Page page = Application.LoadComponent(new Uri("/Page1.xaml", UriKind.Relative)) as Page;
                if (page != null)
                    gridMain.Children.Add(page);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }
}


EDIT: A short note: As John Simmons pointed out, you should consider making this a UserControl instead. A Page is designed to be navigable. All you're doing is encapsulating controls which is why UserControl exists. That being said, WPF is very flexible and will let you do most anything.
 
Share this answer
 
v4

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900