WPF Animation: Complete callback not called if to == from - c#

I'd like to find a cleaner way to get the animation complete callback to fire when to == from. Right now I hack this to work by adding a small amount to my from value. Is there a better way?
//todo is there a way to get animations to call their Complete() even when to == from?
if (to.Equals(from)) {
from += .01;
}
DoubleAnimation animation = new DoubleAnimation {
Name = axis == Axis.X ? TranslateTransform.XProperty.Name : TranslateTransform.YProperty.Name,
From = from,
To = to,
Duration = TimeSpan.FromSeconds(translate.Time),
FillBehavior = FillBehavior.Stop,
EasingFunction = translate.Curve.ToEase(),
IsAdditive = false,
};
AnimationClock = animation.CreateClock();
AnimationClock.Completed += (sender, args) => {
};

You can check values before the creation of the animation.
If the Duration is important you can start a timer and launch the code when it ticks.
Something like this should do the trick :
if (to.Equals(from)) {
if (_timer == null)
{
_timer = new Timer(x =>
{
//completeCode
}, null, translate.Time * 1000, Timeout.Infinite);
}
else
{
_timer.Change(translate.Time * 1000, Timeout.Infinite);
}
return;
}

Related

Xamarin Timer is not Running as long as it says

I have this code
Color[] colours = new Color[5]{Color.Red, Color.Blue, Color.Green, Color.Yellow, Color.Black};
public int randGen(int lower, int upper)
{
Random random = new Random();
return random.Next(lower, upper);
}
public PlayGame()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
changeColour();
}
public void changeColour()
{
int milliseconds = randGen(1000, 5000);
int count = 0;
Device.StartTimer(TimeSpan.FromMilliseconds(milliseconds), () =>
{
var layout = new StackLayout { Padding = new Thickness(5, 10) };
var label = new Label { Text = "Time: ", TextColor = Color.Green, FontSize = 25 };
layout.Children.Add(label);
label.Text += milliseconds.ToString();
this.Content = layout;
if (count < 4)
{
BackgroundColor = colours[count];
count++;
milliseconds = randGen(1000, 5000);
return true;
}
else
{
BackgroundColor = Color.Black;
return false;
}
}
);
}
Which has an array of colours. The idea is that every 1-5 seconds (which should be random each time), the background colour should change, and the text should write how long the screen was on for.
Currently, however, the time shown in the text is not reflective of the time each screen shows for, and I have some speculative concern that milliseconds in:
Device.StartTimer(TimeSpan.FromMilliseconds(milliseconds)
doesn't change at all. Any ideas?
this is what I would do - have your timer fire every second (or whatever granularity you need) but only execute your code every X times
using System.Timers;
// these are class variables
Timer timer;
int timecount = 0;
// adjust this dynamically so your code only executes every 1-n seconds
int interval = 1;
// to this wherever you want to start the timer
timer = new Timer();
timer.Elapsed += Timer_Elapsed;
// fire every 1 sec
timer.Interval = 1000;
timer.Start();
// timer event handler
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
timecount++;
if (timecount == interval)
{
timecount = 0;
// do other stuff here
}
}

How expensive is using a System.Action?

I am in a situation where I rely on a System.Action to clean up my UI when a method finishes running. Because this method can be called from different points in the application the cleanup actions may be vastly different.
I'm worried they may be expensive, and that I should find another way to handle the situation.
MoveObject(myControl, new Thickness(0, 1, 2, 3), a =>
{
myButton.IsEnabled = true;
myGrid.IsEnabled = true;
myComboBox.SelectedIndex = 0;
Mouse.OverrideCursor = null;
});
private void MoveObject(FrameworkElement control, Thickness margins, Action<wMain> action, double speedCoefficient = 1)
{
var speed = new TimeSpan(Convert.ToInt64(_defaultAnimationSpeed.Ticks*speedCoefficient));
var animation = new ThicknessAnimation
{
From = control.Margin,
To = margins,
Duration = speed
};
var finalAction = new Action<wMain>(a =>
{
action(this);
control.BeginAnimation(MarginProperty, null); //Clear the animation so the property can be set manually.
control.Margin = margins;
});
animation.Completed += (sender, args) => { Dispatcher.BeginInvoke(finalAction, this); };
control.BeginAnimation(MarginProperty, animation);
}
I sometimes also execute actions by writing action(this); instead of calling the Dispatcher. Is one better than the other?
Delegate invocations are rather cheap; don't worry about it.
You only need the dispatcher if you aren't on the UI thread.

Delay windows phone 8 progress-bar appearance

I want to delay appearance of progressbar in Windows Phone 8 application for 2 sec.
So when I call webservice if I don't receive response after 2 sec progress-bar should appear.
I have implemented code with DispatcherTimer but it does not seams to work as expected.
This variable is binded to IsEnabled and IsVisible of ProgressBar control.
Problem is that this code works randomly and not after 2 sec.When I increase timer for 20 sec progress-bar is still appearing even every response is bellow 1 sec.
private bool _isProgressBarLoading;
public bool IsProgressBarLoading
{
get
{
return _isProgressBarLoading;
}
set
{
if (_isProgressBarLoading != value)
{
if (value)
{
var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(2000) };
timer.Tick += delegate
{
timer.Stop();
_isProgressBarLoading = true;
};
timer.Start();
}
else
{
_isProgressBarLoading = false;
}
NotifyOfPropertyChange(() => IsProgressBarLoading);
}
}
}
How about using different Timer operating on separate thread:
System.Threading.Timer myTimer = null;
private bool _isProgressBarLoading = false;
public bool IsProgressBarLoading
{
get { return _isProgressBarLoading; }
set
{
if (_isProgressBarLoading != value)
{
if (value)
{
if (myTimer == null)
{
myTimer = new System.Threading.Timer(Callback, null, 3000, Timeout.Infinite);
}
else myTimer.Change(3000, Timeout.Infinite);
// it should also work if you create new timer every time, but I think it's
// more suitable to use one
}
else
{
_isProgressBarLoading = false;
NotifyOfPropertyChange(() => IsProgressBarLoading);
}
}
}
}
private void Callback(object state)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
_isProgressBarLoading = true;
NotifyOfPropertyChange(() => IsProgressBarLoading);
});
}
DispatcherTimer is working on the Main thread, I think it will be better to use other Thread.
And as for your code it should work if it looks like this - notify when you change the value:
if (value)
{
var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(2000) };
timer.Tick += delegate
{
timer.Stop();
_isProgressBarLoading = true;
NotifyOfPropertyChange(() => IsProgressBarLoading);
};
timer.Start();
}
else
{
_isProgressBarLoading = false;
NotifyOfPropertyChange(() => IsProgressBarLoading);
}

Defeating the Async Quagmire

I am attempting to do a RouteQuery but it only allows for async calls. Unfortunately when the return is hit it hasn't finished the QueryAsync()
Is there a standard way around this?
Usually I would just start a timer and wait for a property to populate but that is way to ghetto. There has to be a solution for this.
private bool wayPointHit(GeoCoordinate geo)
{
RouteQuery query = new RouteQuery();
List<GeoCoordinate> coords = new List<GeoCoordinate>();
coords.Add(new GeoCoordinate(curLocation.Latitude, curLocation.Longitude));
coords.Add(geo);
query.Waypoints = coords;
bool hit = false;
query.QueryCompleted += (sender, args) =>
{
var rt = args.Result;
if (rt.LengthInMeters <= 20)
{
hit = true;
}
};
query.QueryAsync();
return hit;
}

Trying to fire WPF StoryBoard multiple times with different Completed-actions

I'm unsuccessful in making a storyboard in code behind and running it multiple times chained to each other. Somehow, it seems the storyboard keeps in context, and will not reset.
I'm animating several elements, and X number of times I'm recursively running the animation-method, but with different call-back actions in the Completed event. First animation runs fine, but the rest it doesn't animate at all (the completed-event fires).
If I create a StoryBoard in a method and run it, should it not be disposed after it is completed? I'm trying to do storyboard.Remove().
private void SlideLeft(int numberOfStepsToSlide)
{
if (numberOfStepsToSlide < 1) return;
Slide(() => SlideLeft(numberOfStepsToSlide - 1));
}
protected void Slide(Action callBackAfterAnimation = null)
{
var sb = new Storyboard();
sb.FillBehavior = FillBehavior.Stop; //i thought maybe this would fix it, but no
//..
//.. a number of double animations created and added to storyboard
//..
sb.Completed += (sender, e) =>
{
sb.Stop();
sb.Remove();
//..
//..sending message to ViewModel and manipulating values
//..
if (callBackAfterAnimation != null)
callBackAfterAnimation();
};
sb.Begin();
}
Thanks for your time!
Sorry, completely forgot about this question!
You don't want to call Remove - that basically kills the animation dead, by killing all of the animation clocks created to run it...Try something like this instead (quick-and-dirty example):
var win = new Window();
win.Width = 50;
win.Height = 50;
int runCount = 3;
int halfSteps = runCount * 2;
double toWidth = 500.0;
var sb = new Storyboard();
var biggerator = new DoubleAnimation(toWidth, new Duration(TimeSpan.FromSeconds(2)));
sb.Children.Add(biggerator);
Storyboard.SetTarget(biggerator, win);
Storyboard.SetTargetProperty(biggerator, new PropertyPath("Width"));
sb.Completed += (o,e) =>
{
sb.Stop();
halfSteps--;
if(halfSteps <= 0)
{
win.Height = 150;
}
else
{
biggerator.To = biggerator.To == 0 ? toWidth : 0;
sb.Begin();
}
};
sb.Begin();
win.Show();

Categories

Resources