问题
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).
回答1:
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 VisualBrush
es? 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/
来源:https://stackoverflow.com/questions/27871383/how-to-draw-1216-canvas-elements-without-hanging-application-in-wpf