I am developing an app I want to add some cool icons. Because I am using the beautiful MahApps library, I want to have a visual on the icons in MahApps.Metro/MahApps.Metro.Resources/Icons.xaml, so I did some string manipulations to grab the x:Key part of each <Canvas x:Key="appbar_3d_3ds" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0"> line. In short, all the string manipulations I did ended up with 1216 copies of the following:
<controls:Tile
Title="appbar_zune" Count="1215" Grid.Row="121" Grid.Column="15" TiltFactor="2" Width="1*" Height="1*" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Rectangle Margin="0" Fill="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}">
<Rectangle.OpacityMask>
<VisualBrush Stretch="Fill"
Visual="{StaticResource appbar_zune}" />
</Rectangle.OpacityMask>
</Rectangle>
</controls:Tile>
Note that each copy of the <control:Tile has the appropriate properties Count, Grid.Row and Grid.Column correctly set.
However, I always end up with the Application not responding window message. Now as I said, my motive is just to get a visual on that collection of pretty icons and instead I get a fat application crash. I just want to know if there is a way to display such a huge collection without crashing anybody's computer (Note: the system is really low on RAM: one of my test machines that run inside virtualbox).
First, make your Fill binding have Mode=OneWay. I bet you don't need it to be TwoWay and it may be the defalt in your setup. TwoWay bindings cost much more.
Second, consider using ever harsher version: Mode=OneTime. Since the icons are unlikely to change, you don't need any change tracking at all. This will save you even more resources.
In your case, First+Second will probably not give you a huge boost, but it's worth trying and remembering.
Third, how about your VisualBrushes? Do all of them use the same Visual="{StaticResource appbar_zune}"? Then why do you create thousands of instances? Instead of copy-pasting, create just one instance and make all items use that one instance. You may save much time and memory.
Fourth, and most important and usually giving the greatest speedup, is - you have tons of items. I bet you have some scrolling, horizontal or vertical. But how do you generate and display those pile of items? Creating them ALL at once is .. wasteful. They don't fit all on screen, right?
Do you have some ItemsControl that generate that thousand items? Investigate the ItemsPanel property of that ItemsControl and turn on virtualizing option on that panel. This will cause it to link to the scrollbar and it will start dynamically creating only those items that are on-screen and destroying items that moved off-screen. Well, I oversimplified it, but letssay it works like that. Note that containers like ListBox (and many others) is an ItemsControl too, so it also applies here.
Or maybe you have huge explicit XAML file with that thousand controls inside some StackPanel with no ItemsControl? That's not really wise. But oh well.. you can still turn on virtualization on that StackPanel.
Turning on virtualization usually is a good idea if you have more than few tens of items. It's often a must of you have a hundred, and it is a must if you reach thousands and more. However, virtualization costs: it very often resets/reinitializes items. If your ItemTemplate is really complex, virtualization may cause the scrolling to became "jaggy/laggy", I don't know how to express that in english, sorry. The compositor thread may simply not have enough time to recalculate and relayout all the fast moving items. If you hit that problem, try setting the Height of the Items to a unchanging truly fixed constant value. It helps greatly in speeding up the layout. but if your ItemTemplate is really wickedly complex, it may not help either. In such dead-end case your only option is ... redesign and to simplify the item template.
EDIT:
Of course all of that won't gain you anything if you don't have scrollbar and if you are trying to display a ton of items at once. In this case, strive to simplify or remove Bindings, Templates, component nesting (sometimes it's better to calculate positions manually than using three embeded Grids), use rendering cache or (...).. Sorry, I start making too many guesses, too many options..
EDIT:
I just noticed Width="1*" and Stretch, so you probably have a Grid at the top, not StackPanel. Since you want them equally-sized, the UniformGrid may have better performance. Also, with some work you can add virtualizing to the Grids, too:
from 4.5 and up, it's much easier - article: WPF 4.5 new virtualizing features
below, it requires more work, see Dan Crevier's 4-part series blog:
One: http://blogs.msdn.com/dancre/archive/2006/02/06/implementing-a-virtualized-panel-in-wpf-avalon.aspx
Two: http://blogs.msdn.com/dancre/archive/2006/02/13/531550.aspx
Three: http://blogs.msdn.com/dancre/archive/2006/02/14/532333.aspx
Four: http://blogs.msdn.com/dancre/archive/2006/02/16/implementing-a-virtualizingpanel-part-4-the-goods.aspx
If virtualizing the Grid is not enough, try moving to Canvas instead and force some width/height/positions manually. Removing automatic layouting sometimes saves a lot. Then, you can use VirtualizedCanvas and if you really put there constant-sized items, you'll probably get it as fast as possible. But that's a last-resort option. Things mentioned earlier should work well.
Oh, and one last word about virtualizing: remember that when ScrollView is working in virtualizing mode, then the Position is not counted in pixels/points anymore. In v-mode, the Position of the scrollbar is counted in items, that is, position=2.5 means that the scroller is at halfway through the third item (2 items passed and a half more), not at pos=2.5 "pixels".
Side note: "million point canvas": https://blogs.msdn.microsoft.com/kaelr/2010/08/11/zoomableapplication2-a-million-items/
Related
Here's the scenario:
I've got a series of many views primarily containing listboxes. Listboxes, some with large amounts of data in them, some with very little.
Currently, I've got an attached behaviour that essentially just detects when new content has been loaded into the container for these views (a ContentPresenter) and triggers a simple XAML storyboard that offsets the container by 200px to the left, or to the right, and also lowers the opacity to 0% (at 0ms) then over 100ms it restores the opacity to 100% and reduces the offset back to 0px, creating a 'sliding into view' sort of effect.
Now, the issue:
The framerate of this animation varies horribly anywhere from a smooth transition to jittery, to outright just lagging at 1 frame until completion.
This is almost certainly due to the fact it's having to load the new view, render everything in it, then even further; lists with loads of data in them need even more render time, THEN whilst all that's happening it's trying to animate its location changing which seems to drastically affect the performance of the animation.
it shocks me that something as simple as moving some pixels across the screen is so graphically intensive for WPF to handle
Here's a crude representation of the animation itself:
My question:
Is there any sort of fundamental best-practice for dealing with intensive animations in WPF so as to improve their performance, or any kinds of small changes to things that when added up boost performance?
Or is WPF just terrible at animating and we just have to deal with it.
I think your problem is, that you try to do everything at the same time or same thread. Just try to do it async in splitted tasks. There are plenty of different approaches to that. Another approach would be to just handle the work with a semaphore, this should push your performance a bit up.
I've come up with a solution which, in my case, has always solved the jitter problem. It is certainly worth trying. The strategy is to take any element that will not change throughout the animation, insert it in a local reference framework such as a grid or canvas, make that grid or canvas store cached versions of its content, and then apply your animation to the grid or canvas, rather than the inside elements. It is as simple as this:
<Canvas ...Your canvas properties...>
<Canvas.CacheMode>
<BitmapCache />
</Canvas.CacheMode>
...Your UI elements...
</Canvas>
or, for a grid:
<Grid ...Your grid properties...>
<Grid.CacheMode>
<BitmapCache />
</Grid.CacheMode>
...Your UI elements...
</Grid>
You'll want to simply ensure that you do not update anything inside the canvas (or grid), as this will undo the cached version and you'll find yourself with a similar issue where WPF will regenerate its rendering on every frame.
By having cached versions of the content, what WPF will be moving across the screen is not a dynamically updated element, it is simply a bitmap of the last render of that element, and WPF will update that bitmap only if changes are applied to the element.
If that does not solve your issues, it means that performance is hindered by something occurring outside the generation of the local element. This means you'll want to look at your overall application, or reduce the entire frame rate of your application. However, in virtually all cases of jittering and jerky movements I found for my WPF animations, the BitmapCache solution seems to work all the time. Just be careful in not applying animations to objects inside the element being cached.
I'm transitioning to using WPF after using WinForms for 6+ years. Previously, using On_Paint, and drawing with Graphics and Brushes and the like, I've created custom UI elements like the below:
I'd like to duplicate this look using WPF but I'm not sure where to begin or how to do it. Thanks for any help.
The basic answer is that you need to override the ControlTemplate for the button you are styling (do this in a resource for code reuse!).
The basic format would be:
<Window.Resources>
<Style TargetType="Button" Key="MyAwesomeButtonStyle">
... Bunch of stuff including control template
</Style>
</Window.Resources>
<Grid>
<Button Style="{StaticResource MyAwesomeButtonStyle}"/>
</Grid>
To get a starting point for what the existing control template looks at, look at MSDN. For a button specifically, look at Button Styles and Templates (you can get there from the first link by scrolling down a little and clicking the "Button Styles and Templates" link).
To match your picture, you will likely need to replace the wrapping Border element with as simple Grid and add a Path to get the parallelogram shape. RadialGradientBrush can be used for the background of the path to get the color effect.
Without a more narrow question, its hard to say specifically what you need (without taking the time to just figure out the template), but my advice would be to put the existing control template into your program and use it as an override for one of your buttons. Then modify pieces to learn what they do/affect. Finally, modify the correct pieces to look like you want.
Basically, trial and error is probably your best bet as far as learning how to modify/write these are concerned.
When delving into WPF, it's best to forget everything you know about Win Forms and just pretend you're learning how to develop UIs for the first time. Pretty much everything is different, so expect a good year or so of study and work before it really starts to gel.
You can do a lot with Control Templates, though your particular controls are complex enough in appearance that hard-coding the Paths for the borders may be a bit awkward. You can get something close to the old GDI way of doing things using DrawingVisual, which lets you define vector graphics in code.
Note that you even if you did use a DrawingVisual to get the particular look you're after, you'd still probably want to use it inside a ControlTemplate for Button. This way you can take advantage of all the code that's already been written for Button and anybody using your template doesn't have to change out their Buttons for custom controls; all they have to do is apply your template to change how it looks.
Again, this is a deep and complex topic, so expect to do a lot of reading/tinkering before you have all of the concepts down. Best of luck and happy coding; WPF is well worth the pain of entry.
I'm facing a big problem with optimizing rendering a lot of custom controls. I'm developing a system that will be diplaying thousand custom controls and update several values of these controls every second or frequently. There will be also support for zooming and panning. But I found out that rendering(on canvas) takes a lot of time, zooming and panning is laggy.
Does anyone have an experience with this kind of wpf application?
In my experience this is almost always due to inadvertently disabling virtualization and container recycling, usually through styling/templating. My first step would be to examine the WPF Tree Visualizer at run-time and make sure you're only drawing the elements that are actually visible on-screen.
You can use BitmapCache to disable rerendering controls that have not changed.
In my experience this greatly improves zooming and panning performance.
Enable it for the more complex controls.
Example:
<Rectangle CacheMode="BitmapCache" />
<Rectangle>
<Rectangle.CacheMode>
<BitmapCache/>
</Rectangle.CacheMode>
</Rectangle>
Details here
Use 2 canvases that are the same size and at the same position (a top canvas overlaying a bottom canvas).
Bottom Canvas: displays all controls. This is a static image (cached) which might get refreshed every 30 seconds or so.
Top Canvas: displays every control that has changed in the last 30 seconds.
The 2 canvases are exactly aligned so every recently-changed control on the top canvas will exactly obscure its out-of-date twin on the bottom canvas.
This way you only have to draw the few controls that have changed rather than all thousand controls.
I've started exploring the Data and UI virtualization features in WPF recently and stumbled upon something strange.
I created a DataGrid's with Virtualizing enabled and filled it with a list with 1,000,000 items. This works great and amazingly fast.
<Grid>
<DataGrid x:Name="employees" VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsContainerVirtualizable="True"
VirtualizingPanel.VirtualizationMode="Recycling"/>
</Grid>
However, when I nested it under a StackPanel it loads for over a minute until I get OutOfMemoryException. I've set the same VirtualizingPanel properties on the StackPanel as well but it doesn't help.
Is this behaviour intentional or am I missing something basic here? And how can I manage to support data virtualization in nested controls?
a StackPanel is an "infinite container" (notice the quotes), in the sense that it does NOT impose a limit in the size of its children, like a Grid or DockPanel does.
What this means in terms of UI virtualization is that, since your DataGrid is not limited in Height, it will grow endlessly and render all it's items, effectively losing UI virtualization.
See MSDN: WPF Layout for more details.
The bottom line is that you need make sure you use the appropiate layout containers, depending on your needs.
How do you code that "loading" or "waiting" graphic in WPF?
If someone had a page that requires loading a lot information, typically on a flash web site, you see a circle spinning of some sort that universally means that there is a lot of data that is being loaded and the user needs to wait.
This is more than just a nice effect. It has real world application and importance. If someone opens a form or page or window that does not have this graphic and instead sees a form that should have information displayed but instead sees blank fields, his first notiion might rightly be that there is no information to be seen. This could be true even if the cursor turns to a waiting mode. The vast fields of emptiness will be more present and overpowering of an image than a cursor that might be in some sort of spinning state.
I suppose I could throw something together. I suppose I could present a gif of a rotating circle that is shown at the beginning and then hidden once all the data loading methods have been called. But maybe there is some sort of shared common graphic to use for free and without knowing exactly what this thing is called, I do not know what name or phrase to use to search for one. What are they called?
Also, maybe there is something already build into WPF or even MVVM that uses this. Is there? Also, maybe someone has worked out a way to make the whole form or page gray and inactive while displaying a rotating circle and has been kind enough to share it. Is this true?
As I see it, you're sort of asking a bunch of different questions here. So, though I normally dislike the piecemeal reply, here it is:
But maybe there is some sort of shared
common graphic to use for free and
without knowing exactly what this
thing is called, I do not know what
name or phrase to use to search for
one. What are they called?
I'm sure there are royalty-free images on the web that you could use. I would probably search for "busy cursor" or even "hourglass", since, up until recently, that's how the major operating systems rendered them.
Also, maybe there is something already
build into WPF or WCF or even MVVM
that uses this. Is there?
First, we'd better get clear on our terms:
MVVM does not have anything "built into" it, because it's a programming pattern, not a framework. There are plenty of implementations of it, but I don't know if any contain specific graphical elements. Could be!
WCF is Windows Communication Foundation, and its libraries almost certainly don't contain any graphical elements.
WPF is Windows Presentation Foundation, and that's where you will find (or define your own) graphical elements. It does contain a progress bar, but although you can customize the look of any WPF control, it's really meant to show progress from a starting point to an endpoint. That might not be what you want. WPF can certainly display animated images, though. Here's an example from SO for displaying animated GIFs.
Also, maybe someone has worked out a
way to make the whole form or page
gray and inactive while displaying a
rotating circle and has been kind
enough to share it. Is this true?
Almost certainly. Again, there are numerous approaches you could take here. Some of them are mentioned in this question, for example. Searching for "overlays" or "splash screens" could point you in the right direction. In short (in WPF):
Make the page "inactive" by setting its controls' Inactive properties and / or trapping Preview(Key|Mouse)Down events
"Gray out" the page by displaying a translucent element, such as a Grid or Rectangle over top of it. Depending on how you do this, it might handle making the underlying controls "inactive", as well (don't quote me on this, but I believe an element like a Rectangle will intercept mouse clicks, so the user can't click on controls underneath it...)
Use a background worker, and for a circular progress bar in wpf, see http://www.codeproject.com/Articles/49853/Better-WPF-Circular-Progress-Bar.aspx for a very good one.
BackGroundWorker thread is your friend.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
I am not sure, just an idea. Show a transparent modal window that contains spinning circle until the data is loaded by the background thread. When the data is loaded. close the modal window.
You could simply use attached properties
Here's a post with complete sample code (just a couple of classes)
Your main WPF Windows would simply look something like this:
<Window.Resources>
<CollectionViewSource x:Key="DataList" Source="{Binding TestData}" />
</Window.Resources>
<Grid Background="AliceBlue" app:AsyncNotifier.Trigger="{Binding IsDataLoading}"
app:AsyncNotifier.SpinnerText="Loading...">
<TabControl Grid.RowSpan="2">
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding
Source={StaticResource DataList}}" />
<Button Content="Do Update" HorizontalAlignment="Left" Command="{Binding LoadData}"
VerticalAlignment="Top" Width="75" Grid.Row="1" Margin="0,5" />
</Grid>
</TabItem>
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</TabItem>
</TabControl>
</Grid>