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.
Related
With a grid, i can say that i want one element to take up 3/4 of the page and one to take up 1/3 of the page by using width = "3*" and width = "1*"
Is it possible to do the same with Stackpanels? Or is the only way to do it by placing the stackpanels inside a grid?
In the .NET Framework, there are a number of different Panel classes. Some of them like the Grid and DockPanel provide resizing capabilities that resize the Panel children, while others don't. Generally, the ones that provide this functionality have a cost in terms of CPU and/or RAM.
Therefore, when requiring a Panel for simple layout purposes, these Panel types should be avoided and a simple StackPanel should be used instead. Other times, we require this extra functionality and so we should use one of these more expensive Panels.
Please see the Panels Overview page on MSDN for a much fuller description of the .NET Panels.
So, to actually answer your question(s), no, a StackPanel cannot use similar sizing capabilities as the Grid and you can't do what you want even if you place StackPanels inside a Grid because although the Grid's cells may change size when the parent is resized, the StackPanels contained within will not.
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/
I have a requirement to create a "super grid" as part of an application, which will consist of multiple frozen and scrolling panes. The grid also has the requirements that it must support the following features:
Display a datagrid with 100s of rows with “reasonable” scrolling performance
Group by a certain field, collapse/expand grouped
Display any control inside a field, e.g. images, text, combobox, button
Allow buttons and editing of fields as well as read-only mode
Allow multiple vertical split panes (or workaround, multiple synchronised grids)
User column re-ordering with persistence
User column sorting ascending/descending
Dynamic insertion/deletion of columns (column choosing)
4th dimension below grouping – RowDetails template on selected row
Allow Flashing / animated cell/row templates on value update
My initial feasibility suggests that the WPF Datagrid is capable of almost all the requirements above (with heavy templating), however the multiple vertical split panes may not be covered. Below is a diagram of what I'm trying to achieve:
I would like to enquire if anyone has done this before and if so, what grid technology you have used.
Can the above be done in WPF Datagrid? I am aware of Frozen columns however this appears to apply to freezing the first N columns only
Can the above be achieved with multiple grids and synchronizing the Vertical scroll positions?
If so, will this solution still be virtualized?
Are there any third party grids you can recommend to achieve this instead? Please note we will be heavily styling the grid according to in-house branding so it must be flexible and Blendable
Any comments/suggestions appreciated.
Update - Feasibility Investigation Results
I have a working prototype for this using 3x grids and collapsable grid splitters between them.
The scrollviewers are synchronized using the answer from this question.
Selected row synchronized across three grids by binding to a common SelectedIndex property with TwoWay binding.
I will be working on grouping and rowdetails templates next... Thanks to answerers for your points! :)
I've implemented most of your requirements and more with the devexpress wpf grid control. It's not free and there's a bit of a learning curve (their documentation is essential), but it'll save you so much time in the long run...
I've done this with a standard DataGrid with perfect performance and ultimate flexibility, but it requires a sound architecture. Both data and UI need to be virtualized - no control can annul the cost of sorting a large set of data (for example), so if that sorting is happening on the UI thread, the user will notice. If your back-end supports paging and filters, your front-end can virtualize and the user will never know that only 100 rows have been loaded out of their 10000. As they scroll, pages of data are loaded & unloaded in the background and the UI never ceases to be responsive.
However, knowing where you work leads me to suspect that your front-end is the real workhorse...
I have a tree- or menu-like data structure which I want to display in a Panel (specifically a self-made PolarPanel), but all items must be VisualChildren of the same panel.
If I define a HierarchicalDataTemplate, I need to create an ItemsControls and specify it as IsItemsHost, or am I mistaken here? Can I somehow reference the parent's ItemsHost?
<HierarchicalDataTemplate
DataType="{x:Type local:ItemsCollection}"
ItemsSource="{Binding Children}"
>
<!--/* "virtual" ItemsHost referencing the parent's ItemsHost? */-->
</HierarchicalDataTemplate>
(My first workaround was to have a GetAllTheChildren property in the root node which returns all children and all children of the SelectedChild, recursively. This doesn't easily work together with ObservableCollection, because changes have to bubble up to the root to refresh GetAllTheChildren again (which reloads more items than would be neccessary).)
I know I can do it in code-behind by creating and drawing the items' Views into the same parent, but I'm searching for a nice WPF, indirect, automagic way.
EDIT: I am now using an improved workaround, where all ItemControls / Panels are drawn on top of each other. This preserves the hierarchical aspect, which makes this a much better solution than the one mentioned above (with GetAllTheChildren). But it still doesn't answer my question...
This is what it looks like graphically:
Note how the hierarchical elements have to be arranged in the same space.
Is there a clean and simple solution to this?
I must admit that the graphic result is very attractive, even I wouldn't know how to use that kind of control!
To answer to your question, I never tried to share the same panel as host, but I don't think would be possible. That would break the visual tree hierarchy schema. Any visual element cannot have more than a parent.
Assuming that every hierarchy level maps his own ring, I surely bet on the classical way to create such a composite UI. There's no doubt about the convenience of using the HierarchicalTemplates and ItemsControls, even it could be a complex task.
Moreover, I don't understand whether your goal is to have some kind of collapse, such an ordinary tree-view. In this case, avoiding the classical WPF approach would be a nightmare!
My solution is to create a custom panel, being able to arrange their children on a ring.
That's not all. It must be created both a custom Control and an ItemsControl. The first one will represent the ring-slice that has to be rendered, and the other just as the generator.
The control acting as item holder will have two properties: angle and radius. The thickness can be set apart.
The panel, calling MeasureOverride, must take in account the ring nesting, and set both the angle and radius of their children accordingly.
From my viewpoint, the holding control should derive from a ContentControl. That because you need to render the shape of the slice, and that task should be done by this control. Any particular embedded control (text, buttons, etc) can be placed inside as content.
Of course, all that is brainstorming. I'm not guarantee that is the best solution, nor that would work as is.
Hope it helps.
Cheers
I have been using WinForms since the first framework introduced and invested a lot of time and effort in it. Now I am attempting to move to WPF and, honestly, it's not so easy.
Now I have a task, I need to implement a simple wizard, each page of which has a aligned to center group of controls. The group contains a set of buttons, four button in a row. Width of the group is constant, height is variable -- it depends on the number of buttons inside.
It's a simple task for WinForms, but I have no idea how to do it using XAML.
I have three questions:
1). Obviously, the buttons inside a group is a WrapPanel which is placed in a Grid's cell. It's simple. But how to calculate height of the WrapPanel not using code behind?
2). Which is recommended way to implement wizard? Data template or some kind of Tab Control? I probably will need to have some transition effects when switching pages.
3). Is it acceptable in WPF world to use binding as a way to repositioning controls?
Thank you in advance!
The WrapPanel will auto-adjust its height based on its contents by default. WPF is a big advancement from WinForms precisely because of the new layout paradigms. No code behind is needed for anything you've mentioned.
As for 2; there are a lot of ways to implement this, depending on how close you adhere to MVVM (if at all); I'd recommend using a styled TabControl at first (you can adjust the style to present visually the steps in the wizard as tabs, without letting the user jump between tabs), as it's easiest. Also, it's possible to bind pretty much everything to the TabControl.
3 is possible, but should be rarely needed. And I mean it.
Now then; a simple example to show you the power of WPF.
You can have in your ViewModel (if you're not familiar with MVVM google it or read any of Josh Smith's articles or book or... wow there's such a wealth of information on it I don't know which to choose) a collection of objects, let's say Step classes, which you can bind to the ItemsSource of the TabControl. The TabControl will automatically create a tab for each of your Step.
Inside your Step class, you can have a collection of items, let's say... um, Collection<Push> (I'm struggling not to use known classes like Action or Button). If the ItemTemplate of the TabControl contains anything that recognizes that collection, as in a ListBox styled internally to use a WrapPanel for its ItemsContainer, you're done: the template will show the list of Pushes in a WrapPanel and that's that.
Now, I probably shouldn't write a full tutorial here anyway, but that should get you started. Read up on DataTemplates, ItemsControl etc. (again, I'm having difficulties picking resources... Dr. WPF has awesome articles on both, but they might be a bit advanced) and you should be good to go. Just remember that there's a good reason why WPF features a lot more fluid layouts than any previous desktop technology, you should become familiar with that.