Posted by: Zeeshan Amjad | November 28, 2012

Named PART idiom


We already saw an example of using PART_ template here and here. Now the question is if we want to use PART_ in our control then how can we do it.

Let’s first create a custom control. The whole point is to override OnApplyTemplate method. In this method we will check the PART name and then take appropriate action on it. Here is our simple example of OnApplyTemplate where we just change the foreground and background color of the control.

Code Snippet
public override void OnApplyTemplate()
{
    base.OnApplyTemplate();

    if (this.Template != null)
    {
        Control c = this.Template.FindName("PART_MyPart", this) as Control;

        if (c != null)
        {
            c.Background = Brushes.Blue;
            c.Foreground = Brushes.Yellow;
        }
    }
}

 

We also TemplatePart attribute to document this name.

Code Snippet
[TemplatePart(Name = "PART_MyPart", Type = typeof(Control))]
public class MyCustomControl : Control

 

Here is a complete code of our custom control.

Code Snippet
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace WpfNamedPart
{
    [TemplatePart(Name = "PART_MyPart", Type = typeof(Control))]
    public class MyCustomControl : Control
    {
        static MyCustomControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (this.Template != null)
            {
                Control c = this.Template.FindName("PART_MyPart", this) as Control;

                if (c != null)
                {
                    c.Background = Brushes.Blue;
                    c.Foreground = Brushes.Yellow;
                }
            }
        }
    }
}

 

Now let’s use this custom control. In main window we are going to create two custom controls in stack panel. One defined the named part and the other doesn’t. Here is XAML of our main window.

Code Snippet
<Window x:Class="WpfNamedPart.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:local="clr-namespace:WpfNamedPart"
        Title="Named Part Demo" Height="300" Width="400">
    <StackPanel>
        <local:MyCustomControl Margin="5">
            <local:MyCustomControl.Template>
                <ControlTemplate>
                    <Grid>
                        <Button x:Name="PART_MyPart" Width="150" Height="35" Content="Named Part defined"/>
                    </Grid>
                </ControlTemplate>
            </local:MyCustomControl.Template>
        </local:MyCustomControl>
        <local:MyCustomControl Margin="5">
            <local:MyCustomControl.Template>
                <ControlTemplate>
                    <Grid>
                        <Button Width="150" Height="35" Content="Named Part not defined"/>
                    </Grid>
                </ControlTemplate>
            </local:MyCustomControl.Template>
        </local:MyCustomControl>
    </StackPanel>
</Window>

 

Here is the output of this program.

NamedPartDemo

The output of the program clearly shows that template would apply if we specify the correct name of the control in control template.

Now there is a question, why not do the same thing in User control. We can define the same method in our user control class.

Code Snippet
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace WpfNamedPart
{
    /// <summary>
    /// Interaction logic for MyUserControl.xaml
    /// </summary>
    [TemplatePart(Name = "PART_MyPart", Type = typeof(Control))]
    public partial class MyUserControl : UserControl
    {
        public MyUserControl()
        {
            InitializeComponent();
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (this.Template != null)
            {
                Control c = this.Template.FindName("PART_MyPart", this) as Control;

                if (c != null)
                {
                    c.Background = Brushes.Blue;
                    c.Foreground = Brushes.Yellow;
                }
            }
        }
    }
}

 

But if we are going to use this control with named part then we will get an error message. Let’s add the user control in our stack panel in similar way we used the custom control.

Code Snippet
<local:MyUserControl>
    <local:MyUserControl.Template>
        <ControlTemplate>
            <Grid>
                <Button x:Name="PART_MyPart" Width="150" Height="35" Content="Named Part defined"/>
            </Grid>
        </ControlTemplate>
    </local:MyUserControl.Template>
</local:MyUserControl>

 

But this time we will get the compilation error.

error MC3093: Cannot set Name attribute value ‘PART_MyPart’ on element ‘Button’. ‘Button’ is under the scope of element ‘MyUserControl’, which already had a name registered when it was defined in another scope. Line 29 Position 33.


Leave a comment

Categories