Flash animation for my textblock - c#

I have set up a textblock to set the string "ALARM" added to it when the alrm clock goes off. The code is working fine for this process. I am trying to make this string "ALARM" (or the textblock itself) flash when the alarm goes off.
I am able to work out the codes to make the string "ALARM" fade in fade out using mouse events but cant figure out how to make it happen w/o the need for mouse events. I tried textBlock_Loaded event and that doesn't work. I want the fade in fade out to be ongoing forever in a loop to create a flashing effect.
Please advice if there is an event that would fit my need. Been trying one by one down the list of available events with no success. My codes for the mouse events is below. Appreciate any advice. Thanks.
private void textBlock3_MouseLeave(object sender, MouseEventArgs e)
{
TextBlock textblk = (TextBlock)sender;
DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(2));
textblk.BeginAnimation(TextBlock.OpacityProperty, animation);
}
private void textBlock3_MouseEnter(object sender, MouseEventArgs e)
{
TextBlock textblk = (TextBlock)sender;
DoubleAnimation animation = new DoubleAnimation(1, TimeSpan.FromSeconds(2));
textblk.BeginAnimation(TextBlock.OpacityProperty, animation);
}

All you need is an EventTrigger with the "Loaded" event. Set the RepeatBehavior to "Forever" so that the Storyboard keeps repeating, and AutoReverse to "True":
<TextBlock x:Name="textBlock3" Text="hello world">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" AutoReverse="True">
<DoubleAnimation Storyboard.TargetProperty="Opacity"
Duration="0:0:1"
To="0"
/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>

Related

How to reset a storyboard with page cache enabled?

I am using a storyboard to change the opacity of a Rectangle object from 1 to 0. The Storyboard works perfectly using the following codebehind:
StoryboardFadeHider.Begin();
My page is using NavigationCacheMode = NavigationCacheMode.Enabled; to cache the page state. The animation is started in OnNavigatedTo but has a delayed BeginTime. When loading the page for the first time, this works perfectly as the Opacity is 1 and fades to 0. When loading the page from cache, the opacity is 0 because the storyboard run already. The result in this case is
Opacity is 0
Delay
Storyboard starts and sets Opacity to 1
fade to 0.
Setting the Opacity to 1 with FadeHider.Opacity = 1; before running the storyboard doesn't work somehow, my guess is that the cached storyboard overrides this.
This is the storyboard and Rectangle:
<Page.Resources>
<Storyboard x:Name="StoryboardFadeHider">
<DoubleAnimation Storyboard.TargetName="FadeHider" BeginTime="0:0:1" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.5" />
</Storyboard>
</Page.Resources>
...
<Rectangle Grid.Row="0" Name="FadeHider" Opacity="1" Stretch="Fill" Fill="Black" />
How can i set the opacity to 1 everytime the page is loaded before running the storyboard?
When you navigate to other page reset the opacity to 1 in OnNavigatingFrom:
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
StoryboardFadeHider.Stop();
}
Also in OnNavigatedToyou can start the storyboard again :
protected override void OnNavigatedTo(NavigationEventArgs e)
{
StoryboardFadeHider.Begin();
}

Android's Ripple Effect in WPF

I love Androids new animation where you touch a control (listviewitem, button etc etc) and it does a neat animation like this:
I'm wondering how this can be implemented in a nice way globally for all the 'clickable' controls in WPF.
What I specifically need help with is how the circles should be created on the control. The only thing I've thought of was to create own user-controls for each other control (buttons, radiobuttons, etc) where I have a parent for the ellipse as well as the original control itself.
<UserControl>
<Grid MouseLeftButtonDown="handler">
<Button/> <--- this would be the button which you normally would place
</Grid >
</UserControl>
And in the handler-method then create an ellipse on the point e.GetPosition(handler) using the margin-properties and later animate it. This solution would work. But it would be a hassle to do this for every control I would want the ripple effect on. Basically something like this:
void handler(object sender, MouseButtonEventArgs e)
{
Grid parent = (Grid)sender;
Ellipse ellipse = new Ellipse();
ellipse.Height = 10; // would be animated
ellipse.Width = 10; // would be animated
Point p = e.GetPosition(parent);
ellipse.Margin = new Thickness(p.X, p.Y, 0, 0);
parent.Children.Add(ellipse);
// do the animation parts to later remove the ellipse
}
Is there a cleaner, more expandable way to place ellipses on my controls other than the way I earlier demonstrated since not all controls support having children?
UPDATE:
This problem was so interesting to me that I implemented it. You can find it on my Github page: https://github.com/Domysee/WpfCustomControls. There are multiple custom controls, the one you are looking for is RippleEffectDecorator.
Now I explain what I did:
I created a custom control that inherits from ContentControl, RippleEffectDecorator. It defines an additional dependency property HighlightBackground, which is used for the background after you clicked the element.
The ControlTemplate of RippleEffectDecorator consists of a Grid, an Ellipse and a ContentPresenter.
<ControlTemplate TargetType="{x:Type l:RippleEffectDecorator}">
<Grid x:Name="PART_grid" ClipToBounds="True" Background="{TemplateBinding Background}"
Width="{Binding ElementName=PART_contentpresenter, Path=ActualWidth}"
Height="{Binding ElementName=PART_contentpresenter, Path=ActualHeight}">
<Ellipse x:Name="PART_ellipse"
Fill="{Binding Path=HighlightBackground, RelativeSource={RelativeSource TemplatedParent}}"
Width="0" Height="{Binding Path=Width, RelativeSource={RelativeSource Self}}"
HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ContentPresenter x:Name="PART_contentpresenter" />
</Grid>
</ControlTemplate>
I used a Grid instead of a Border so that I can add multiple child elements (necessary that Ellipse and ContentPresenter can overlap). The ellipse binds its Height property to its own width, so that it is always a circle.
Now to the important part: the animation.
The Grid defines in its resources a Storyboard, which is played on every MouseDown event.
<Storyboard x:Key="PART_animation" Storyboard.TargetName="PART_ellipse">
<DoubleAnimation Storyboard.TargetProperty="Width" From="0" />
<ThicknessAnimation Storyboard.TargetProperty="Margin" />
<DoubleAnimation BeginTime="0:0:1" Duration="0:0:0.25" Storyboard.TargetProperty="Opacity"
From="1" To="0" />
<DoubleAnimation Storyboard.TargetProperty="Width" To="0" BeginTime="0:0:1.25" Duration="0:0:0" />
<DoubleAnimation BeginTime="0:0:1.25" Duration="0:0:0" Storyboard.TargetProperty="Opacity" To="1" />
</Storyboard>
The storyboard animates the width property of the ellipse so that it fills the area completely. It also has to animate the Margin, because the ellipse positions itself relative to the upper left point (not around its center).
The start position of the ellipse, its target width and its position in the container throughout the effect has to be set programmatically.
I overwrite the OnApplyTemplate() method to add an event handler to the mouse down event, which starts the storyboard and sets all necessary values.
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
ellipse = GetTemplateChild("PART_ellipse") as Ellipse;
grid = GetTemplateChild("PART_grid") as Grid;
animation = grid.FindResource("PART_animation") as Storyboard;
this.AddHandler(MouseDownEvent, new RoutedEventHandler((sender, e) =>
{
var targetWidth = Math.Max(ActualWidth, ActualHeight) * 2;
var mousePosition = (e as MouseButtonEventArgs).GetPosition(this);
var startMargin = new Thickness(mousePosition.X, mousePosition.Y, 0, 0);
//set initial margin to mouse position
ellipse.Margin = startMargin;
//set the to value of the animation that animates the width to the target width
(animation.Children[0] as DoubleAnimation).To = targetWidth;
//set the to and from values of the animation that animates the distance relative to the container (grid)
(animation.Children[1] as ThicknessAnimation).From = startMargin;
(animation.Children[1] as ThicknessAnimation).To = new Thickness(mousePosition.X - targetWidth / 2, mousePosition.Y - targetWidth / 2, 0, 0);
ellipse.BeginStoryboard(animation);
}), true);
}
Note: the last parameter of AddHandler() determines whether or not you want to receive handled events. It is important to set this to true, because some UiElements handle mouse events (e.g. Button). Otherwise the MouseDownEvent would not fire and therefore the animation not executed.
To use it simply add the element on which you want to have this effect as child of RippleEffectDecorator, and the Background to Transparent:
<cc:RippleEffectDecorator Background="Green" HighlightBackground="LightGreen">
<Button FontSize="60" Background="Transparent">stuff</Button>
</cc:RippleEffectDecorator>
Note2: some elements include triggers which set the template on MouseOver (e.g. Button) and therefore hide the effect. If you dont want that you have to set the template of the button and remove these triggers. The easiest way is to use Blend, get the template of the button from it, remove all triggers and add it as template of your button.
there also a very cool WPF material design library
http://materialdesigninxaml.net/
I suggest using Custom Controls over UserControls for this. Almost everything could be handled in xaml that way. Once you have your control styled then all you have to do is add an Ellipse and set a MouseDown or Button.Pressed trigger to your ControlTemplate.Triggers. Then your animation would only need to increase the height and width of the Ellipse until the ripple effect was completed then fade the Opacity to 0.
For the Ellipse make sure you have the position fixed and for the same color scheme try a white fill and an opacity of 0.3-5.
For the ring effect on the button in the top corner of your add a 2nd Ellipse set the Fill to Transparent and the Stroke to White.

C# equivalent for xaml code for animation

I created this little animation using expression blend for a rectangle named "rect"
<Storyboard x:Name="flipanim">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)" Storyboard.TargetName="rect">
<EasingDoubleKeyFrame KeyTime="0" Value="90"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
Now I want to show this animation for every listbox item(generated using itemtemplate) loaded at runtime,How can I set listitems' animate property.How can I specify TargetName property for list items?
And if that's not possible then I would like to know about how to convert the above code in C#.
May this will help you
DoubleAnimation rotation = new DoubleAnimation();
rotation.From = 0;
rotation.To = 90;
rotation.Duration = new Duration(TimeSpan.FromSeconds(0.5));
Storyboard.SetTarget(rotation, rect);
Storyboard.SetTargetProperty(rotation, new PropertyPath("(UIElement.RenderTransform).(RotateTransform.Angle)"));
Storyboard flipanim= new Storyboard();
flipanim.Children.Add(rotation);
flipanim.Begin();
You can define the Storyboard inside the ItemTemplate, so each item has it's own, and use an EventTrigger for the Loaded event and an Action to start your Storyboard. Define the Storyboard as a Resource of the first element inside your DataTemplate so you can access named elements (like here).

How can I trigger animation from C#? [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Start Storyboard When Text Changes
I've created a animation:
<phone:PhoneApplicationPage
...>
<phone:PhoneApplicationPage.Resources>
<Storyboard x:Name="MessageFadeInOut" Storyboard.TargetProperty="Opacity">
<DoubleAnimation From="0" To="1" Duration="0:0:1" BeginTime="0:0:0" />
<DoubleAnimation From="1" To="1" Duration="0:0:1" BeginTime="0:0:1" />
<DoubleAnimation From="1" To="0" Duration="0:0:1" BeginTime="0:0:2" />
</Storyboard>
</phone:PhoneApplicationPage.Resources>
What I'm trying to do where is have something fade in, stay for a bit then fade out.
I trigger it by:
private void Unit_Tap_1(object sender, System.Windows.Input.GestureEventArgs e)
{
Storyboard sb = this.Resources["MessageFadeInOut"] as Storyboard;
Storyboard.SetTarget(sb, this.Message);
sb.Begin();
}
But on the sb.Begin() I get "System.InvalidOperationException". How come? Message is a Image
I'm not sure what "Message" is in this context, and that might be the reason why you're seeing this exception. Or it might not be .
Either way, you shouldn't grab Storyboards and start those manually. You should use VisualStateManager to manage your visual states by encapsulating a storyboard in each state. You can read more about this # Start Storyboard When Text Changes
A good way to get started in VSM would be to watch these videos by Steve White in the Expression Blend 2 launch:
Adding Control States # http://msdn.microsoft.com/en-us/expression/ff898424
Create Custom Buttons # http://msdn.microsoft.com/en-us/expression/ff921363
Customize a Checkbox’s Checkmark # http://msdn.microsoft.com/en-us/expression/ff921365
Use an In-State Animation # http://msdn.microsoft.com/en-us/expression/ff921380
Each of these videos is part of a series, so consider watching the rest of the series. There are also articles you can read # http://www.interact-sw.co.uk/iangblog/2008/06/10/visual-state

WPF, Opacity and Threads

I have two overlapping (in same position, having the same size) MediaElements. They will contain images. The opacity of one element will be set to 1.0 and the opacity of the other set to 0.0. The idea here would be a simple transition for a slide-show type deal. When it's time to display the next slide, the background element loads a picture and the opacity of both elements switches gradually.
I tried (successfully) to implement this behavior using System.Timers, only to find that having more than some arbitrary number of timers in the same application would cause .NET to randomly spawn and cede control of timer_elapsed to several different threads. This caused unpredictable results and generally made me question my sanity.
So, I decided to do the same thing, but with System.Threads and their Sleep functions. For whatever reason, gradually cycling the opacity worked perfectly with the insane timers but fails utterly with threads. And it fails in a ridiculous way. The opacity of both elements does change, but there's no in between. The element is shown either with opacity at 1.0 or 0.0. Otherwise I would notice that roughly half the pictures weren't being cycled through.
After much googling, I thought perhaps the priority of the thread that the opacity changes were occurring on was somehow keeping the UI elements from being rendered immediately. But then I recalled that because I was using dispatcher invocations on the media elements, all of the action was taking place on the main thread anyway, so it wouldn't make a difference.
Contemplate the following code: https://gist.github.com/956093
As suggested you should use the native animations; i have come accross this thread issue before as well and in general i try to avoid using Dispatchers, i pretty much only use them to modify data if i must (e.g. ObservableCollection cannot be modified in a background thread, don't know any other examples actually).
You could still use normal threads, they work well if you use bindings to update the UIElements which nicely bypasses the dispatching issue.
Animation example:
<Grid Name="testGrid" Tag="2">
<Grid.Resources>
<Storyboard x:Key="FadeAnim2to1">
<DoubleAnimation Storyboard.Target="{x:Reference img1}"
Storyboard.TargetProperty="Opacity"
Duration="0:0:1" To="1"/>
<DoubleAnimation Storyboard.Target="{x:Reference img2}"
Storyboard.TargetProperty="Opacity"
Duration="0:0:1" To="0"/>
</Storyboard>
<Storyboard x:Key="FadeAnim1to2">
<DoubleAnimation Storyboard.Target="{x:Reference img1}"
Storyboard.TargetProperty="Opacity"
Duration="0:0:1" To="0"/>
<DoubleAnimation Storyboard.Target="{x:Reference img2}"
Storyboard.TargetProperty="Opacity"
Duration="0:0:1" To="1"/>
</Storyboard>
</Grid.Resources>
<Image x:Name="img1" Source="Images/Default.ico" Width="200" Height="200" Opacity="0"/>
<Image x:Name="img2" Source="Images/Error.ico" Width="200" Height="200"/>
</Grid>
<Button Content="Fade" Click="Button1_Click"/>
private void Button1_Click(object sender, RoutedEventArgs e)
{
Storyboard anim;
if ((string)testGrid.Tag == "1") //This is just for brevity, you should of course not use the Tag to store state information, let alone number strings
{
anim = testGrid.Resources["FadeAnim1to2"] as Storyboard;
testGrid.Tag = "2";
}
else
{
anim = testGrid.Resources["FadeAnim2to1"] as Storyboard;
testGrid.Tag = "1";
}
anim.Begin();
}

Categories

Resources