I am trying to make an animated custom control with two different styles being displayed depending on a boolean Property.
I have been able to move elements back and forth as well as changing colors to plain colors, but I would like to be able to use a LinearGradient instead of a plain color and this is where it gets tricky.
I am aware that I could do this in a "not so tricky" way in code behind, but I really want to split code behind and display between .cs and .xaml files, so I'd love to have a fully XAML solution.
Here is the code of my Control:
<Canvas
Width="100"
Height="40"
Background="Red">
<Ellipse Width="20" Height="20" >
<Ellipse.RenderTransform>
<TranslateTransform X="0" Y="0"/>
</Ellipse.RenderTransform>
<Ellipse.Stroke>
<SolidColorBrush Color="White"/>
</Ellipse.Stroke>
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.504,1.5" StartPoint="0.504,0.03">
<GradientStop Color="#FFFFFF" Offset="0" />
<GradientStop Color="#BBBBBB" Offset="0.567"/>
</LinearGradientBrush>
</Ellipse.Fill>
<Ellipse.Style>
<Style TargetType="Ellipse">
<Style.Triggers>
<DataTrigger Binding="{Binding Test}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard >
<Storyboard>
<ColorAnimation Duration="0:0:0.5" To="Black" Storyboard.TargetProperty="(Stroke).(SolidColorBrush.Color)" />
<DoubleAnimation Duration="0:0:0.5" To="100" Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="0:0:0.5" To="White" Storyboard.TargetProperty="(Stroke).(SolidColorBrush.Color)"></ColorAnimation>
<DoubleAnimation Duration="0:0:0.5" To="0" Storyboard.TargetProperty="(TextBox.RenderTransform).(TranslateTransform.X)"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
</Canvas>
I would love when my storyBoard begins or ends for instance, to be able to switch the two colors of my gradient, but I see no way to differentiate them as I can't use the Storyboard.TargetName property in a style.
You could use this to animate the first (index = 0) GradientStop of the LinearGradientBrush if that's what you want:
<ColorAnimation Duration="0:0:2" To="Black"
Storyboard.TargetProperty="(Ellipse.Fill).(LinearGradientBrush.GradientStops)[0].Color" />
Related
I want to be able to click my button and on the click trigger I want the background to fill up with a circle, much like how a materialistic button would. I know there is a library for this but I'd much rather develop my own because I want to learn.
This is what it would look like when you click, rather than when you move the mouse out of it.
I've tried playing around with the style of the button, trying to make some animation using storyboards but I can't seem to figure it out.
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Blue"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Transparent"/>
</Trigger>
<EventTrigger RoutedEvent="Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard BeginTime="00:00:00"
RepeatBehavior="Forever"
AutoReverse="True"
Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)">
<ColorAnimation From="Black" To="Red" Duration="0:0:0.1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
When I click the button I want the background to fill up starting from a circle shape (the button shouldn't be circle shaped), filling up the rest of the button, what I am seeing now is just a fading color of the entire thing which is not what I was expecting.
Here is an example of such an effect.
I just used a Rectangle, not a Button, for simplicity. But you surely can apply this technique to any control.
The events MouseEnter and MouseLeave are used here, but you can replace them with click events or whatever you need.
<Rectangle Width="150" Height="150" Stroke="Black" StrokeThickness="1">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard Duration="0:0:1">
<!-- we just scale the brush in this animation -->
<DoubleAnimation From="0" To="2" Storyboard.TargetProperty="(Rectangle.Fill).(DrawingBrush.Transform).(ScaleTransform.ScaleX)"/>
<DoubleAnimation From="0" To="2" Storyboard.TargetProperty="(Rectangle.Fill).(DrawingBrush.Transform).(ScaleTransform.ScaleY)"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard Duration="0:0:1">
<DoubleAnimation From="2" To="0" Storyboard.TargetProperty="(Rectangle.Fill).(DrawingBrush.Transform).(ScaleTransform.ScaleX)"/>
<DoubleAnimation From="2" To="0" Storyboard.TargetProperty="(Rectangle.Fill).(DrawingBrush.Transform).(ScaleTransform.ScaleY)"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
<Rectangle.Fill>
<!-- the rectangle's fill is a brush which can be scaled -->
<DrawingBrush>
<DrawingBrush.Transform>
<!-- the initial scale values are 0, so we don't see the fill -->
<ScaleTransform ScaleX="0" ScaleY="0" CenterX="75" CenterY="75"/>
</DrawingBrush.Transform>
<DrawingBrush.Drawing>
<GeometryDrawing Brush="MediumBlue">
<GeometryDrawing.Geometry>
<EllipseGeometry RadiusX="150" RadiusY="150" Center="75,75" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
In the ControlTemplate of the ToggleButton, I'm defining a Border which has a Polygon. The problem is that the EventTrigger is only applicable on the polygon, not the entire Border.
<ToggleButton Padding="30, 10">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Border Background="Red">
<Polygon Points="12,12 12,26, 22,19" Fill="#4B86B1" Margin="0,0,5,0" RenderTransformOrigin="0.6,0.5">
<Polygon.RenderTransform>
<RotateTransform x:Name="rotRect" Angle="0"/>
</Polygon.RenderTransform>
<Polygon.Effect>
<DropShadowEffect ShadowDepth="0.5" Direction="0" Color="Black" Opacity="1" BlurRadius="1"/>
</Polygon.Effect>
</Polygon>
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="ToggleButton.Checked">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="rotRect" Storyboard.TargetProperty="Angle" From="0" To="90" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="ToggleButton.Unchecked">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="rotRect" Storyboard.TargetProperty="Angle" From="90" To="0" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
In case you wanted to just rotate the polygon around its center, get rid of the RenderTransformOrigin and instead set a CenterX and CenterY on your Transformation:
<ToggleButton Margin="150,100">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Border Background="Red">
<Polygon Points="12,12 12,26, 22,19" Fill="#4B86B1" Margin="0,0,5,0">
<Polygon.RenderTransform>
<RotateTransform x:Name="rotRect" Angle="0" CenterX="17" CenterY="19"/>
</Polygon.RenderTransform>
<Polygon.Effect>
<DropShadowEffect ShadowDepth="0.5" Direction="0" Color="Black" Opacity="1" BlurRadius="1"/>
</Polygon.Effect>
</Polygon>
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="ToggleButton.Checked">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="rotRect" Storyboard.TargetProperty="Angle" From="0" To="90" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="ToggleButton.Unchecked">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="rotRect" Storyboard.TargetProperty="Angle" From="90" To="0" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
If you really want to rotate the button too, simply move the Transformation to the border:
<ToggleButton Margin="100">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Border Background="Red">
<Border.RenderTransform>
<RotateTransform x:Name="rotRect" Angle="0"/>
</Border.RenderTransform>
<Polygon Points="12,12 12,26, 22,19" Fill="#4B86B1" Margin="0,0,5,0">
<Polygon.Effect>
<DropShadowEffect ShadowDepth="0.5" Direction="0" Color="Black" Opacity="1" BlurRadius="1"/>
</Polygon.Effect>
</Polygon>
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="ToggleButton.Checked">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="rotRect" Storyboard.TargetProperty="Angle" From="0" To="90" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="ToggleButton.Unchecked">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="rotRect" Storyboard.TargetProperty="Angle" From="90" To="0" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
The problem is that your Polygon is actually quite larger than it appears; the horizontal and vertical alignment both default to Stretch, so even though the shape you define is relatively small, the Polygon is being laid out such that it fills your entire Border (less a 5pt margin on the right).
I would make two changes:
Set the horizontal and vertical alignments to Left and Top, respectively.
Get rid of the 12pt of 'empty' space inside the polygon, and shift them to the polygon's Margin.
<Polygon Points="0,0 0,14 10,7" Fill="#4B86B1" RenderTransformOrigin="0.5,0.6"
Margin="12,12,5,0" HorizontalAlignment="Left" VerticalAlignment="Top">
<Polygon.RenderTransform>
<RotateTransform x:Name="rotRect" Angle="0" />
</Polygon.RenderTransform>
<Polygon.Effect>
<DropShadowEffect ShadowDepth="0.5" Direction="0" Color="Black"
Opacity="1" BlurRadius="1"/>
</Polygon.Effect>
</Polygon>
I also generally use Path over Polygon (and most other Shape classes, except perhaps Ellipse). I find it helps keep me reasonably fluent in the Path Markup Syntax, which is useful when I need to create more elaborate geometry. An equivalent Path to your Polygon would be:
<Path Data="M 0,0 L 0,14 10,7 Z" ... />
It's up to you which to use. One is not inherently 'better' than the other.
Below my rectangle in WPF:
<Rectangle Margin="5,0" HorizontalAlignment="Left" Width="380" Height="25" Fill="LightYellow" Stroke="Orange" StrokeThickness="2" RadiusX="8" RadiusY="8"/>
I would like to start bliking for some seconds (and then stop) the rectangle stroke property when a property "StartBlinking" in view model changes from false to true.
I would like to implement storyboard in xaml not in c# code.
How can I do this?
I have tried this but not working:
<Rectangle Margin="5,0" HorizontalAlignment="Left" Width="380" Height="25" Fill="LightYellow" Stroke="Orange" StrokeThickness="2" RadiusX="8" RadiusY="8">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Style.Resources>
<Storyboard x:Key="flashAnimation" >
<DoubleAnimation Storyboard.TargetProperty="Stroke" From="1" To="0" AutoReverse="True" Duration="0:0:0.5" RepeatBehavior="Forever" />
</Storyboard>
</Style.Resources>
</Style>
</Rectangle.Style>
</Rectangle>
I am using C# and .NET 3.5 in Visual Studio 2008.
You could animate the Opacity property of the Stroke using a DataTrigger and a Storyboard:
<Rectangle Margin="5,0" HorizontalAlignment="Left" Width="380" Height="25" Fill="LightYellow"
Stroke="Orange" StrokeThickness="2" RadiusX="8" RadiusY="8">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<DataTrigger Binding="{Binding StartBlinking}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Stroke.(SolidColorBrush.Opacity)"
To="0" AutoReverse="True" Duration="0:0:0.5" RepeatBehavior="6x" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Stroke.(SolidColorBrush.Opacity)"
To="1" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
I have a Textblock which animates when the bound Textis changing:
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:0" To="0.0"/>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:1" From="0.0" To="1.0" BeginTime="0:0:0"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
Currently the whole Textblock is fading in as one, but I want the fading to go from left to right.
Is this possible?
EDIT
I solved my problem with the comments below. Here's my solution:
<Style x:Key="FadeLeftRightLabel" TargetType="Label">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<ContentPresenter>
<ContentPresenter.OpacityMask>
<LinearGradientBrush StartPoint="0.0,0.5" EndPoint="1.0,0.5">
<LinearGradientBrush.GradientStops>
<GradientStop x:Name="GradientStop1" Offset="0" Color="Black"/>
<GradientStop x:Name="GradientStop2" Offset="0" Color="Transparent"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</ContentPresenter.OpacityMask>
</ContentPresenter>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="GradientStop1" Storyboard.TargetProperty="Offset" Duration="0:0:1" From="0.0" To="1.0" BeginTime="0:0:0"/>
<DoubleAnimation Storyboard.TargetName="GradientStop2" Storyboard.TargetProperty="Offset" Duration="0:0:1" From="0.0" To="1.0" BeginTime="0:0:0"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You're going to want to use an "Opacity Mask" made out of a linear gradient brush that uses an alpha in the start/end colours.
http://msdn.microsoft.com/en-us/library/ms743320(v=vs.110).aspx#creatingopacitymaskswithgradients
Then animate the gradient stop point(s)
http://msdn.microsoft.com/en-us/library/ms748815(v=vs.110).aspx
Try this, Have Resource like this,
<Window.Resources>
<Storyboard x:Key="Storyboard1" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="txttest">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
TextBlock:
<Grid x:Name="LayoutRoot" Background="White" Margin="0,0,0,-45">
<TextBlock Text="this is to test" FontSize="45" x:Name="txttest" Background="Black" Foreground="White" Margin="0,175,0,114"/>
</Grid>
In your Constructor,
var anim = this.Resources["Storyboard1"] as Storyboard;
if (anim != null) anim .Begin();
I want to apply a Storyboard to my Rectangle Fill like this:
<Rectangle Name="MyRectangle"
Width="100"
Height="100">
<Rectangle.Fill>
<SolidColorBrush x:Name="MySolidColorBrush" Color="Blue" />
</Rectangle.Fill>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseEnter">
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetName="MySolidColorBrush"
Storyboard.TargetProperty="Color"
From="Blue" To="Red" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
But I want to insert the Storyboard in a Style, i tried this:
<Style xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
TargetType="{x:Type Rectangle}">
<Style.Triggers>
<EventTrigger RoutedEvent="Shape.Loaded">
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetName="MySolidColorBrush"
Storyboard.TargetProperty="Color"
From="Blue" To="Red" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
<Setter Property="Shape.Fill" Value="Blue" x:Name="MySolidColorBrush"/>
</Style>
Using this code:
var rect = new Rectangle();
using (FileStream stream = new FileStream("myStyle.xaml", FileMode.Open))
rect.Style = XamlReader.Load(stream) as Style;
But it does not work and throws an exception. How I have to change my Style?
Change this in your Storyboard
Storyboard.TargetProperty="Color"
to
Storyboard.TargetProperty="Fill.Color"
and remove
Storyboard.TargetName="MySolidColorBrush"