3

This is my actual button style:

<Style x:Key="CategoryButtonStyle" TargetType="{x:Type Button}">
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Padding" Value="1"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <Grid x:Name="grid">
                    <Path x:Name="TabPath" StrokeThickness="2"
                          Margin="{Binding ElementName=buttonContent, Converter={x:Static c:ContentToMarginConverter.Value}}"
                          Stroke="{StaticResource BorderBrush1}"
                          Fill="{StaticResource TabItemPathBrush}">
                        <Path.Data>
                            <PathGeometry>
                                <PathFigure IsClosed="False" StartPoint="1,0" 
                                            Segments="{Binding ElementName=buttonContent, Converter={x:Static c:ContentToPathConverter.Value}}">
                                </PathFigure>
                            </PathGeometry>
                        </Path.Data>
                        <Path.LayoutTransform>
                            <!-- For some reason  -->
                            <ScaleTransform ScaleY="-1"/>
                        </Path.LayoutTransform>
                    </Path>
                    <Rectangle x:Name="TabItemTopBorder" Height="2" Visibility="Visible"
                               VerticalAlignment="Bottom" Fill="{StaticResource BorderBrush1}"
                               Margin="{Binding ElementName=TabPath, Path=Margin}" />
                    <ContentPresenter x:Name="buttonContent" Margin="10,2,10,2" VerticalAlignment="Center"
                                      TextElement.Foreground="{StaticResource ForegroundBrush}"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsPressed" Value="True">
                        <Setter Property="Fill" TargetName="TabPath">
                            <Setter.Value>
                                <SolidColorBrush Color="#FFe4f6fa"/>
                            </Setter.Value>
                        </Setter>
                        <Setter Property="BitmapEffect">
                            <Setter.Value>
                                <DropShadowBitmapEffect Direction="302" Opacity="0.4" 
                                                        ShadowDepth="2" Softness="0.5"/>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="False">
                        <Setter Property="Opacity" TargetName="grid" Value="0.25"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

and I need add code that I could set button to selected state which looks like when button is pressed. I was thinking about using VisualStateManager but I am not sure if it's good way and how can I do this. I've started with something like this:

<VisualStateManager.VisualStateGroups>
     <VisualState x:Name="Normal" />
     <VisualState x:Name="Selected">
        <Storyboard>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TabPath" 
                                   Storyboard.TargetProperty="Fill">
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="#FFe4f6fa" />
             </ObjectAnimationUsingKeyFrames>
         </Storyboard>
     </VisualState>
</VisualStateManager.VisualStateGroups>

But It's not working. I just don't know what to use in storyboard.

Edit - Almost working:

<VisualState x:Name="Selected">
     <Storyboard>
        <ColorAnimation Storyboard.TargetName="TabPath" Storyboard.TargetProperty="Fill.(LinearGradientBrush.GradientStops)[0].(GradientStop.Color)" To="#FFe4f6fa" Duration="0:0:0" />
        <ColorAnimation Storyboard.TargetName="TabPath" Storyboard.TargetProperty="Fill.(LinearGradientBrush.GradientStops)[1].(GradientStop.Color)" To="#FFa9cde7" Duration="0:0:0" />               
      </Storyboard>
 </VisualState>

Forget to add my brushes:

<LinearGradientBrush x:Key="TabItemSelectedPathBrush" StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FFe4f6fa" Offset="0"/>
    <GradientStop Color="#FFa9cde7" Offset="1"/>
</LinearGradientBrush>

<LinearGradientBrush x:Key="TabItemPathBrush" StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FFa9cde7" Offset="0"/>
    <GradientStop Color="#FF3164a5" Offset="1"/>
</LinearGradientBrush>
Libor Zapletal
  • 13,752
  • 20
  • 95
  • 182
  • Your requirement is not clear. There is no `Selected` visual state for button, You could use a `ToggleButton` if you want to show a Checked state. Or by `Selected`, do you mean `Focused` state? – Mat J Apr 28 '13 at 12:51
  • I mean that I can define some state and then change visual to this stat by something like this: `var bl = VisualStateManager.GoToState(selectedButton, "Activate", true);`. It really not depends on name of state (Selected, Activate, it doesn't matter). – Libor Zapletal Apr 29 '13 at 05:02

3 Answers3

5

Use ToggleButton that uses the same style and template.

In your style (the one in your original question) change TargetType on style and control template to ButtonBase.

In control template triggers add this trigger:

 <Trigger Property="ToggleButton.IsChecked" Value="True">
     <Setter Property="Fill" TargetName="TabPath">
         <Setter.Value>
             <SolidColorBrush Color="#FFe4f6fa"/>
         </Setter.Value>
     </Setter>
     <Setter Property="BitmapEffect">
         <Setter.Value>
             <DropShadowBitmapEffect Direction="302" Opacity="0.4" ShadowDepth="2" Softness="0.5"/>
         </Setter.Value>
     </Setter>
 </Trigger>

Now you can use this style for buttons, toggle buttons, radio buttons and check boxes.

XAMeLi
  • 6,189
  • 2
  • 22
  • 29
3

Personally I would use Blend for such task, it's really the companion to Visual Studio for developing WPF applications.

In a few clicks you would get to it, nothing prevents you to either use it or just copy the XAML it generated to your project.

Editing the button style

enter image description here

Triggers

enter image description here

Here are all your button states

enter image description here

Blend is like bread for butter, WPF is tastier with it, unless you prefer plain butter :-)

Note that if you are on VS2012 and working on a WPF project, Microsoft Blend + SketchFlow Preview for Visual Studio 2012 is the version that will allow you to edit WPF projects. The version bundled with VS2012 is only for Windows Store apps.

aybe
  • 15,516
  • 9
  • 57
  • 105
3

VisualStates should be declared on the root element of the ControlTemplate. With the following code,

        <Grid>
    <Grid.Resources>
        <SolidColorBrush x:Key="FillBrush" Color="Magenta" />
        <Color x:Key="StateBrush" >#a3bfb1</Color>
        <Style x:Key="CategoryButtonStyle" TargetType="{x:Type Button}">
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>

            <Setter Property="Padding" Value="1"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border Background="Green" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" x:Name="root">

                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Selected">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TabPath" 
                               Storyboard.TargetProperty="Fill.(SolidColorBrush.Color)">
                                                <DiscreteObjectKeyFrame KeyTime="0"  Value="{StaticResource StateBrush}">

                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Grid x:Name="grid">
                                <Path x:Name="TabPath" StrokeThickness="1" 

                      Stroke="Blue"
                      Fill="{StaticResource FillBrush}" Canvas.Left="16.75"
          Canvas.Top="14"
          Width="50"
          Height="40"
          Data="F1 M 26.75,24L 16.75,34L 23.5,34L 34,24L 23.5,14L 16.75,14L 26.75,24 Z ">
                                    <Path.LayoutTransform>
                                        <!-- For some reason  -->
                                        <ScaleTransform ScaleY="-1"/>
                                    </Path.LayoutTransform>
                                </Path>
                                <ContentPresenter x:Name="buttonContent" Margin="10,2,10,2" VerticalAlignment="Center"
                                  TextElement.Foreground="{TemplateBinding Foreground}"/>
                            </Grid>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    </Grid.Resources>
    <Button x:Name="test" Style="{StaticResource CategoryButtonStyle}" Content="Test"  VerticalAlignment="Center" Width="200px" BorderBrush="#888" BorderThickness="1px"/>
</Grid>

this code will change the state as intended.

VisualStateManager.GoToState(test, "TestState", true);

Make changes to the template as required, but make sure you put the VisualState declaration in the Root element of the ControlTemplate (Border in this example)

EDIT: I've updated the Selected VisualState's transition declaration with correct property path. Let me know if it solves your problem.

Mat J
  • 5,422
  • 6
  • 40
  • 56
  • This TestState is working but I can't get to work state which changes property Fill in Path. – Libor Zapletal Apr 29 '13 at 07:56
  • @Bibo, The path and value should be specified as per my edit. Check if it resolves the problem. – Mat J Apr 29 '13 at 08:40
  • thanks but I want to use that converters because it resize paths of the buttons accord to lenght of button content. Have you any solution that works with path like I haven in my question? – Libor Zapletal Apr 29 '13 at 09:10
  • Ignore the `Path`'s code, Just take note of `Storyboard.TargetProperty="Fill.(SolidColorBrush.Color)"` in the `VisualState` Transition, You can use your existing Markup of the Path. I just removed them for testing as I don't have the code for them. it should work fine as Converter is not for `Fill`. – Mat J Apr 29 '13 at 09:16
  • @Bibo Can you update the error you get with the converters in place, if any? – Mat J Apr 29 '13 at 10:38
  • There isn't any error. It just don't change color. I started with your code which works and I changed it to mine so I found where is problem. The problem is with `Fill` and that I use `StaticResources`. If I use just color then when changing state the color is changed too but when I have there `StaticResource` than it still same. Do you know solution for this? And one more problem. When I move with mouse out of button then the state is changed back to `Normal` state. Can I made it permanent until I change it to `Normal` state? – Libor Zapletal Apr 29 '13 at 19:30
  • @Bibo See my updated code. I'm not sure where you have the problem. it works as expected. Remove unwanted `VisualState` declarations to prevent button from automatically changing states. For better control, you'll need to derive your own control. – Mat J Apr 30 '13 at 05:12
  • I edited my post and I forget to show my brushes in resources. There are LinearGradient so that's why I have problem. Now I have that ColorAnimation and it's working but I have another problem. Now when I change state of one button it will change colors of all buttons. So what's wrong now? Why the buttons are changing? Maybe I still do this wrong way? How can I change LinearGradientBrush in button from one to another? – Libor Zapletal Apr 30 '13 at 05:56
  • :) That is because you are changing the color of the same brush which is shared by all the buttons. Move the brushes to `ControlTemplate.Resources` and you'll be fine. – Mat J Apr 30 '13 at 06:04
  • Oh, I am so sorry. I just give the bounty to wrong answer :/ You helped me very much, thank you and sorry. – Libor Zapletal May 02 '13 at 14:54
  • :) no problem about that. Accept the helpful answer so other users can spend less time finding solutions. – Mat J May 02 '13 at 15:01