My WPF program's storyboard animations stutter ruining user experience (for example on adding larger number of items to ObservableCollection and ListBox being updated).
My idea was to make animation run smoother by dispatching them with higher priority so I tried
Storyboard sb = new Storyboard();
//all animations
this.Dispatcher.Invoke(() =>
{ sb.Begin();
}, System.Windows.Threading.DispatcherPriority.Send);
I thought there would be difference between "Send" as highest priority and "SystemIdle" as lowest, but there isn't.
I hope there is small problem in my understanding of the dispatcher and there is a simple fix, but I am open to any ideas of improving animation performance.
EDIT
Implemented on each animated control RenderTargetBitmap which takes screenshot on animation start and replaces complicated visual tree with one image control, and switches it back when animation is complete
Populating listbox that ruins my animation in Dispatcher with SystemIdle priority
Set framerate to 30
Checked in perfmon and nothing seems software rendered, RenderTier is 2
If I disable populating listbox completely everything runs smoothly
Listbox is populated like this
Task.Factory.StartNew(() =>
{
List<MyData> _f = new List<MyData>();
//populate _f
Dispatcher.BeginInvoke(new Action(() => {
_MyCollection.Clear();
_MyCollection.AddRange(_f); //Adding items without raising events *
}), DispatcherPriority.SystemIdle);
}
*I even use addRange for ObservableCollection from ObservableCollection : calling OnCollectionChanged with multiple new items
Related
So I searched for a solution and I didn't find something...
I have an application that use Storyboard for animations, and a button that create the animation and start it. looks like this:
DoubleAnimation animation = new DoubleAnimation
{
//properties
};
sb = new Storyboard();//sb is the Storyboard
sb.Children.Add(animation);
Storyboard.SetTargetName(animation, myControl.Name);
Storyboard.SetTargetProperty(animation, new PropertyPath(Canvas.TopProperty));
sb.Begin(this);
The problem is that when I click the button while the animation still running, it restart the animation, and I want it to "add it to the order" so when the animation finished it starts again over and over, the amount of times I clicked the button.
Instead of trying to change a storyboard that's already running I suggest you instead use just a list of animations.
Or storyboards if you need some functionality in storyboard like animating several things at once.
You can then add them to a list ( or queue even ).
Subscribe to the completed event of each.
Keep a currentIndex pointer so you know which one is running.
When the completed event is hit, unhook your event handler from the one just finished, hook the next one. Begin the next storyboard.
i want to cancel or stop or handle rendering(or drawing) a WPF Element for lower cpu usage(cancel all forever animations and rendering in background or collapsed visibility items for use best performance in my app).
i read all Optimizing WPF Application Performance
void MainWindow_LayoutUpdated(object sender, EventArgs e)
{
e.Handled = true;//can i handle an object rendering?
}
LayoutUpdated is not going to help you at all. It is an event that fires on all UI elements associated with a Dispatcher after a full layout pass has completed, meaning the work is already done.
You have a few options, depending on what your goal is.
Hide the Element
If you don't want the element to be visible or perform any layout, then you can set its Visibility property to Collapsed. It should skip most of its layout logic and all of its rendering logic while it is collapsed.
Remove the Element
Removing the element from the visual tree is a sure way to keep it from doing anything. You can add it back later if you need to. The process for how to do this depends a lot on how your code is currently setup.
Reduce the Performance Cost of the Element
If the element is expensive because it is doing a lot of work, you could look into optimizing it in general, and/or look into disconnecting its event listeners and bindings during the time when you don't want it doing work.
Pause UI Processing
If you want to temporarily pause all UI processing while you perform some operation (to prevent reentrancy), you can use Dispatcher.DisableProcessing. Note that this will disable UI processing for the entire thread, not just the processing of a specific element.
using (Dispatcher.DisableProcessing())
{
// Do work while the dispatcher processing is disabled.
}
A Static Picture of the Element
If you want to permanently disable all processing of an element, I don't know of a way to do that while still keeping it visible on the screen. However, you could use a RenderTargetBitmap to render the element to an ImageSource and then replace the original element with an Image element that has its Source property set to the bitmap.
RenderTargetBitmap target = new RenderTargetBitmap((int)element.RenderSize.Width, (int)element.RenderSize.Height, 96.0, 96.0, PixelFormats.Default);
target.Render(element);
image.Source = target;
Now you basically have a "picture" of the element in place of where the original element was.
So im developing this app, where i got something that reminds alot about a list, but it doesn't use a list control or something. I add grids & rectangels to a scrollviewer, and when i doubletap a grip or a rectangel, it dissapears with a fade animation (which i also need help to do, as well as fade in, fast, one by one on app startup), and when that happens, i want the grid or rectangel beneath the one that faded out (or was removed, what is the best solution?), to be slided up, and replace the empty position. Please, do not misunderstand the question, i dont want you to make it for me, i want to know how since i absolutely cannot find ANY solution at all. It kind of works like google now for android and iphone. How can i do this the best way? Thank you SO much! Best regards, Erik
My DoubleAnimation to fade the grid:
DoubleAnimation fadeGrid = new DoubleAnimation();
fadeGrid.From = 0;
fadeGrid.To = 1;
fadeGrid.Duration = new Duration(TimeSpan.FromSeconds(0.5));
fadeGrid.AutoReverse = false;
1) Use animations for your fade out and slide up. For fade, you'll be doing a DoubleAnimation on the Opacity property. For sliding items up, you'll be doing an animiation on the TranslateTransform property. See these MSDN guides: http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj206955(v=vs.105).aspx and http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.windows.media.translatetransform(v=vs.105).aspx
2) For actually properly moving things up, you'll want to capture the Completed event from the animiation (http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.windows.media.animation.timeline.completed(v=vs.105).aspx). When the animiation completes, remove those controls from the StackPanel inside the ScrollViewer, and undo any position animation you did on the items below it to animate them 'sliding' up.
3) After creating an animiation, you need to add it to a storyboard:
Storyboard sb = new Storyboard();
sb.Children.Add(fadeGrid);
Storyboard.SetTarget(fadeGrid, myRectangle);
Storyboard.SetTargetProperty(fadeGrid, new PropertyPath("(UIElement.Opacity)"));
sb.Begin();
The syntax for the propertypath but not be quite perfect, but you get the idea. You can see another example that specifically mentions opacity here: http://blogs.msdn.com/b/silverlight_sdk/archive/2008/03/21/silverlight-animations-a-walkthrough.aspx
I'm running a fairly simple task with the kinect, just trying to display data streams using the polling method. I am running all of my processing on a backgroundworker, and using dispatcher to update the UI.
The code below works fine for depth and color streams that write to a bitmap in the dispatcher. However, displaying the skeleton requires a Grid with children (polylines). This code runs without error, but does not update the UI to show the line. What do I need to do to make the UI refresh the Grid and show the child objects?
this._SkeletonGrid.Dispatcher.BeginInvoke(new Action(() =>
{
joints = new[] { JointType.Head, JointType.ShoulderCenter, JointType.ShoulderLeft, JointType.ShoulderRight, JointType.ShoulderCenter };
this._SkeletonGrid.Children.Add(CreateFigure(skeleton, userBrush, joints));
}));
_SkeletonGrid is the xaml Grid object, CreateFigure returns a polyline.
G'day,
I am attempting to simulate the old XBox 360 GUI with the sliding tabs (Remember, you'd press left or right and the content would slide in depending on the tab?) Anyways, at the moment, I have this working well, however I cannot get the "animation" working.
When the user presses left arrow or right arrow, my OpenWindow(int iIndex) method will be called, where iIndex is the index to the next or previous "window" to be slid in. (Not a window... each "Window" is a struct with a parent grid control containing a button and a smaller grid control that contains the windows contents.)
Now, my problem lies with resizing the parent grid control. When it is slid in, it is resized by calling mygrid.Width += 1; That works, but I don't see it happen over a determined period of time, it just lags a bit and then resizes to the required width. Whereas if I call this.Width += 1 in the same method, (this being the main program window) the window resizes how I want the grid control to resize. I've tried UpdateLayout() but to no avail. This tells me my timing is okay.
If anyone could be of assistance, it would be greatly appreciated.
Here is my OpenWindow method...
public void OpenWindow(int iIndex)
{
int iInterval = 1;
for (int i = (int)myDict[iIndex].Shell.Width; i < (int)stack_outter.Width; i += iInterval)
{
myDict[iIndex].Shell.Width += 1;
myDict[iIndex].Shell.UpdateLayout();
System.Threading.Thread.Sleep(1);
}
myDict[iIndex].Shell.Width = stack_outter.Width - (BUTTON_WIDTH * (myDict.Count - 1));
}
myDict is a Dictionary, Shell is the grid that I am attempting to animate when resizing. Sorry about the code, it's messy, my code is always hacked when I am trying to get stuff working :)
Thanks,
Ash
Neried Web Solutions
Your OpenWindow method is happening on the Dispatcher thread. That's also the thread responsible for rendering, so as long as your OpenWindow method doesn't return, nothing gets rendered.
The proper way to fix this would be to animate the Width property. I don't have any experience in starting animations from code (I've only used them in the past for things like a fade-in button highlight on mouse over, which is more easily done from WPF), but I took a quick read-through this page, Animation Overview on MSDN, and I think you'll want something like this:
DoubleAnimation myDoubleAnimation = new DoubleAnimation();
myDoubleAnimation.From = myDict[iIndex].Shell.Width;
myDoubleAnimation.To = stack_outter.Width;
myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(0.5));
myDoubleAnimation.AutoReverse = false;
myDoubleAnimation.RepeatBehavior = new RepeatBehavior(1.0);
myStoryboard = new Storyboard();
myStoryboard.Children.Add(myDoubleAnimation);
Storyboard.SetTarget(myDoubleAnimation, myDict[iIndex].Shell);
Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath(FrameworkElement.WidthProperty));
myStoryboard.Begin(myDict[iIndex].Shell);