Almog G.
Almog G.

Reputation: 817

How to Use same template for similar buttons

I'm quite new to WPF. Below is what I tried to do in order to use the same controlTemplate to buttons that the only difference between them is the PathGeometry value.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Shared.xaml" />
    </ResourceDictionary.MergedDictionaries>

    <Style TargetType="Button" x:Key="buttonHeader">
        <Setter Property="Width" Value="18" />
        <Setter Property="Height" Value="18" />
        <Setter Property="Cursor" Value="Hand" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border Name="BorderStyle" Background="Transparent" >
                        <Path 
                            x:Name="CheckMark"
                            HorizontalAlignment="Center"
                            VerticalAlignment="Bottom"
                            SnapsToDevicePixels="False" 
                            Stroke="#FF4D4D4D"
                            StrokeThickness="2" StrokeEndLineCap="Flat" StrokeStartLineCap="Flat"
                            Data="{DynamicResource geoPath}">
                        </Path>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter TargetName="BorderStyle" Property="Background" Value="#B2FFFFFF" />
                            <Setter TargetName="CheckMark" Property="Stroke" Value="#D8727272" />
                        </Trigger>
                        <Trigger Property="IsPressed" Value="true">
                            <Setter TargetName="BorderStyle" Property="Background" Value="#B2707070" />
                            <Setter TargetName="CheckMark" Property="Stroke" Value="#D8FFFFFF" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <PathGeometry x:Key="X_Sign">
        <PathFigure StartPoint="0,0">
            <LineSegment Point="10,10"/>
        </PathFigure>
        <PathFigure StartPoint="0,10">
            <LineSegment Point="10,0"/>
        </PathFigure>
    </PathGeometry>

    <PathGeometry x:Key="Min_Sign">
        <PathFigure StartPoint="0,0">
            <LineSegment Point="10,0"/>
        </PathFigure>
    </PathGeometry>

    <Style x:Key="ButtonX" BasedOn="{StaticResource buttonHeader}" TargetType="Button">
        <Style.Resources>
            <StaticResource x:Key="geoPath" ResourceKey="X_Sign"/>
        </Style.Resources>
    </Style>
    <Style x:Key="ButtonXMinimize" BasedOn="{StaticResource buttonHeader}" TargetType="Button">
        <Style.Resources>
            <StaticResource x:Key="geoPath" ResourceKey="Min_Sign"/>
        </Style.Resources>
    </Style>
</ResourceDictionary>

In the designer I actually get exactly what I want, but when I try to run the application i get a XamlParseException and the innerException is:

Unable to cast object of type 'System.Windows.Media.PathGeometry' to type 'System.Windows.ResourceDictionary'

What am I missing and how can I fix it? Also, I'll be happy to know if there is a better way to do it.

Thanks in advance.

Upvotes: 1

Views: 380

Answers (2)

nmclean
nmclean

Reputation: 7734

Although passing a resource directly through StaticResource doesn't work reliably, XAML elements can usually be broken down further into reusable parts. If you look at the PathGeometry class declaration, you'll notice it has:

[ContentPropertyAttribute("Figures")]

which means the Figures property is what actually gets set when you nest a child in XAML. The type of this property is PathFigureCollection, which your PathFigure elements are being added to. This means you could store the PathFigureCollection itself as a resource:

<PathFigureCollection x:Key="XSignFigures">
    <PathFigure StartPoint="0,0">
        <LineSegment Point="10,10"/>
    </PathFigure>
    <PathFigure StartPoint="0,10">
        <LineSegment Point="10,0"/>
    </PathFigure>
</PathFigureCollection>

and then apply it to a PathGeometry when you need it:

<Style x:Key="ButtonX" BasedOn="{StaticResource buttonHeader}" TargetType="Button">
    <Style.Resources>
        <PathGeometry x:Key="geoPath" Figures="{StaticResource ResourceKey=XSignFigures}" />
    </Style.Resources>
</Style>

More info: http://msdn.microsoft.com/en-us/library/ms788723.aspx#collection_syntax

Upvotes: 1

Sphinxxx
Sphinxxx

Reputation: 13047

You can't use a StaticResource as a "proxy" for an actual resource (here: PathGeometry) like that.

What you can do is move each PathGeometry into its corresponding <Style.Resources> section. Thus you won't have any PathGeometries called "X_Sign" or "Min_Sign" - just two that are called "geoPath":

...

<Style x:Key="ButtonX" BasedOn="{StaticResource buttonHeader}" TargetType="Button">
    <Style.Resources>
        <!--<StaticResource x:Key="geoPath" ResourceKey="X_Sign"/>-->
        <PathGeometry x:Key="geoPath">
            <PathFigure StartPoint="0,0">
                <LineSegment Point="10,10"/>
            </PathFigure>
            <PathFigure StartPoint="0,10">
                <LineSegment Point="10,0"/>
            </PathFigure>
        </PathGeometry>
    </Style.Resources>
</Style>
<Style x:Key="ButtonXMinimize" BasedOn="{StaticResource buttonHeader}" TargetType="Button">
    <Style.Resources>
        <ResourceDictionary>
            <!--<StaticResource x:Key="geoPath" ResourceKey="Min_Sign"/>-->
            <PathGeometry x:Key="geoPath">
                <PathFigure StartPoint="0,0">
                    <LineSegment Point="10,0"/>
                </PathFigure>
            </PathGeometry>
        </ResourceDictionary>
    </Style.Resources>
</Style>

Upvotes: 0

Related Questions