I'm writing a 3D wpf application using Viewport3D. When user push a button, I must start DoubleAnimation on AxisAngleRotation3D, but it must be done synchronous. I can't do it on animation.Completed, because this animation run the next one and the next one recursively.
ButtonHandler must work on UI thread, because to calculate animation I hardly use Viewport3D.
So I must in UI thread start synchronous animation and after it finished continoue work.
I tried this code, but it cause deadlock:
AxisAngleRotation3D axis;
DoubleAnimation animation;
DependencyProperty dp;
var worker = new Thread(() =>
{
Dispatcher.CurrentDispatcher.InvokeShutdown();
Dispatcher.Run();
});
worker.SetApartmentState(ApartmentState.STA);
worker.Start();
AutoResetEvent animationDone = new AutoResetEvent(false);
EventHandler handler = null;
handler = (sender, args) =>
{
animation.Completed -= handler; // remove self
animationDone.Set(); // signal completion
};
animation.Completed += handler;
Dispatcher.CurrentDispatcher.Invoke(new Action(() =>
{
axis.BeginAnimation(dp, animation);
}));
animationDone.WaitOne();
Ok, it's just an idea. Why don't you create this animations in few steps. First start first batch of animation and then, when they'll finish run another set of animations. You could synchronize it with some kind of timer.
You can first create list of animations and objects to run in each step and then just invoke them.
Related
The gist of the code is
Storyboard story = new Storyboard();
DoubleAnimation anim = new DoubleAnimation();
anim.Completed += anim_Completed(object sender, EventArgs e);
...
story.Children.Add(anim);
story.Completed += story_Completed(object sender, EventArgs e);
story.Begin(control, true);
return;
In another method I have:
// Finish the Storyboard now
story.SkipToFill(control);
// I want it to get back to me here after the Completed events have run.
The problem is that the Completed events don't run until the next pass of the WPF dispatcher message loop which is no good to me because they update some state. I also tried
story.Stop(control);
but then the Completed handlers don't get run at all it seems. Is there a way to get the Completed handlers to fire immediately?
To avoid running your storyboard on another ui thread and handling locks or signals you can try the following:
Action emptyDelegate = delegate() { };
control.Dispatcher.Invoke(DispatcherPriority.Render, emptyDelegate);
...to force a render pass and hence pick up the completed events that don't seem to fire when you need them to during processing in the UI's code-behind.
You may see some artefacts come into and out of existence though as any updated dependency property values will, of course, be rendered.
I have a WPF application where a data-intensive operation is called by a context menu selection.
I'm using Dispatcher to run the data-intensive operation in the background, but the context menu does not fully close before it begins - it starts to close (i.e. fade) but doesn't disappear completely before the operation begins. The result is a sloppy looking half-faded context menu sitting open on the screen while my "executing" message displays and the data operation completes.
I understand that there is a separate "rendering" thread that runs in the background of a WPF application, and it seems to me like the Close operation of the context menu is completing but the rendering event of fading the context menu away is not finishing before the data-intensive operation begins.
Here is my code:
private void mnuAddProblem_Click(object sender, RoutedEventArgs e)
{
CareGuide selectedCareGuide = (CareGuide)grdCareGuides.SelectedItem;
List<Problem> selectedProblems = new List<Problem>();
for (int i = 0; i < grdSuggestedProblems.SelectedItems.Count; i++)
{
selectedProblems.Add((Problem)grdSuggestedProblems.SelectedItems[i]);
}
LoadingWindow loader = new LoadingWindow();
loader.Owner = this;
loader.WindowStartupLocation = WindowStartupLocation.CenterOwner;
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate
{
Dispatcher.BeginInvoke(new Action(delegate { loader.ShowDialog(); }));
Dispatcher.Invoke((Action)(() => { dataWorker.AddSuggestedProblems(selectedProblems, selectedCareGuide); }));
Dispatcher.BeginInvoke(new Action(delegate() { loader.Close(); }));
};
worker.RunWorkerAsync();
}
I was able to fix this by disabling the "fading" animation that occurs when opening and closing the context menu by overriding the system parameter that controls popup animation:
<PopupAnimation x:Key="{x:Static SystemParameters.MenuPopupAnimationKey}">None</PopupAnimation>
I have a storyboard which I reuse to animate some pictures, I wanna perform some operation after each animation, which includes some calculations, and then running another animation, so I believe I should be using the StoryBoard's Completed Event MyStoryboard.Completed += storyboard_Com​pleted;
What I'm curious about is, should I start the next animation in the current StoryBoard's Storyboard_Completed Event? And, are there any implications if I started the first animation in a separate thread using the Application.Current.Dispatcher Object?
If I called a StoryBoard.Begin() in a separate thread using the Application.Current.Dispatcher, does the Storyboard_Completed Event also get invoked in the UI thread? In this case, do I still need to wrap the Next Animation within another Dispatcher Invoke?
private void Story_Completed(object sender, EventArgs e)
{
Application.Current.Dispatcher.Invoke((Action)delegate()
{
SomeNewStoryBoardAnimation.Begin();
}
}
Is this correct? Or is there a better way to check if a storyboard has ended and start the next set of calculations & storyboard animation right after that?
I've thought of using a single background worker to handler all animations and calculations in sequence, but I'm also wondering how to "wait" for the animation to complete before starting on the next set of calculations and animations. Is it normal for a BackGroundWorker to have Thread.sleep while waiting for animation to complete?
You could wrap the Storyboard in a Task object and await its completion.
Here is an excellent bit of sample code illustrating how to do just that, taken from a blog post by Morten Nielsen:
public static class StoryboardExtensions
{
public static Task BeginAsync(this Storyboard storyboard)
{
System.Threading.Tasks.TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
if (storyboard == null)
tcs.SetException(new ArgumentNullException());
else
{
EventHandler<object> onComplete = null;
onComplete = (s, e) => {
storyboard.Completed -= onComplete;
tcs.SetResult(true);
};
storyboard.Completed += onComplete;
storyboard.Begin();
}
return tcs.Task;
}
}
Essentially you're creating an extension method, which returns a Task object signalling the completion of the Storyboard. In this way, you get some nice fluid syntax like this:
//Start the storyboard and asynchronously await completion...
await myStoryboard.BeginAsync();
//Do my other stuff here, after the storyboard completes...
Using the Storyboard.Completed event should work for your purposes. The Storyboard.Completed event handler should fire on the UI thread, so you should not need to call Application.Current.Dispatcher.Invoke to fire off the second Storyboard.
There should be no implications if you call the original Storyboard.Begin using Application.Current.Dispatcher.Invoke. This won't launch the storyboard animation on a new thread. It will asynchronously invoke the animation on the main UI thread. Whether you call Begin on the UI thread yourself or whether you use Application.Current.Dispatcher.Invoke to do it, the final result should be the same. Your completed event handler will fire when the storyboard finishes, and you can perform your calculations and fire off the next storyboard.
See the following question for some discussion of storyboard having being used in the past as a timer because of the fact that it runs on the UI thread:
What is the point of using Storyboard as timer?
Also, this is probably overkill for the specific case you are describing, but if you need to orchestrate a bunch of sequential, asynchronous operations, you could use Reactive Extensions:
http://msdn.microsoft.com/en-us/data/gg577609.aspx
The following article includes a sequential storyboard example (though the article is old enough that the syntax has probably changed):
http://www.wintellect.com/cs/blogs/jlikness/archive/2010/08/22/coroutines-for-asynchronous-sequential-workflows-using-reactive-extensions-rx.aspx
I've seen some examples of code where StoryBoard is used as Timer, such as:
void Update()
{
if (_sb == null)
{
_sb = new Storyboard();
_sb.Completed += _sb_Completed;
_sb.Duration = new Duration(TimeSpan.FromSeconds(1));
}
if (_sb_completed)
{
_sb.Begin();
_sb_completed = false;
}
}
void _sb_Completed(object sender, EventArgs e)
{
PerformUpdate();
_sb_completed = true;
}
Is Storyboard at some point better than Timer? Why do people use it?
P.S. Question is related to Silverlight and/or WPF.
A Storyboard, like a DispatcherTimer, runs on the UI thread. So in either case you will never see a cross thread exception.
You can use a Storyboard over a DispatcherTimer because the Storyboard has a higher priority.
But im not sure about the Timer object itself as I've never really used it in favor of the Storyboard or DispatcherTimer.
Using a Storyboard is different from using a standard System.Threading.Timer. The Storybroad operates on the main thread hence the Completed even can manipulate the UI elements without getting cross thread exceptions. The standard Timer callback doesn't run on the UI thread and will therefore need additional help to manipulate UI elements.
However as Mark points out if all that is really needed is a delay then a DispatcherTimer would be the more intuative choice. The difference with DispatcherTimer is that its designed to invoke its Tick event regularly whereas the Storyboard will only call Completed at most once for each call to Begin. A DispatcherTimer can be used in this way by calling its Stop method in the first Tick event occurance.
I have a task that takes a long time to execute. In order to inform the user of the progress, I have a progress bar that I update inside DoWork.
Can anybody tell me if this is the best way to update the progress bar? I have heard that there is a ReportProgress event handler but I am confused because I'm unsure of the purpose of ReportProgress.
Since the Background worker works in a separate thread, you'll run into problems if you try to access UI objects. Calling the ReportProgress method on the worker from inside the DoWork handler raises the ProgressChanged event. That event should be handled in the UI thread so as to easily access the control.
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += DoWorkHandler;
worker.WorkerReportsProgress = true;
worker.ProgressChanged += (s, e) =>
{ myProgressBar.Value = e.ProgressPercentage; };
worker.RunWorkerAsync();
...
public void DoWorkHandler(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (working)
{
// Do Stuff
worker.ReportProgress(progressPercentage);
}
}
The ProgressChanged event is what you are looking for. However, make sure you create the BackgroundWorker like below so it actually raises this event when ReportProgress is called.
BackgroundWorker bw = new BackgroundWorker() { WorkerReportsProgress = true };
bw.ProgressChanged += ... ;
ReportProgress is what you would use to update the progress of your task, including things like the UI--in your case, a proggress bar.
You should check out the MSDN docs, located here.
basically, you create a handler for the ReportProgress event, then in your DoWorkEventHandler, you call the ReportProgress like so:
worker.ReportProgress((i * 10));