How does one animate path data in wpf? We have a series of templated silverlight-5 controls which change this property; these controls need to be adapted to work in wpf. In wpf, when the VSM tries to change the state, it crashes with the following exception:
Cannot animate the 'Data' property on a 'System.Windows.Shapes.Path' using a 'System.Windows.Media.Animation.ObjectAnimationUsingKeyFrames'
Inner exception:
The animation(s) applied to the 'Data' property calculate a current value of [XXX-The path data-] , which is not a valid value for the property.
There is an example below for a button-control: a path which goes from a circle to a star- how can this be implimented in wpf?
<Style x:Key="ButtonStyle1" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="MouseOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Path.Data)" Storyboard.TargetName="path">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>M 291.42858 512.36218 216.73569 387.62221 74.321018 416.8994 169.87441 307.31546 98.0216 180.91821 l 133.74814 57.01338 98.00719 -107.39498 -12.89251 144.82014 132.42459 60.0235 -141.71614 32.49039 z</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Viewbox>
<Path x:Name="path" Stroke="Black" Fill="Black" UseLayoutRounding="False" Data="m 357.14285 425.21933 c 0 71.79702 -58.20298 130 -130 130 -71.79701 0 -129.999997 -58.20298 -129.999997 -130 0 -71.79702 58.202987 -130 129.999997 -130 71.79702 0 130 58.20298 130 130 z"/>
</Viewbox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
No point in using element syntax if you do not create an object instance, it's still a string and the Data is Geometry, just wrap it in said tag and it should work.
<DiscreteObjectKeyFrame.Value>
<Geometry>
M 291.42858 512.36218 216.73569 387.62221 74.321018 416.8994 169.87441 307.31546 98.0216 180.91821 l 133.74814 57.01338 98.00719 -107.39498 -12.89251 144.82014 132.42459 60.0235 -141.71614 32.49039 z
</Geometry>
</DiscreteObjectKeyFrame.Value>
Related
I am a beginner in WPF and have been trying to come up with a custom style template for a ToggleButton. I've so far managed to define the paths with bezier curves, but got stuck into the following issue:
How can I combine the two Paths, such that when I hover the ToggleButton, the color in BOTH PATHS is changed according to the attached image. There need to be two distinct color values, one for the main part and the other for the blue bar on the top. Here is a part of my canvas in XAML:
<Canvas Width="180" Height="180" Canvas.Left="0" Canvas.Top="0" HorizontalAlignment="Center" VerticalAlignment="Top">
<Path Stretch="Fill" StrokeLineJoin="Miter" Stroke="#FFB5BECB" Fill="#FFEDF2F7" Data="F1 M 90,18C 109.882,18 127.882,26.0589 140.912,39.0883L 152.225,27.7746C 136.301,11.8497 114.301,2.00002 90,2.00002L 90,18 Z"/>
<Path Stretch="Fill" StrokeLineJoin="Miter" Stroke="#FFB5BECB" Fill="#FFCDD5DE" Data="F1 M 90,18C 109.882,18 127.882,26.0589 140.912,39.0883L 152.225,27.7746C 136.301,11.8497 114.301,2.00002 90,2.00002L 90,18 Z "/>
<Path Stretch="Fill" StrokeThickness="1" StrokeLineJoin="Miter" Stroke="#FF6F9FFF" Fill="#FF6F9FFF" Data="F1 M 89.9999,5.00005C 113.472,5.00005 134.722,14.514 150.104,29.896L 152.225,27.7746C 136.301,11.8497 114.301,2.00002 90,2.00002L 89.9999,5.00005 Z "/>
<Path Stretch="Fill" StrokeThickness="1" StrokeLineJoin="Miter" Stroke="#FF13487E" Fill="#FF13487E" Data="F1 M 89.9999,5.00005C 113.472,5.00005 134.722,14.514 150.104,29.896L 152.225,27.7746C 136.301,11.8497 114.301,2.00002 90,2.00002L 89.9999,5.00005 Z"/>
</Canvas>
The following image shows the expected primary and the hover states.
You can name your elements by assigning an x:Name. Then you can refer to them in a Trigger that checks the IsMouseOver property on a parent container, e.g. the Canvas.
Since you do not provide your full control template, this one is the part that you posted, adapted to change the Strokes and Fills of the Paths on mouse over.
<ToggleButton Content="Templated Toggle Button">
<ToggleButton.Template>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<!-- ...here might be a container that you did not provide in your question. -->
<Canvas x:Name="MyCanvas" Width="180" Height="180" Canvas.Left="0" Canvas.Top="0" HorizontalAlignment="Center" VerticalAlignment="Top">
<Path x:Name="TopPart" Stretch="Fill" StrokeLineJoin="Miter" Stroke="#FFB5BECB" Fill="#FFCDD5DE" Data="F1 M 90,18C 109.882,18 127.882,26.0589 140.912,39.0883L 152.225,27.7746C 136.301,11.8497 114.301,2.00002 90,2.00002L 90,18 Z "/>
<Path x:Name="BottomPart" Stretch="Fill" StrokeThickness="1" StrokeLineJoin="Miter" Stroke="#FF13487E" Fill="#FF13487E" Data="F1 M 89.9999,5.00005C 113.472,5.00005 134.722,14.514 150.104,29.896L 152.225,27.7746C 136.301,11.8497 114.301,2.00002 90,2.00002L 89.9999,5.00005 Z"/>
</Canvas>
<ControlTemplate.Triggers>
<Trigger SourceName="MyCanvas" Property="IsMouseOver" Value="True">
<Setter TargetName="TopPart" Property="Stroke" Value="#FFB5BECB"/>
<Setter TargetName="TopPart" Property="Fill" Value="#FFEDF2F7"/>
<Setter TargetName="BottomPart" Property="Stroke" Value="#FF6F9FFF"/>
<Setter TargetName="BottomPart" Property="Fill" Value="#FF6F9FFF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
Update for your comment. A control template can consist of several named part and visual states that define the visual appearance and behavior of the control. Some parts may even be required for the control to work correctly. You can find them in the documentation for styles and templates with examples, but beware that those may be incomplete.
In case of the ToggleButton, there are many visual states e.g. MouseOver, Pressed, Checked, and so on. You can simply define storyboards for each to define the appearance and transitions between states. This approach uses the VisualStateManager, which is an alternative to styles and triggers. There are many tutorials out there. Your template could look like below. Add storyboards to states as necessary. An easier way to define visual states than editing XAML code is to use Blend, where you can edit them conveniently using a GUI Editor. See Blend 2015: Create Visual States or similar tutorials for an overview.
<ToggleButton Content="Templated Toggle Button">
<ToggleButton.Template>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<!-- ...here might be a container that you did not provide in your question. -->
<Canvas x:Name="MyCanvas"
Width="180"
Height="180"
Canvas.Left="0"
Canvas.Top="0"
HorizontalAlignment="Center"
VerticalAlignment="Top">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="Stroke.(SolidColorBrush.Color)"
Storyboard.TargetName="TopPart">
<EasingColorKeyFrame KeyTime="0"
Value="#FFB5BECB" />
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="Fill.(SolidColorBrush.Color)"
Storyboard.TargetName="TopPart">
<EasingColorKeyFrame KeyTime="0"
Value="#FFEDF2F7" />
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="Stroke.(SolidColorBrush.Color)"
Storyboard.TargetName="BottomPart">
<EasingColorKeyFrame KeyTime="0"
Value="#FF6F9FFF" />
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="Fill.(SolidColorBrush.Color)"
Storyboard.TargetName="BottomPart">
<EasingColorKeyFrame KeyTime="0"
Value="#FF6F9FFF" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="Stroke.(SolidColorBrush.Color)"
Storyboard.TargetName="TopPart">
<EasingColorKeyFrame KeyTime="0"
Value="DarkRed" />
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="Fill.(SolidColorBrush.Color)"
Storyboard.TargetName="TopPart">
<EasingColorKeyFrame KeyTime="0"
Value="Red" />
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="Stroke.(SolidColorBrush.Color)"
Storyboard.TargetName="BottomPart">
<EasingColorKeyFrame KeyTime="0"
Value="Blue" />
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="Fill.(SolidColorBrush.Color)"
Storyboard.TargetName="BottomPart">
<EasingColorKeyFrame KeyTime="0"
Value="Blue" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled" />
</VisualStateGroup>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked" />
<VisualState x:Name="Unchecked" />
<VisualState x:Name="Indeterminate" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Path x:Name="TopPart"
Stretch="Fill"
StrokeLineJoin="Miter"
Stroke="#FFB5BECB"
Fill="#FFCDD5DE"
Data="F1 M 90,18C 109.882,18 127.882,26.0589 140.912,39.0883L 152.225,27.7746C 136.301,11.8497 114.301,2.00002 90,2.00002L 90,18 Z " />
<Path x:Name="BottomPart"
Stretch="Fill"
StrokeThickness="1"
StrokeLineJoin="Miter"
Stroke="#FF13487E"
Fill="#FF13487E"
Data="F1 M 89.9999,5.00005C 113.472,5.00005 134.722,14.514 150.104,29.896L 152.225,27.7746C 136.301,11.8497 114.301,2.00002 90,2.00002L 89.9999,5.00005 Z" />
</Canvas>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
I have a ControlTemplate which targets a button control. The ControlTemplate has two images for the normal and pressed states, one for each. I want to use this ControlTemplate in 8 different buttons in the screen, each one with a diferent image in front of it.
<ControlTemplate x:Key="ButtonControlTemplate1" TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates1">
<VisualState x:Name="Pressed1">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="image">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="image1">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Normal1"/>
<VisualState x:Name="Disabled1"/>
<VisualState x:Name="MouseOver1"/>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates1">
<VisualState x:Name="Focused1"/>
</VisualStateGroup>
<VisualStateGroup x:Name="CommonStates"/>
<VisualStateGroup x:Name="FocusStates"/>
</VisualStateManager.VisualStateGroups>
<Image x:Name="image" Source="source1" />
<Image x:Name="image1" Source="source2" Visibility="Collapsed"/>
</Grid>
</ControlTemplate>
How can I put a third image inside the template that can receive a different source for each button?
Something like this:
<Button Template="{StaticResource ButtonControlTemplate1}" thirdImage="source_to_third_image"/>
Why not just use the Content property for your third image?
Your ControlTemplate will look like this:
<ControlTemplate x:Key="ButtonControlTemplate1" TargetType="Button">
<Grid>
...
<ContentPresenter Content="{TemplateBinding Content}" />
<Image x:Name="image" Source="source1" />
<Image x:Name="image1" Source="source2" Visibility="Collapsed"/>
</Grid>
</ControlTemplate>
And your button declaration will look like this:
<Button>
<Image Source="source3" />
</Button>
Then you just need to add a relevant visual state.
From what I understand in your question, you're looking to implement attached properties.
Since the button itself does not carry properties for three separate image sources (you could hack around the Content property, but that would be a pain), you will need to implement those and simply perform standard binding from your template.
I've video player with two button: Play and Pause.
I want to use only one button. when user clicks on Play, the button appearance will changed to Pause and vice versa.
What is the better approach to achieve that task without using cs code behind?
I've tried to use DataTrigger to my IsPlaying property, but with no much success....
Here is my code:
<Window.Resources>
<Viewbox x:Key="viewboxSource" >
<Viewbox.Triggers>
<DataTrigger Binding="{Binding IsPlaying}" Value="True">
<Setter Property="Path">
<Setter.Value>
<Path Stroke="Black" StrokeThickness="1" Fill="AliceBlue">
<Path.Data>
<GeometryGroup>
<EllipseGeometry Center="100,100" RadiusX="100" RadiusY="100"/>
</GeometryGroup>
</Path.Data>
</Path>
</Setter.Value>
</Setter>
</DataTrigger>
</Viewbox.Triggers>
</Viewbox>
</Window.Resources>
<StackPanel>
<Button Content="{StaticResource viewboxSource}"></Button>
</StackPanel>
But I gut an error that says " 'Path' member is not valid because it does not have a qualifying type name " .
Can anyone can help or give me a better solution?
These kind of behaviour fits toggle button patern.
Make a style in your resources
<Style x:Key="PlayToggleButtonStyle" TargetType="ToggleButton" >
and then define a templeate in it
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
What is the most important here is to use VisualStateManager.
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Disabled"/>
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver"/>
<VisualState x:Name="Checked">
<Storyboard>
<DoubleAnimation Duration="0" To="2" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)" Storyboard.TargetName="border" />
<ColorAnimation Duration="0:0:0.2" To="#FF392929" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="border"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
I use 2 animation. One moves button for 2 pixels and second change the gradient which gives a nice experience.
The only drawback is you need to use storyboards to handle these states. You need to add a Path object which I called Geometry nad mainupulate it.
<Storyboard Storyboard.TargetName="Geometry"
Storyboard.TargetProperty="Data">
<ObjectAnimationUsingKeyFrames>
<DiscreteObjectKeyFrame KeyTime="0" Value=""/> <!-- place the data here (how your button looks like) -->
</ObjectAnimationUsingKeyFrames>
</Storyboard>
But IMHO the better solution is to place 2 Path object in the template that on is over another and change the opacity of the top-most one.
<Storyboard>
<DoubleAnimation Storyboard.TargetName="TopGeometry" Storyboard.TargetProperty="Opacity" Duration="0:0:0.5" To="0.0">
<DoubleAnimation.EasingFunction>
<QuadraticEase EasingMode="EaseIn"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
You would have a nice transition between these two states. What is more, no data is needed f.e IsPLaying property.
I've already scoured my XAML file for the word "Width" and it only appears with a number as a value at the root element, setting the width of the entire control. The word "height" only has a numerical value at the root element and two textboxes that aren't in the same Grid cell as the checkbox below.
Unfortunately, regardless of the values I set in the UserControl element for height and width, the following checkbox doesn't get bigger:
<Style x:Key="CheckBoxStyle1" TargetType="CheckBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<Grid ToolTipService.ToolTip="">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="path" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Unchecked"/>
<VisualState x:Name="Indeterminate"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Image x:Name="lightbulb" Source="BestLogin_Images/lightbulb.png" ToolTipService.ToolTip=""/>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="140,0,0,0" Content="" ToolTipService.ToolTip=""/>
<Path x:Name="path" Data="M41.75,5.5 L53.75,2.25 L58.5,0.75 L65.75,1.5 L73,3.75 L83.25,8.25 L88.5,13.25 L95.5,21 L102,33.75 L102.5,44.5 L97.5,59.25 L90.25,74.75 L86.5,82.5 L84.25,91.5 L83.75,96.5 L79.5,104.25 L79.75,108.75 L78.5,113.25 L80,114.5 L79.75,119.75 L79,126 L76.75,132 L74.5,134.5 L68.75,140.5 L65,142 L64.53125,143.125 L62.75,144.75 L61.4375,145.59375 L57.71875,145.90601 L55.15625,143.46875 L53.4375,142.40625 L50.625,141.46875 L47.15625,137.34375 L43.5,135 L40.375,129.84375 L39.5,128.53125 L40.4375,127.375 L41.25,126.09375 L41.15625,124.78125 L39.15625,123.65625 L39.343998,121.5625 L39.343998,116.15625 L39.406498,111.3125 L38.750252,104.90625 L35.583599,99.791664 L35.292271,91.125 L33.375278,80.75 L31.54195,77.583336 L26.791613,69.75 L21.332918,59.541668 L19.874918,56.25 L19.08325,37.708332 L19.37525,36.75 L21.750242,27.333334 L27.041893,18.416666 L31.79188,11.833333 L37.416862,7.0833335 L38.791859,6.5833335 z" HorizontalAlignment="Left" Margin="19.083,0.75,0,18.094" Stretch="Fill" Fill="#B2FFF500" Opacity="0" ToolTipService.ToolTip="Click me to light up your day."/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
...snip...(Grid layout)
<CheckBox Content="CheckBox" Style="{StaticResource CheckBoxStyle1}" HorizontalAlignment="Left" VerticalAlignment="Stretch" Grid.Row="2" Name="CheckBox1" />
Sizes of elements in WPF are determined by more than just Width and Height. You also need to consider all of the Alignment settings: HorizontalAlignment, VerticalAlignment, and sometimes HorizontalContentAlignment, and VerticalContentAlignment, as well as the layout Panels being used to arrange your elements. Here you've left-aligned the CheckBox so it will never get a bigger Width than the minimum it needs. You haven't included the Grid XAML but I suspect that the Row for the CheckBox may be set to Height="Auto" but I can't tell for sure without seeing the rest of the layout.
I wrote simple template in xaml for button s.(for silver-light 4)
So when I try use "ControlTemplate.Triggers", I found that is impossible in silver-light, and we must use Visual-State in Silver-Light
so I wrote first ControlTemplate with Visual-State but it not work fine.(here is code)
<Style x:Key="NextButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid x:Name="MainGrid">
<Border x:Name="MainBorder"
BorderThickness="2"
BorderBrush="#FFC0C0C0"
Background="Bisque"
CornerRadius="4 4 4 4" >
<TextBlock x:Name="lbl"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Text=">"
Foreground="#FFC0C0C0"
FontWeight="Bold"
FontFamily="TimesNewRoman"
FontSize="15"/>
</Border>
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="CommonStates">
<vsm:VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimationUsingKeyFrames AutoReverse="False" Duration="00:00:00.2"
Storyboard.TargetName="MainBorder"
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color}">
<SplineColorKeyFrame KeyTime="00:00:00" Value="#FF606060"/>
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames AutoReverse="False" Duration="00:00:00.2"
Storyboard.TargetName="lbl"
Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color}">
<SplineColorKeyFrame KeyTime="00:00:00" Value="#FF606060"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
When i use this style and move on this border this border, both of border and textbloc became invisible. so
1) What do i do?
2) and is there any good examples for Visual-State
Because of two simple mistakes u r style was not working , else all is right.
1)Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color}
It will be :
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)
2) The same goes for the textblock :
Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color}
It will be :
Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)