I'm trying to get a combobox in winform that has around 5'000 entries. I've run into a problem before - addrange hangs with this many entries. I created a seperate control to do this for myself without lagging, but I'm adding this functionality to existing comboboxes.
I was looking up VirtualizingStackPanel for WPF when trying to see if I could get around this.
Is there a way to improve the performance of addrange for a couple of thousand string entries?
Have you tried using ComboBox.BeginUpdate and ComboxBox.EndUpdate? Using those methods improves performance when adding items.
Of course, if you have 5000 items then maybe using a ComboBox isn't the right control (having more than 200 items or so makes scrolling impossible, which defeats the point of having a drop-down selector). Have you considered using a normal textbox but with an autocomplete provider instead?
Related
I know this is a stupid question, I need to let you guys know that I am fully aware that it is useless in 99% of situations to make a listbox with this many elements in c#:
That being said I need this to be done...is there any way to populate a listbox with 40000 elements without it completely destroying performance/freezing up, thanks!
note: I have tried it, this is per the exact requirements of a professor...when adding 40000 elements through a DataSource and DataBind the application freezes up
You tell me.
for(i=0;i<40000;i++)
{
listBox1.Items.Add("click me");
}
Even if is possible (I never tried it), the usability for this form will be 0.
In that cases a more usable implementation is via lookup text-boxes and lists, where the user can enter a text to search record that matches this text and displays them in a any kind of list.
It is of course possible to do it, but not very practicable.
When using a desktop technology like WinForms or WPF, for a large number of items like this you are better off using something like an auto complete textbox, and have it set to filter/search after the user has typed two or three characters. In this case you can also use a control that offers scrolling virtualisation - this means that there is only a limited number of UI elements created in the scrolling portion of the dropdown, and those elements get reused when a scroll occurs. If you don't use virtualisation then a new element gets created for every list item that gets scrolled in to view. (Note that Silverlight controls have this functionality - just in case it's an option).
For ASP.NET though I would suggest that you do not want to do anything that would cause a large transfer of data (large items, or small items but lots of them) as it won't be performant. Instead you should look to do what Google does - retrieve search results in a paged fashion.
I have a ComboBox which when I repopulate it, it seems to be quite a time consuming task. After doing some profiling I have found that the majority of the time is spent in the ComboBox.Items.AddRange(Array) method. I have included an example method below which shows how I carryout the repopulating of the ComboBox.
public void Repopulate(IList<MyType> sortedList)
{
MyComboBox.BeginUpdate();
try
{
MyComboBox.Items.Clear();
MyComboBox.Items.AddRange(sortedList.ToArray());
}
finally
{
MyComboBox.EndUpdate();
}
}
sortedList contains around 280 items, and there are up to 53 ComboBox's which need to be repopulated. So it can take quite some time to repopulated all these controls (approx. 700ms on a high spec machine, 8000ms on a low spec machine) which is too slow for my requirements. I tried adding the sortedList to a new IList and that took around 1ms (on my high spec machine).
I need to get the repopulating of the ComboBox so that it takes significantly less time, ideally similar times to the IList but any performance increase would be good. So far I have been unable to find any way to improve the speed of the re-population.
Does anyone have any idea's on how I can reduce the time taken to re-populate the ComboBox?
Your problem could be that you have enabled the combobox's Sorted property. When this is enabled and you call AddRange the combobox sorts all those items, which if your items are already sorted, is unnecessary.
To prove my point, I created two combobox's that were populated using 10,000 sorted ints and AddRange. The only difference was that one combobox had the Sorted property enabled, the other one didn't. Here are the times in milliseconds of the resulting AddRange call
notSortedCombo: 5ms
sortedCombo: 1140ms
Could this be your problem? Could you have 53 comboboxes that have the sorted property enabled?
AddRange already calls BeginUpdate and EndUpdate under the hood, so you aren't gaining anything by calling it yourself.
This shaved a few milliseconds off of it for me:
public void Repopulate(IList<string> sortedList) {
comboBox1.BeginUpdate();
comboBox1.Items.Clear();
foreach (string item in sortedList) {
comboBox1.Items.Add(item);
}
comboBox1.EndUpdate();
}
The bigger problem is probably the design: 53 combo boxes is a lot of combo boxes to throw at a user — the user won't be able to interact with all 53 controls at once. You could get a little hacky and just populate the combo boxes with the visible value (1 item) and then populate the list as the control gets focused or in a background timer.
But consider reducing the number of controls on the screen. White space is considered a good thing.
You can improve performance by changing property FormattingEnabled of your combobox to False
The problem is that Visual Studio keeps it as True if you don't specify items "manually" in the designer, but add them programmatically.
Try, it solved issue for me
Regarding UI virtualization: Virtualization exists both for WPF (Example) and WinForms. But only some controls support it, like ListView, DataGridView, TreeView, etc. Those are the controls designed for bigger amounts of data. If possible you could switch to one of these controls.
Are all controls visible on screen at the same time? Maybe only updating the visible ones will help.
An alternative is to update the combo boxes asynchronously. If you're lucky enough to work with the new async/await stuff in .NET this is easily done. If you want to do it manually, you can just update a single combobox and schedule the update of the next combobox a few milliseconds in the future (use a timer or the TPL). This way the UI is at least still responsive while the update takes place.
Yet another way is to update the list only when the user focusses a certain ComboBox. There's usually no need to update the contents because the user will only see it when he uses the ComboBox.
You could also try to hide the combobox before you update its data. Or you could tell windows not to redraw the UI while modifying the contents (StackOverflow topic here). But I haven't tested whether this really improves the performance.
is there any way to make winforms listView showing data like treeView. i mean to make it verticaly oriented and to show columnHeaders one under another and not to put them in horizontal line?
thanx for assistance
Consider using this open-source ObjectListView.
It's a mature control that can show all sorts of lists, a tree-list and much much more (not my code - I'm just a happy user).
It take a few minutes to get used to the idea of using it, but once you got it - using it is a breeze.
Examples:
You can also consider using Better ListView. The usage is 99% same as the original .NET ListView, but it has many extras. For example, every item has a ChildItems property in which you can put further children.
I'm trying to accomplish what should be a very simple task using the ListView Control. All I am trying to do is add two Items to a ListView Control. One Item goes under the "Title" Column and the other Item goes under the "Status" Column. I have seen many examples for the ListView Control and none of them cover this particular need.
I've read the MSDN documentation on the ListView Control and find it rather odd they don't mention this... Or maybe I've overlooked it?
In ListView parlance, these are not separate items. It sounds like you want to add one item, and then what ListView calls a subitem. Assuming that Title is your first column and Status your second, you want:
ListViewItem myItem = listView.Items.Add("My Item's Title");
myItem.SubItems.Add("My Item's Status");
If you are just getting started with a ListView, you can save yourself a lot of time, headaches and frustration by using ObjectListView -- an open source wrapper around .NET WinForms ListView.
It solves many problems that you are going to find while trying to use a ListView, as well as just making it much easier to use. Just one example: it automatically handles sorting rows by clicking on column headers.
The desired functionality of the 'enhanced' combo box is a quick find method. Each item in the combobox has a ToString() method, such that they can be displayed in the drop down list. On clicking an item in the drop down list, the combobox's observers are notified of the selection.
Also, every time the typed text in the combobox changes, a list of "Candidates" is generated, made of all those items in the drop down list which contain the text typed in so far. Hitting enter takes you to the first candidate in that list, repeatedly hitting enter cycles you through the list.
I've implemented this functionality by deriving from ComboBox - I figured this made sense as I'm still functionally left with a combobox, it just has this "QuickFind" feature added. However, the logic to create the candidate list and cycle through it, whilst simple, isn't totally trivial, and would enjoy some testing.
However, as seen here it doesn't seem to be all that easy to test a ComboBox just by constructing it and prodding the extra routines I've added - it needs to exist on a form for it to behave the same way as it does in the application. This seems altogether too much effort to test a simple addition to a simple combo box!
Nothing in the code to cycle through the candidates is specific to my application however - I have created a general control that may be used in any number of contexts, the only requirement being that the objects in the combobox have a ToString() methiod, which is the same restriction placed on objects going into normal comboboxes anyway, and is guaranteed by C# .NET.
So, with testability in mind, where would you put the enhanced functionality?
As with the post you references: seperating the logic from the gui elements is also the solution here.
You should consider using a controller-like class which exposes a list of items which you can databind to your ComboBox' DataSource. The controller itself is responsible for maintaining this list.
So whenever you type a letter in the ComboBox you call a function on the controller, let's say UpdateList(string typedString). In this way you have seperated the logic of filling the list with 'candidates'.
Now you can easily write a number of tests which each call UpdateList() with different arguments and inspect the list of items afterwards. No GUI stuff needed to do the testing, you're only testing the algorithm itself.