Replace part of default template in WPF Replace part of default template in WPF wpf wpf

Replace part of default template in WPF


Unfortunately, I think you have to replace the entire template:

From MSDN: http://msdn.microsoft.com/en-us/library/aa970773.aspx

Controls in Windows Presentation Foundation (WPF) have a ControlTemplate that contains the visual tree of that control. You can change the structure and appearance of a control by modifying the ControlTemplate of that control. There is no way to replace only part of the visual tree of a control; to change the visual tree of a control you must set the Template property of the control to its new and complete ControlTemplate.


Actually there is a way (sort of). You can create your own custom control and override the OnApplyTemplate function to change the style dynamically.

For example, create a custom control like so (I am doing this in silverlight but it's all the same I presume):

namespace SilverlightClassLibrary1{    public class MyButton: Button    {        public string BackgroundColor { get; set; }        public override void OnApplyTemplate()        {            base.OnApplyTemplate();            if (BackgroundColor != null)            {                Rectangle r = this.GetTemplateChild("BackgroundGradient") as Rectangle;                if (r != null)                {                    r.Fill = new SolidColorBrush(Color.FromArgb(255,                         Convert.ToByte(BackgroundColor.Substring(1,2),16),                        Convert.ToByte(BackgroundColor.Substring(3,2),16),                        Convert.ToByte(BackgroundColor.Substring(5,2),16)));                }            }        }    }}

The interesting part is the GetTemplateChild method, that's looking for a Rectangle control named "BackgroundGradient". (BTW, it's easier if you define custom controls in a separate project, so create a new "Silverlight class library" project if you haven't already done so and put it into that project.)

Then add a new resource dictionary file and override the control template and make sure you have a rectangle named "BackgroundGradient". In this case we're using the standard button control template that I've cut down a bit:

<ResourceDictionary    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    xmlns:custom="clr-namespace:SilverlightClassLibrary1;assembly=SilverlightClassLibrary1">    <Style TargetType="custom:MyButton">        <Setter Property="Background" Value="#FF1F3B53"/>        <Setter Property="Foreground" Value="#FF000000"/>        <Setter Property="Padding" Value="3"/>        <Setter Property="BorderThickness" Value="1"/>        <Setter Property="BorderBrush">            <Setter.Value>                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">                    <GradientStop Color="#FFA3AEB9" Offset="0"/>                    <GradientStop Color="#FF8399A9" Offset="0.375"/>                    <GradientStop Color="#FF718597" Offset="0.375"/>                    <GradientStop Color="#FF617584" Offset="1"/>                </LinearGradientBrush>            </Setter.Value>        </Setter>        <Setter Property="Template">            <Setter.Value>                <ControlTemplate TargetType="Button">                    <Grid>                        <Border x:Name="Background" CornerRadius="3" Background="White" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">                            <Grid Background="{TemplateBinding Background}"  Margin="1">                                <Border Opacity="0"  x:Name="BackgroundAnimation" Background="#FF448DCA" />                                <Rectangle x:Name="BackgroundGradient" Fill="White" >                                </Rectangle>                            </Grid>                        </Border>                        <ContentPresenter                              x:Name="contentPresenter"                              Content="{TemplateBinding Content}"                              ContentTemplate="{TemplateBinding ContentTemplate}"                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"                              Margin="{TemplateBinding Padding}"/>                        <Rectangle x:Name="DisabledVisualElement" RadiusX="3" RadiusY="3" Fill="#FFFFFFFF" Opacity="0" IsHitTestVisible="false" />                        <Rectangle x:Name="FocusVisualElement" RadiusX="2" RadiusY="2" Margin="1" Stroke="#FF6DBDD1" StrokeThickness="1" Opacity="0" IsHitTestVisible="false" />                    </Grid>                </ControlTemplate>            </Setter.Value>        </Setter>    </Style></ResourceDictionary>

So you can now declare a button control and override a style if you'd like:

<UserControl x:Class="SilverlightApplication1.MainPage"            ...            xmlns:custom="clr-namespace:SilverlightClassLibrary1;assembly=SilverlightClassLibrary1">        <custom:MyButton>Normal Button 1</custom:MyButton>        <custom:MyButton>Normal Button 2</custom:MyButton>        <custom:MyButton BackgroundColor="#8888cc">Customized Background</custom:MyButton>

I presume you could get get even more clever and pass through a resource name or a style name and load it dynamically.

You then need to include your resource file as part of your application:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"              x:Class="SilverlightApplication1.App"             >    <Application.Resources>        <ResourceDictionary >            <ResourceDictionary.MergedDictionaries>                <ResourceDictionary Source="Dictionary1.xaml" />            </ResourceDictionary.MergedDictionaries>        </ResourceDictionary>    </Application.Resources></Application>

and you'll see your custom property changes in your XAML designer.


I would redesign your template to be a Custom Control, using ContentPresenters as placeholders for the elements that will change.

To use these placeholders you must link them to dependency properties.

Check this post to see how this is done

http://www.codeproject.com/Articles/82464/How-to-Embed-Arbitrary-Content-in-a-WPF-Control

"Using Custom Control" explains my approach