WPF Animating a Run element to flash - wpf

This is kind of a weird problem I am having right now. What I am trying to do is animate a Run element to essentially flash/blink. The parent is a Hyperlink which contains multiple Inlines of type Run and Image. Now I am trying to animate the Foreground color of the element but it does not seem to work.
Here is my code for a hyperlink.
CallbackHyperLink callbackLink = new CallbackHyperLink();
ToolTipService.SetShowDuration(callbackLink, 3600000);
ToolTipService.SetInitialShowDelay(callbackLink, 0);
callbackLink.Foreground = new SolidColorBrush(Colors.Magenta); // Default text color of the link
callbackLink.TextDecorations = null; // Disable the underline until mouse over
callbackLink.ToolTip = f.Tooltip; // Set the tooltip string
DoubleAnimation opacityAnim = new DoubleAnimation();
opacityAnim.From = 1.0;
opacityAnim.To = 0.0;
opacityAnim.FillBehavior = FillBehavior.Stop;
opacityAnim.Duration = TimeSpan.FromSeconds(BlinkDurationOff);
opacityAnim.AutoReverse = true;
_blinkAnimation.Children.Add(opacityAnim);
Storyboard.SetTarget(opacityAnim, callbackLink.Foreground);
Storyboard.SetTargetProperty(opacityAnim, new PropertyPath(SolidColorBrush.OpacityProperty));
_blinkAnimation.Stop();
_blinkAnimation.Begin();
So that gets put in a storyboard which get fired. However the foreground is not getting animated and I am not seeing any warnings that im trying to animate something I shouldn't. Anybody have any ideas?
Thanks

This works:
Storyboard.SetTarget(opacityAnim, callbackLink);
Storyboard.SetTargetProperty(opacityAnim, new PropertyPath(UIElement.OpacityProperty));
Edit full working example with a TextBlock stolen from here :
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
TextBlock callbackLink = new TextBlock();
callbackLink.HorizontalAlignment = HorizontalAlignment.Center;
callbackLink.VerticalAlignment = VerticalAlignment.Center;
callbackLink.Text = "Test";
this.Content = callbackLink;
NameScope.SetNameScope(this, new NameScope());
var b = new SolidColorBrush(Colors.Magenta);
callbackLink.Foreground = b;
this.RegisterName("MyAnimatedBrush", b);
DoubleAnimation opacityAnimation = new DoubleAnimation();
opacityAnimation.To = 0.0;
opacityAnimation.Duration = TimeSpan.FromSeconds(0.5);
opacityAnimation.AutoReverse = true;
opacityAnimation.RepeatBehavior = RepeatBehavior.Forever;
Storyboard.SetTargetName(opacityAnimation, "MyAnimatedBrush");
Storyboard.SetTargetProperty(
opacityAnimation, new PropertyPath(SolidColorBrush.OpacityProperty));
Storyboard mouseLeftButtonDownStoryboard = new Storyboard();
mouseLeftButtonDownStoryboard.Children.Add(opacityAnimation);
callbackLink.MouseEnter += delegate(object sender2, MouseEventArgs ee)
{
mouseLeftButtonDownStoryboard.Begin(this, true);
};
callbackLink.MouseLeave += delegate(object sender2, MouseEventArgs ee)
{
mouseLeftButtonDownStoryboard.Stop(this);
};
}

Also another way to link to the foreground opacity call without registering the name of the brush is the following.
Storyboard.SetTarget(opacityAnim, callbackLink);
Storyboard.SetTargetProperty(opacityAnim, new PropertyPath("Foreground.Opacity"));
This seems to finally work for me. Still tweaking my solution as I am doing a lot of Stop/Begin calls on the animation which I believe is not good way of doing things.

Related

Add event handler (MouseDown) dynamically for PathFigure C# WPF

I created an object from points with this code, dynamically:
SolidColorBrush brushColor = (SolidColorBrush)new BrushConverter().ConvertFromString(_brushColor);
PathFigure figures = new PathFigure();
figures.StartPoint = points[0];
points.RemoveAt(0);
figures.Segments = new PathSegmentCollection(points.Select((p, i) => new LineSegment(p, i % 2 == 0)));
PathGeometry pg = new PathGeometry();
pg.Figures.Add(figures);
canvas.Children.Add(new Path { Stroke = brushColor, StrokeThickness = 3, Data = pg });
Now I want to add event handler for this object. Would not be a problem if object is a path or polyline type. I would just add event handler like this:
poly.MouseDown += new MouseButtonEventHandler(poly_MouseDown);
void poly_MouseDown(object sender, MouseButtonEventArgs e)
{
//code
}
Problem is that I have to use Figures and PathGeometry which does not accept MouseDown event handlers. Since they are from System.Windows.Media class and Path/Polyline is from System.Windows.Shapes I can't find a solution to assign right event handler (MouseDown) to my Figure object.
What is the solution or is there any nice workaround solution for this problem? Maybe cast or convert it somehow?
As I see it you're skipping a crucial step.
Declare the Path Object first, give it the event, and then insert it into your Canvas:
SolidColorBrush brushColor = (SolidColorBrush)new BrushConverter ().ConvertFromString (_brushColor);
PathFigure figures = new PathFigure ();
figures.StartPoint = points[0];
points.RemoveAt (0);
figures.Segments = new PathSegmentCollection (points.Select ((p, i) => new LineSegment (p, i % 2 == 0)));
PathGeometry pg = new PathGeometry ();
pg.Figures.Add (figures);
Path pgObject = new Path({ Stroke = brushColor, StrokeThickness = 3, Data = pg });
pgObject.MouseDown+=new MouseButtonEventHandler(poly_MouseDown);
canvas.Children.Add (pgObject);

Grid Background Image Opacity Animation

Created a simple jquery-nivo-like slider using wpf and want to add an animation between slides that fades the background, changes the background image, and finally fades back in the new background image. I was trying to do the following....I get no errors, background changes, but there wasn't any animation either...what am I doing wrong?
public void SetSlider(MyItem item)
{
//Fade out
DoubleAnimation fadeOutAnimation = new DoubleAnimation(0, TimeSpan.FromSeconds(3));
fadeOutAnimation.AutoReverse = false;
grdContent.Background.BeginAnimation(Brush.OpacityProperty, fadeOutAnimation);
//set background
ImageBrush bgBrush = new ImageBrush();
bgBrush.ImageSource = new BitmapImage(new Uri(item.ImageFile.SavedDirectoryAndFile, UriKind.Absolute));
grdContent.Background = bgBrush;
//Set title
txtTitle.Text = item.Title;
//set Summary
txtSummary.Text = item.Summary;
//Fade back in
DoubleAnimation fadeInAnimation = new DoubleAnimation(1, TimeSpan.FromSeconds(3));
fadeInAnimation.AutoReverse = false;
grdContent.Background.BeginAnimation(Brush.OpacityProperty, fadeInAnimation);
}
Figured it out... I had to apply the animation to the brush that was set in the background property...not the background property itself and I had to make some timing changes. Here is my final solution:
public void SetSlider(MyItem item)
{
//Create the fade out animation.
DoubleAnimation fadeOutAnimation = new DoubleAnimation(0, TimeSpan.FromMilliseconds(500));
fadeOutAnimation.AutoReverse = false;
//wait until the first animation is complete before changing the background, or else it will appear to just "fadeIn" with now fadeout.
fadeOutAnimation.Completed += delegate(object sender, EventArgs e)
{
//once the fadeout is complete set the new back ground and fade back in.
//Create a new background brush.
ImageBrush bgBrush = new ImageBrush();
bgBrush.ImageSource = new BitmapImage(new Uri(item.ImageFile.SavedDirectoryAndFile, UriKind.Absolute));
bgBrush.Opacity = 0;
//Set the grid background to the new brush.
grdContent.Background = bgBrush;
//Set the brush...(not the background property) with the animation.
DoubleAnimation fadeInAnimation = new DoubleAnimation(1, TimeSpan.FromMilliseconds(500));
fadeInAnimation.AutoReverse = false;
bgBrush.BeginAnimation(Brush.OpacityProperty, fadeInAnimation);
};
//Fade out..before changing the background.
var currentBackground = grdContent.Background;
currentBackground.BeginAnimation(Brush.OpacityProperty, fadeOutAnimation);
//Set title
txtTitle.Text = item.Title;
//set Summary
txtSummary.Text = item.Summary;
}
A simpler approach will be using Transitional's they have a sample project of image animation.
References:
xmlns:trans="clr-namespace:Transitionals;assembly=Transitionals"
xmlns:transc="clr-namespace:Transitionals.Controls;assembly=Transitionals"
xmlns:transt="clr-namespace:Transitionals.Transitions;assembly=Transitionals"
xmlns:refl="clr-namespace:System.Reflection;assembly=mscorlib"
<transc:TransitionElement x:Name="TransitionBox" Grid.Row="1" Content="{Binding
CurrentImage}">
<transc:TransitionElement.Transition>
<transt:TranslateTransition StartPoint="1,0" EndPoint="0,0"
Duration="0:0:0.6"/>
</transc:TransitionElement.Transition>
</transc:TransitionElement>
or use SlideShow Control it has if its an automatic Transition.
<transc:Slideshow.TransitionSelector>
<trans:RandomTransitionSelector>
<trans:RandomTransitionSelector.TransitionAssemblies>
<refl:AssemblyName Name="Transitionals" />
</trans:RandomTransitionSelector.TransitionAssemblies>
</trans:RandomTransitionSelector>
</transc:Slideshow.TransitionSelector>

How can I slide a button/textbox or any control in WPF?

I want to simply animate a text-box such that it fades in and also moves to the left (or any x/y position). How can I achieve that?
Also will it matter if it's inside a Grid?
Here's a sketchy method i just wrote for fading in any kind of UIElement:
public static void FadeIn(UIElement element, int xOffset, TimeSpan duration)
{
Transform tempTrans = element.RenderTransform;
TranslateTransform trans = new TranslateTransform(xOffset, 0);
TransformGroup group = new TransformGroup();
if (tempTrans != null) group.Children.Add(tempTrans);
group.Children.Add(trans);
DoubleAnimation animTranslate = new DoubleAnimation(0, (Duration)duration);
animTranslate.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut };
DoubleAnimation animFadeIn = new DoubleAnimation(0, 1, (Duration)duration) { FillBehavior = FillBehavior.Stop };
animTranslate.Completed += delegate
{
element.RenderTransform = tempTrans;
};
element.RenderTransform = trans;
element.BeginAnimation(UIElement.OpacityProperty, animFadeIn);
trans.BeginAnimation(TranslateTransform.XProperty, animTranslate);
}
If some of the workings are not clear feel free to ask.

WPF: Get specify image from touch

I was added picture as a children to layer called "canvas". By the following code:
if (addChild)
{
Image i = new Image();
BitmapImage src = new BitmapImage();
src.BeginInit();
src.UriSource = new Uri(path, UriKind.Absolute);
src.EndInit();
i.Source = src;
i.Width = 200;
i.IsManipulationEnabled = true;
double rotAngle = Rand.GetRandomDouble(-3.14/4, 3.14/4);
i.RenderTransform = new MatrixTransform(Math.Cos(rotAngle), -Math.Sin(rotAngle),
Math.Sin(rotAngle), Math.Cos(rotAngle), Rand.GetRandomDouble(0, this.Width - i.Width), Rand.GetRandomDouble(0, this.Height - i.Width));
canvasImages.Add(i);
canvas.Children.Add(i);
Canvas.SetZIndex(i, canvas.Children.Count-1);
addedFiles.Add(path);
maxZ++;
}
Here is the problem. I'm trying to make an event called "canvas_TouchDown" which can detect the specify picture when I touched it so that it will get the center of that image object.
List<Image> canvasImages = new List<Image>();
private void canvas_TouchDown(object sender, TouchEventArgs e)
{
foreach (Image canvasImage in canvasImages)
{
if (canvasImage.AreAnyTouchesCaptured == true)
{
System.Diagnostics.Debug.WriteLine("I found image that you touch");
}
}
}
However, there is nothing happened. I also try to use PersistId property but it doesn't work. Have any suggestion?
Regard,
C.Porawat
If you are adding the image to the canvas, touching it and expecting the canvas to receive the touch you will be disappointed. You should either listen to "touch down" on the image or "preview touch down" on the canvas.

Using a Storyboard animation on a programmatically-added control

I'm trying to fade in a new control to my application's "app" area which is programmatically added after the existing controls are removed. My code looks like this:
void settingsButton_Clicked(object sender, EventArgs e)
{
ContentCanvas.Children.Clear();
// Fade in settings panel
NameScope.SetNameScope(this, new NameScope());
SettingsPane s = new SettingsPane();
s.Name = "settingsPane";
this.RegisterName(s.Name, s);
this.Resources.Add(s.Name, s);
Storyboard sb = new Storyboard();
DoubleAnimation settingsFade = new DoubleAnimation();
settingsFade.From = 0;
settingsFade.To = 1;
settingsFade.Duration = new Duration(TimeSpan.FromSeconds(0.33));
settingsFade.RepeatBehavior = new RepeatBehavior(1);
Storyboard.SetTargetName(settingsFade, s.Name);
Storyboard.SetTargetProperty(settingsFade, new PropertyPath(UserControl.OpacityProperty));
ContentCanvas.Children.Add(s);
sb.Children.Add(settingsFade);
sb.Begin();
}
However, when I run this code, I get the error "No applicable name scope exists to resolve the name 'settingsPane'."
What am I possibly doing wrong? I'm pretty sure I've registered everything properly :(
I wouldn't hassle with the NameScopes etc. and would rather use Storyboard.SetTarget instead.
var b = new Button() { Content = "abcd" };
stack.Children.Add(b);
var fade = new DoubleAnimation()
{
From = 0,
To = 1,
Duration = TimeSpan.FromSeconds(5),
};
Storyboard.SetTarget(fade, b);
Storyboard.SetTargetProperty(fade, new PropertyPath(Button.OpacityProperty));
var sb = new Storyboard();
sb.Children.Add(fade);
sb.Begin();
I solved the problem using this as parameter in the begin method, try:
sb.Begin(this);
Because the name is registered in the window.
I agree, the namescopes are probably the wrong thing to use for this scenario. Much simpler and easier to use SetTarget rather than SetTargetName.
In case it helps anyone else, here's what I used to highlight a particular cell in a table with a highlight that decays to nothing. It's a little like the StackOverflow highlight when you add a new answer.
TableCell cell = table.RowGroups[0].Rows[row].Cells[col];
// The cell contains just one paragraph; it is the first block
Paragraph p = (Paragraph)cell.Blocks.FirstBlock;
// Animate the paragraph: fade the background from Yellow to White,
// once, through a span of 6 seconds.
SolidColorBrush brush = new SolidColorBrush(Colors.Yellow);
p.Background = brush;
ColorAnimation ca1 = new ColorAnimation()
{
From = Colors.Yellow,
To = Colors.White,
Duration = new Duration(TimeSpan.FromSeconds(6.0)),
RepeatBehavior = new RepeatBehavior(1),
AutoReverse = false,
};
brush.BeginAnimation(SolidColorBrush.ColorProperty, ca1);
It is possible odd thing but my solution is to use both methods:
Storyboard.SetTargetName(DA, myObjectName);
Storyboard.SetTarget(DA, myRect);
sb.Begin(this);
In this case there is no error.
Have a look at the code where I have used it.
int n = 0;
bool isWorking;
Storyboard sb;
string myObjectName;
UIElement myElement;
int idx = 0;
void timer_Tick(object sender, EventArgs e)
{
if (isWorking == false)
{
isWorking = true;
try
{
myElement = stackObj.Children[idx];
var possibleIDX = idx + 1;
if (possibleIDX == stackObj.Children.Count)
idx = 0;
else
idx++;
var myRect = (Rectangle)myElement;
// Debug.WriteLine("TICK: " + myRect.Name);
var dur = TimeSpan.FromMilliseconds(2000);
var f = CreateVisibility(dur, myElement, false);
sb.Children.Add(f);
Duration d = TimeSpan.FromSeconds(2);
DoubleAnimation DA = new DoubleAnimation() { From = 1, To = 0, Duration = d };
sb.Children.Add(DA);
myObjectName = myRect.Name;
Storyboard.SetTargetName(DA, myObjectName);
Storyboard.SetTarget(DA, myRect);
Storyboard.SetTargetProperty(DA, new PropertyPath("Opacity"));
sb.Begin(this);
n++;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message + " " + DateTime.Now.TimeOfDay);
}
isWorking = false;
}
}

Resources