I have integrated a feature in my tool that allows me to expand all recursively. I realise this would generally be a slow process anyway but currently. I have maybe 100 nested controls which are all expanded at once, so calling Expand All means that each control/datatemplate etc is processed at once.
This causes the tool to lock up for a good 10 seconds before responding. Of course, once the view hierarchy has been constructed, its then fast (I can collapse and expand instantly from then on). But it seems a little odd that there isn't a faster way to generate large forms, I wouldn't consider 100-500~ controls very many.
I have looked into virtualisation, but it doesn't appear to be useful to me in this case because all controls are expanded at once. Its fine if I expand them on a singular basis.
Edit:
Updates we're needed to the question to describe what the layout of my window is:
I have a number of TextBoxes, ComboBoxes, Sliders which are all nested inside of a number of Expanders. Each expander can contain N number of expanders, this is recursive to an extent. Depending on how the data is laid out. Some of these data types (which are represented by datatemplates) may contain StackPanels if needed and numerous grids for layouts.
To expand all it simply means to iterate each expander (which IsExpanded is bound to an IsExpanded property). I set this property on each data type (and its children) to true. And let the binding do the work of expanding everything. This causes every single control to be added at once.
Thanks
So after reading the edited question, I have a guess what's going on:
The UI is busy on rendering controls. And I assume there's not much data kept behind in viewmodels bound, so the data is not the point in your issue.
When the Expander control gets IsExpanded = true the first time, Xaml is parsed (object / resource parsing, not compiling) and visual tree children are created.
100-500 visual tree elements is a high number for rendering, because each of them (if it's belonging to the System.Windows.Controls namespace) consists of many sub-controls (Border, Grids, TextBox,..).
What you can achieve is NOT shortening the expanding time, but to unblock the UI:
advise the dispatcher to handle IsExpanded asynchronously and see how it behaves. This requires that you don't use the expanders default action:
In the arrow button click handler, skip default handling and insert this (IsExpanded is your bound viewmodel property here):
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(
() => { this.IsExpanded = true; } ));
Related
I am currently on the topic of creating a Combobox that expands with a full treeview where one can select a topic
It is for category selection.
The current approach just displays all "expanded" upfront with different margin depending on which level they are (utilized using itemcontainerstyle margin and a converter)
However, that is not what I want it to be as it tends to grows extremely large with larger category trees
Thus I am searching for other solutions
I could write a custom control but I also face issues in understanding how the selector controls actually get build
Thus my questions are:
Is there a way to get a combobox having a treeview for selecting instead of an itemscontrol?
If no, how to implement the selector properly (could also just derive from control and build it myself, but that would require more work and casting the control to selector etc. would not be possible --> some attached properties are not working then)
I've put together a scheduling application similar in style to that found in outlook, however it can show the schedules of multiple people. I have written a user control, basically a Border with gradient filled background & TextBlock. One of these controls are added to a Canvas at a set location for every appointment. The trouble is, I will have multiple users, with multiple appointments and may need to display 1000 or so appointments at a time. Initially, it takes an absolute age to instantiate all of these objects, however, I can live with this.
Unfortunately, the big problem arises when I try to scroll through the appointments. I have a couple of buttons to scroll left and right and upon clicking these, the UserControls' Left position are moved left or right a certain number of pixels - it can take several seconds between clicking a button and repainting(I also tried with labels just to test, but it was the same).
I guess the real question here is how to implement an interface, showing hundreds of controls with adequate performance and if this isn't achievable, how would I approach such an UI.
One possible option is a TextBlock CustomControl. You can get the exact same style as you have in your usercontrol but with a somewhat faster loading time.
Without a good, minimal, complete code example that reliably reproduces the problem, it will be difficult if not impossible to completely understand the performance problem you are having, never mind provide a solution.
That said, from your description it sounds like you are really looking to present the user with some type of ItemsControl, such as ListBox or ListView (a specialization of ListBox). In an ItemsControl, you can specify an ItemTemplate that defines how each item in the list will appear; this is analogous to the UserControl you apparently are using now.
I believe it's likely it will work fine just with that change alone. I.e. define your per-item visual as a DataTemplate instead of a UserControl, and set the ItemTemplate property of e.g. your ListBox to that template, then just bind your collection of appointment objects to the ListBox.ItemsSource property.
Note that the ListBox class already defaults to using VirtualizingStackPanel in its ItemsPanel template. So you should have no performance problems at all with this approach if you use ListBox.
If you want to use a different ItemsControl or create a custom one, you may or may not find that you need to use a virtualizing panel object explicitly (such as the VirtualizingStackPanel that ListBox uses). With just 1000 items in the list, even a non-virtualized panel may be fine, but if not then even when not using ListBox, you can always specify it explicitly.
This is a getting started question about how to create a reusable wpf slideshow control:
that displays a sequence of any visualizable elements e.g. a series of Image controls or a series of UserControls (should I target ContentControl, or is there a broader type that encompasses more visualizables/controls?)
the control should be able to accept an IList of some kind, which would be the elements/slides to present
the control should expose an Interval property that determines the duration of each slide, but i dont even know the basics of how to get started with that in terms of offering that property to be configured in xaml?
and what should the container be, if any, for the individual slides/controls that are passed in?
To start with, you should probably create a UserControl which contains an Image control, and perhaps Next/Previous Buttons, and anything else you may need. These would all be laid out as normal using a variety of panels, you could probably style most of it with just a Grid.
After that, your UserControl will implement the ImageSource (your IList, or IEnumerable of images), and your interval as dependency properties. These are then settable in XAML.
You would then write the logic which loads the next image and sets it as the Image's Source property, this could happen in the change event for the ImageSource property. You can then get as advanced as you wish with Image preloading/caching etc.
I've just delved into WPF myself for a "Slideshow" like project where I'm showing customer order numbers on screen for a period of time before showing the next, and using Effect/Transitions/Storyboards to move to the next frame. I found a good article on CodeProject
I used a Grid with 2 rows:
Contains my "Changing area".
Contains static information (logo, controls etc).
Rememeber to set "cliptobounds = true" on your changing area if you use any sort of transforms on it. (I know you said you aren't using transitions initially, but once people see it, they'll be asking).
Dependency properties are also easily built in C# if you just type propdp and hit tab.
I am currently working on a program that uses a fairly complex structure of nested winform controls which changes dynamically as a user makes certain selections. To go into more detail about the specific layout of the controls would be to extensive for this question.
When ever a selection is made, a lot of updates are made to the underlying model which is controlled by the user controls. This then results in series of corresponding changes in the size/position/visibility of the displayed controls. All of these changes results in a painfully intense flickering of controls on the screen. I need to somehow fix this so that everytime the user makes a selection the screen is basically frozen until all of the control updates have completed.
I have attempted to use the Control.SuspendLayout/Control.ResumeLayout methods in many different places and ways and I can not eliminate the crazy flickering. I thought that suspending layout on the root control during the changes would fix the problem but it appears that this SuspendLayout doesn't help when child controls are changed.
Do I need to use some other approach rather than SuspendLayout? Is there a way I can debug SuspendLayout to see why it doesn't appear to be cascading to all of the child controls?
Suspend/ResumeLayout isn't your problem here. That only suspends automatic layout, the kind that is triggered by the Anchor and Dock properties. Double-buffering can't fix your problem either, that only suppresses flicker in each individual control. Your real problem is that you are updating too many controls at the same time, each will take its turn to paint itself and that takes time.
What you need is a different kind of double-buffering, compositing. Check out if the solution in this thread solves your problem.
In addition to #tommieb75's suggestion for double buffering, you can try and see if your root level controls have BeginUpdate/EndUpdate method pairs. These should help repress the repaints in between the calls.
If you are using WinForms 2+ then you can just set the 'Control.DoubleBuffer' property on the control to true (in the designer even).
With framework 2, setting DoubleBuffered sets the 3 flags : OptimizedDoubleBuffer, AllPaintingInWmPaint, and and another that I forgot, 'UserPaint' perhaps.
Also, do look at the BeginUpdate/EndUpdate as mentioned by #yetapb.
Use the SetControlStyles in the user Control, the flag is OptimizedDoubleBuffer which will prevent the flickering.
I have a legacy application that is written in C# and it displays a very complex treeview with 10 to 20 thousand elements.
In the past I encountered a similar problem (but in C++) that i solved with the OWNERDATA capability offered by the Win32 API.
Is there a similar mechanism in C#?
EDIT: The plan is to optimize the creation time as well as browsing time. The method available through Win32 API is excellent in both of these cases as it reduce initialization time to nothing and the number of requests for elements are limited to only the ones visible at any one time.
Joshl: We are actually doing exactly what you suggest already, but we still need more efficiency.
One technique for improving performance is to load TreeNodes as the user expands the treeview. Normally a user will not require 20,000 nodes to be open on their screen at once. Only load the level that the user needs to see, along with whatever child information you need to properly display affordances to the user (expand icon if children exist, counts, icons, etc). As the user expands nodes, load children just in time.
Helpful hint from Keith: With the winforms TreeView you need to have at least one child node or it won't show the expand [+], but then you handle the TreeNodeExpanded event to remove that dummy node and populate the children.
In our main WinForm app, we have a treeview loaded all in one shot:
BeginUpdate()
Load 20.000 nodes
EndUpdate()
and so far the performance is still nice. It is actually one of the few components we are not replacing with third party ones.
The TreeView performance, in my experience, gets slow when you load nodes (in one shot, or on demand) without calling Begin/EndUpdate(), especially if your nodes are sorted, but if you call Begin/EndUpdate() correctly, you shouldn't really get performance issues related to the component itself.
I don't believe the .NET TreeView supports what you want, although this type of model is supported by .NET's DataGridView (see DataGridView's VirtualMode property). The TreeView will let you draw your own nodes but it won't let you populate them from some virtual store.
If possible, you might want to consider the use of a DataGridView for your application. If not, managing the nodes manually (like joshl mentions above) might work if you can get around some issues with refreshing the screen properly when nodes are expanded. Outside of that, you might want to check out some of the third party vendors, like this one (Divelements SandGrid), that might (emphasis on might) support your desired mode of operation.
NOTE: The SandGrid is not supported by Divelements as of the end of July 2013.
NOTE: This answer is invalidated by an edit by the questioner saying he already does this kind of thing, but I decided to still post it for future reference by others searching on this topic
When I've done similar things in the past, I've tended to opt for the naive lazy-loading style.
Use the TreeNode.Tag property to hold a reference that you can use to look-up the children
Use the TreeView.BeforeExpand event to populate the child nodes
Optionally use the TreeView.AfterCollapse event to remove them.
To get the [+]/[-] boxes to appear, the best way I've found is to create a singleton dummy TreeNode that gets added as a child to all unpopulated Nodes, and you check for its existence before populating with BeforeExpand.
There is one way to make the TreeView perform much better and that is to create all sub-nodes and hook them together and then add the nodes to the TreeView. If it's the graphical performance we are talking about.
TreeView tree = new TreeView();
TreeNode root = new TreeNode("Root");
PopulateRootNode(root); // Get all your data
tree.Nodes.Add(root);
Otherwise, load them node by node using OnTreeNodeExpanded.
For large data in Windows C# programming, whether it be in WPF or WinForms, I have traditionally added nodes dynamically. I load the initial tree root + children + grandchildren deep. When any node is expanded, I load the tree nodes that would represent the grandchildren of the expanding node, if any.
This pattern also works well with data retrieval. If you are truly loading data from a source of thousands or millions of records you probably don't want to load those all up front. No user wants to wait for that to be loaded, and there is not reason to load data that may never be viewed.
I have typically loaded the grandchildren or the great-grandchildren node data as needed on a background thread, then marshal those data back to the UI thread and create and add the nodes. This leaves the UI responsive. You can visually decorate tree nodes to indicate they are still loading for the case where a user gets ahead of your IO to the data store.
This works for me (CSharp):
Visible = false;
...
Visible = true;
In my case(2000 nodes), it takes only 1~2 seconds to load the tree, which is much more quicker than any other ways.
It might work well in C++.