Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to reuse blocks of XAML?

Tags:

c#

wpf

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)

like image 218
Edward Tanguay Avatar asked Nov 26 '09 12:11

Edward Tanguay


People also ask

How do I reload my XAML page?

Solution 1DataContext = null; page. DataContext = dataModel; To re-render a page, try the solution at Update the WPF UI now: how to wait for the rendering to finish ?[^].

Why do we use XAML in WPF?

The goal of XAML is to enable visual designers to create user interface elements directly. WPF aims to make it possible to control all visual aspects of the user interface from mark-up.

Why is XAML used?

XAML is the language to build user interfaces for Windows and Mobile applications that use Windows Presentation Foundation (WPF), UWP, and Xamarin Forms. The purpose of XAML is simple, to create user interfaces using a markup language that looks like XML.


1 Answers

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}.

like image 167
Mart Avatar answered Nov 07 '22 06:11

Mart