Edward Tanguay Edward Tanguay - 7 days ago 6
C# Question

What is the best way to reuse blocks of XAML?

I've got many user controls like this:

PageManageCustomers.xaml.cs:

public partial class PageManageCustomers : BasePage
{
...
}


which inherit from:

PageBase.cs:

public class BasePage : UserControl, INotifyPropertyChanged
{
...
}


Since PageBase.cs has no accompanying XAML file, I have to put the XAML that it refers to in each of the user controls which inherit it, e.g. the following block is repeated in every XAML file of every control that inherits
PageBase
:

<DataTemplate x:Key="manageAreaCellTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{DynamicResource ManageLinkStyle}"
Tag="{Binding Id}" Text="Delete" MouseDown="System_Delete_Click"/>
<TextBlock Text=" "/>
<TextBlock Style="{DynamicResource ManageLinkStyle}"
Tag="{Binding Id}" Text="Edit" MouseDown="System_Edit_Click"/>
</StackPanel>
</DataTemplate>


I'm trying to put this block into a resource file but can't get the syntax right, it says:


'ResourceDictionary' root element
requires a x:Class attribute to
support event handlers in the XAML
file. Either remove the event handler
for the MouseDown event, or add a
x:Class attribute to the root element.


Or perhaps I could read these blocks in with XamlReader` somehow?

How can I put this repeated block of code in one place so that it is not repeated in every XAML file that inherits BagePage?

Here is a reproducable example of this problem:



Window1.xaml:

<Window x:Class="TestXamlPage8283.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel x:Name="MainContent"/>
</Window>


Window1.xaml.cs:

using System.Windows;

namespace TestXamlPage8283
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();

Page1 page1 = new Page1();
MainContent.Children.Add(page1);

Page2 page2 = new Page2();
MainContent.Children.Add(page2);
}
}
}


Page1.xaml:

<local:BasePage x:Class="TestXamlPage8283.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestXamlPage8283"
Height="40" Width="300">
<StackPanel>
<TextBlock Text="{Binding PageTitle}"
FontSize="14"
FontWeight="Bold"/>
<TextBlock Text="This is XAML that is specific to page one." />
</StackPanel>
</local:BasePage>


Page1.xaml.cs:

namespace TestXamlPage8283
{
public partial class Page1 : BasePage
{
public Page1()
{
InitializeComponent();
PageTitle = "Page One";
}
}
}


Page2.xaml:

<local:BasePage x:Class="TestXamlPage8283.Page2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestXamlPage8283"
Height="40" Width="300">
<StackPanel>
<TextBlock Text="{Binding PageTitle}"
FontSize="14"
FontWeight="Bold"/>
<TextBlock Text="This is XAML that is specific to page two." />
</StackPanel>
</local:BasePage>


Page2.xaml.cs:

namespace TestXamlPage8283
{
public partial class Page2 : BasePage
{
public Page2()
{
InitializeComponent();
PageTitle = "Page Two";
}
}
}


BasePage.cs:

using System.Windows.Controls;
using System.ComponentModel;

namespace TestXamlPage8283
{
public class BasePage : UserControl, INotifyPropertyChanged
{
#region ViewModelProperty: PageTitle
private string _pageTitle;
public string PageTitle
{
get
{
return _pageTitle;
}

set
{
_pageTitle = value;
OnPropertyChanged("PageTitle");
}
}
#endregion

public BasePage()
{
DataContext = this;
}

#region INotifiedProperty Block
public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;

if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}


How do I take this block

<TextBlock Text="{Binding PageTitle}"
FontSize="14"
FontWeight="Bold"/>


out of Page1.xaml and Page2.xaml and put it in one place so I can refer to it from Page1.xaml and Page2.xaml? (so that when I want to change FontSize=14 to FontSize=16, I just have to change it in one place)

Answer

Use resource dictionaries - add a MyDictionary.xaml file to your project, setting its Build Action to "Page":

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <DataTemplate x:Key="manageAreaCellTemplate">
        <StackPanel Orientation="Horizontal">
            <TextBlock Style="{DynamicResource ManageLinkStyle}"
              Tag="{Binding Id}" Text="Delete" MouseDown="System_Delete_Click"/>
            <TextBlock Text=" "/>
            <TextBlock Style="{DynamicResource ManageLinkStyle}"
              Tag="{Binding Id}" Text="Edit" MouseDown="System_Edit_Click"/>
        </StackPanel>
    </DataTemplate>
</ResourceDictionary>

Then in other XAML files you refer to it with:

<ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="MyDictionary.xaml"/>
    </ResourceDictionary.MergedDictionaries>
    ... some other local resources ...
</ResourceDictionary>

and refer to your resource as Template={StaticResource manageAreaCellTemplate}.

Comments