Is it possible to use XamlReader from a XAML file to load in a block of XAML text?

2.8k views Asked by At

I use the following DataTemplate in many controls:

<pages:BasePageManageItems x:Class="TestApp.Pages.PageManageAddresses"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:pages="clr-namespace:TestHistorierung.Pages"
    xmlns:tk="http://schemas.microsoft.com/wpf/2008/toolkit"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             Background="#eee"
             VerticalAlignment="Stretch">
    <pages:BasePageManageItems.Resources>
        <DataTemplate x:Key="manageAreaCellTemplate">
            <Border Padding="2">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Style="{DynamicResource ManageLinkStyle}"
                    Tag="{Binding Id}" Text="Delete" MouseDown="System_Delete_Click"
                    Margin="0 0 5 0"/>
                    <TextBlock Style="{DynamicResource ManageLinkStyle}"
                   Tag="{Binding Id}" Text="Edit" MouseDown="System_Edit_Click"
                    Margin="0 0 5 0"/>
                    <TextBlock Style="{DynamicResource ManageLinkStyle}"
                   Tag="{Binding Id}" Text="Add" MouseDown="System_Add_Click"
                    Margin="0 0 5 0"/>
                    <TextBlock Style="{DynamicResource ManageLinkStyle}"
                   Tag="{Binding Id}" Text="Copy" MouseDown="System_Copy_Click"
                    Margin="0 0 5 0"/>
                </StackPanel>
            </Border>
        </DataTemplate>
    </pages:BasePageManageItems.Resources>

Is there any way to use XamlReader from XAML so that I can simply load the text of the DataTemplate into the XAML file dynamically? I'm imagining something like this (pseudo code):

<pages:BasePageManageItems x:Class="TestApp.Pages.PageManageAddresses"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:pages="clr-namespace:TestHistorierung.Pages"
    xmlns:tk="http://schemas.microsoft.com/wpf/2008/toolkit"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             Background="#eee"
             VerticalAlignment="Stretch">
    <pages:BasePageManageItems.Resources>
        <XamlReader Load="XamlBlocks/DateTemplateManageButtons.xaml"/>
    </pages:BasePageManageItems.Resources>
2

There are 2 answers

0
PanJanek On

You shouldn't place the XamlReader tag in Xaml (I don't even know if it's possible). Instead you can use XamlReader class to create compiled Xaml in code, and attach it to parent element:

 var element = XamlReader.Load(stringContainingXaml);
 this.somePanel.Children.Insert(0, element as FrameworkElement);
2
Nir On

You can put the common XAML in a ResourceDictionary:

XamlBlocks/DateTemplateManageButtons.xaml (added to the project, build action = Page)

<ResourceDictionary x:Class="myNmaespace.DateTemplateManageButtons"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
     <DataTemplate x:Key="manageAreaCellTemplate">
            <Border Padding="2">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Style="{DynamicResource ManageLinkStyle}"
                    Tag="{Binding Id}" Text="Delete" MouseDown="System_Delete_Click"
                    Margin="0 0 5 0"/>
                    <TextBlock Style="{DynamicResource ManageLinkStyle}"
                   Tag="{Binding Id}" Text="Edit" MouseDown="System_Edit_Click"
                    Margin="0 0 5 0"/>
                    <TextBlock Style="{DynamicResource ManageLinkStyle}"
                   Tag="{Binding Id}" Text="Add" MouseDown="System_Add_Click"
                    Margin="0 0 5 0"/>
                    <TextBlock Style="{DynamicResource ManageLinkStyle}"
                   Tag="{Binding Id}" Text="Copy" MouseDown="System_Copy_Click"
                    Margin="0 0 5 0"/>
                </StackPanel>
            </Border>
        </DataTemplate>
    </ResourceDictionary>

XamlBlocks/DateTemplateManageButtons.xaml.cs :

namespace myNamespace
{
    public partial class DateTemplateManageButtons : ResourceDictionary
    {
        private void System_Delete_Click(object sender, RoutedEventArgs e)
        {
            // event handler code
        }
        // other event handlers
    }
}

And in your page:

<pages:BasePageManageItems x:Class="TestApp.Pages.PageManageAddresses"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:pages="clr-namespace:TestHistorierung.Pages"
    xmlns:tk="http://schemas.microsoft.com/wpf/2008/toolkit"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             Background="#eee"
             VerticalAlignment="Stretch">
    <pages:BasePageManageItems.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="XamlBlocks/DateTemplateManageButtons.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </pages:BasePageManageItems.Resources>

If you need the event handler code to run in the page and not the resource dictionary you can do something like:

Define interface for the events:

public interface IDateTemplateManageButtonsEvents 
{
    void System_Delete_Click(object sender, RoutedEventArgs e); 
}

Implement that interface in all pages that use the data template

In the resource dictionary cs file:

private IDateTemplateManageButtonsEvents FindPage(object sender)
{
    DependencyObject current = sender as DependencyObject;
    while(current != null && !(current is IDateTemplateManageButtonsEvents))
    {
        current = VisualTreeHelper.GetParent(current);
    }
    return (IDateTemplateManageButtonsEvents)current;
}
private void System_Delete_Click(object sender, RoutedEventArgs e)
{
    FindPage(sender).System_Delete_Click(sender, e);
}