StepUp
StepUp

Reputation: 38209

How to change templates after clicking Button

I would like to change view of button after button is clicked. For example, I have two templates beforeClicking and afterClicking:

<Style TargetType="Button">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button" x:Name="beforeClicking">
                <Border Name="border" Background="Transparent" BorderThickness="1" BorderBrush="Black">
                    <ContentPresenter/>
                </Border>                    
            </ControlTemplate>
            <!--It is not permitted, but that is pseudocode(what I want)
             <ControlTemplate TargetType="Button" x:Name="afterClicking">
                <Border Name="border" Background="Transparent" BorderThickness="10" BorderBrush="Black">
                    <ContentPresenter/>
                </Border>                    
            </ControlTemplate>
            -->
        </Setter.Value>
    </Setter>
</Style>

What I want is when button is not clicked, then a template beforeClicking should be permanently used and nothing(template or view) should not be altered. However, if user clicks, then button should use a template afterClicking permanently and the view of button should not be altered.

That is at the first click the button uses template afterClicking. At the second click the button uses template beforeClicking. At the third click the button uses template afterClicking. At the fourth click the button uses template afterClicking. And so on.

I would like to get such behavior just by XAML since MVVM approach is used.

How to achieve such a behavior?

Upvotes: 1

Views: 1292

Answers (2)

Mattia Magosso
Mattia Magosso

Reputation: 533

If I were you I would use System.Windows.Interactivity.Behavior rather than EventTriggers, unfortunately EventTriggers are the only way for you to associate in xaml event with operation, the only problem is that EventTriggers only work with TriggerAction objects and you can't create your TriggerAction. This because it is an abstract class which abstract methods are internal and not overridable from outside PresentationFramework library. Here is an example of how prepare your Behavior class:

public class ChangeTemplateBehavior : System.Windows.Interactivity.Behavior<Button>
{
    public static readonly DependencyProperty ControlTemplate1Property = DependencyProperty.Register("ControlTemplate1", typeof(ControlTemplate), typeof(ChangeTemplateBehavior), new PropertyMetadata(default(ControlTemplate)));

    public ControlTemplate ControlTemplate1
    {
        get
        {
            return (ControlTemplate)GetValue(ControlTemplate1Property);
        }
        set
        {
            SetValue(ControlTemplate1Property, value);
        }
    }

    public static readonly DependencyProperty ControlTemplate2Property = DependencyProperty.Register("ControlTemplate2", typeof(ControlTemplate), typeof(ChangeTemplateBehavior), new PropertyMetadata(default(ControlTemplate)));

    public ControlTemplate ControlTemplate2
    {
        get
        {
            return (ControlTemplate)GetValue(ControlTemplate2Property);
        }
        set
        {
            SetValue(ControlTemplate2Property, value);
        }
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.Click += AssociatedObject_Click;
    }

    void AssociatedObject_Click(object sender, RoutedEventArgs e)
    {
        if (this.AssociatedObject.Template == this.ControlTemplate2)
        {
            this.AssociatedObject.Template = this.ControlTemplate1;
        }
        else
        {
            this.AssociatedObject.Template = this.ControlTemplate2;
        }
    }
}

And here is an example of how to write your xaml code:

<Window x:Class="StackoverflowHelpWPF5.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:[YOURLOCALNAMESPACEHERE]"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>

        <ControlTemplate TargetType="Button" x:Key="beforeClicking">
            <Border Name="border" Background="Transparent" BorderThickness="1" BorderBrush="Black">
                <ContentPresenter/>
            </Border>
        </ControlTemplate>

        <ControlTemplate TargetType="Button" x:Key="afterClicking">
            <Border Name="border" Background="Transparent" BorderThickness="10" BorderBrush="Black">
                <ContentPresenter/>
            </Border>                    
        </ControlTemplate>

    </Window.Resources>

    ...

    <Button  Template="{StaticResource beforeClicking}" >
            <i:Interaction.Behaviors>
                <local:ChangeTemplateBehavior ControlTemplate1="{StaticResource afterClicking}" ControlTemplate2="{StaticResource beforeClicking}"></local:ChangeTemplateBehavior>
            </i:Interaction.Behaviors>
    </Button>

Upvotes: 1

James Willock
James Willock

Reputation: 2037

You can handle a click using an EventTrigger. In an event trigger you provide a Storyboard, but in a Storyboard you can't change the Template.

You could do this in an attached property, but looking at your example, you don't need to, and more typically, you'd affect individual properties:

<Style TargetType="Button">
    <Setter Property="BorderThickness" Value="1" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border Name="border" Background="Transparent" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="Black">
                    <ContentPresenter/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <EventTrigger RoutedEvent="Click">
            <EventTrigger.Actions>
                <BeginStoryboard>
                    <Storyboard>
                        <ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="BorderThickness">
                            <DiscreteThicknessKeyFrame Value="10" KeyTime="0" />
                        </ThicknessAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger.Actions>
        </EventTrigger>
    </Style.Triggers>
</Style>

==After discussion: ToggleButton version:==

<Window.Resources>
    <Style TargetType="ToggleButton">
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ToggleButton">
                    <Border Name="border" Background="Transparent" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="Black">
                        <ContentPresenter/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="IsChecked" Value="True">
                <Setter Property="BorderThickness" Value="10" />
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>
<Grid>
    <ToggleButton>Hello World</ToggleButton>
</Grid>

Upvotes: 1

Related Questions