ComboBox.Items.AddRange Performance - c#

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.

Related

massive string array (40000) elements in listbox

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.

Why virtualized ListBox requests old items

I have my own DefferedList:IList class. I overrode
Count, IndexOf, this[]
members and put
Debug.WriteLine("Get " + index);
into this[int index].
Then I binded instance of this class to ListBox.ItemSource.
Everything is fine but UI virtualization works a bit strange for me. I run application. ListBox is populated by first 17 items (from 0 to 17). Then I jump to 200th item
TheList.ScrollIntoView(Item200);
ListBox requests 200th item, then again items from 0 to 16 and finally 184-209.
So why does it request again 0-16 items while they are not visible anymore? It affects performance of my data virtualization very badly.
How to avoid it?
I have some really bad experience with numerous hours wasted in productivity over using custom virtualization of lists used in a standard ListBox. I would advise against using custom lists and use an ObservableCollection instead. I think ListBox was probably mostly tested with an ObservableCollection and any tricks you try to make to avoid populating the entire list are likely to backfire since the ListBox is not really as flexible as you would expect based on the fact that ItemsSource is just an IEnumerable. Actually based on that expectation - you could assume that it needs to enumerate all items between 0 and 200 if you want to display item 200.
If you have a few hundred items in a list - you might want to simplify the data model for displaying them, so you populate an ObservableCollection with all the items from the beginning and only load the item data when the contents of the items are requested and not already available. If that is too much - you could add a small delay before loading the data and check if the collection item view is visible on screen after that delay before you start loading.
Another thing that is supposedly helpful is if your ItemTemplate is of predefined dimensions.

wpf listbox with < 10k entries... how do i make it effiecient?

iphone has 10 listbox entries at a time, it doesnt' load all 10k entries and scroll them all at the same time. it adds one entry, and subtracts one entry at a time. so how do you do this in a wpf listbox? how do i make, say 20 listbox entries, and add one or two, and subtract the same amount? does data binding the listbox take care of this performance issue?
What you are looking for is the VirtualizingStackPanel. This control will only display the items you actually need on screen, even if you have thousands more that are available to the item. Here is the MSDN article about it:
http://msdn.microsoft.com/en-us/library/system.windows.controls.virtualizingstackpanel.aspx
Here is a small article about this control that explains it simply:
http://windowsclient.net/blogs/microsoft_kc/archive/2009/05/13/what-is-virtualizing-stackpanel.aspx
One key point in this second article is that you should use the Recycling property to make sure you use the least amount of resources like so:
VirtualizingStackPanel.VirtualizationMode="Recycling"
The ListBox WPF control uses a VirtualizingStackPanel by default, unless you change the ScrollViewer.CanContentScroll property false, in which case you get smooth scrolling in exchange for a loss of virtualization.

WPF: listview display recently added items on top instead of bottom

I got a collection bind to a listview. This collection gets items added every 4-5 seconds and new items will automatically be added in bottom of the listview. So if you gonna see newest items then you need to scroll down to the bottom.
My question is:
is it possible to like reverse the listview so the new newest items are on top and oldest items in bottom ?
Thanks
Use Insert instead of Add :
collection.Insert(0, newItem);
Note that it's slower than Add since it has to shift all items by 1 position. It might be an issue if the list is very big.
Even though you already voted the other answer as accepted, as they pointed out, while that is a simple, one-line solution, that does a lot of shifting and such which can affect performance.
IMHO, a better, correct way to do this would be to use sorting. It's easy to get the default view of a collection, then apply sorting to it, or just use a CollectionViewSource and apply sorting there. Of course that means you have to have a field to sort on, but that could be as simple as adding an index property if you don't already have a time-stamp or something similar.
Let me know if you want a code example here and I'll provide one.
Two approaches:
1) Use CollectionChange event to capture any item-change in the list.
Then sort the list in descending order and then render.
// subscribe to CollectionChanged event of the ObservableCollection
_recordingsHidden.CollectionChanged += RecordingsHiddenOnCollectionChanged;
private void RecordingsHiddenOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
var view = (CollectionView)CollectionViewSource.GetDefaultView(lvCallHistory.ItemsSource);
view.SortDescriptions.Add(new SortDescription("StartTime", ListSortDirection.Descending));
}
Here 'lvCallHistory' is my list view and sorting is done based on 'StartTime'.
This approach is effective only if the list contains less items. Because sorting is a costly operation and if you update the list frequently, then it might affect the performance of the application. The user may experience 2-3 seconds lag each time the view is rendered.
2) Avoid sorting: Every time you update the list, point the scroll bar to the last added item. (Pros: Very effective and no performance issue. Cons: Items still added to the end of list, even though scroll bar takes you to the new item whenever a new entry comes.)
lvCallHistory.SelectedItem = <your_collection>.Last();
lvCallHistory.ScrollIntoView(lvCallHistory.SelectedItem);
Set sorting order to descending and use add items
this.listView1.Sorting = System.Windows.Forms.SortOrder.Descending;
listView1.Items.Add( listviewitem);
or remove sorting and insert item
listView1.Items.Insert(0, listviewitem);
either of them will work..

WPF Datagrid Lazy load

Details
VS-2008 Professional SP1
Version .net 3.5
Language:C#
I have a WPF Datagrid which loads from Linq-sql query Datacontext data item.The result set contains around 200k rows and its it very slow loading them,sorting,filtering etc.
What is the simple and easy way to improve the speed?
Couple of things I saw searching are
Scrollview,Data virtualization etc people also talk about Paging,Profiling etc
Loading data: 200k rows is a lot of data that no one (user) wants to see in one place. It will definitely reduce your UI user experience. So your best bet is to filter your data just to reduce the amount of it (for example do not show closed orders, just show the open ones). If you can't do so, you should use Virtualization. I didn't see any applications that use pagination in order to show data (Of course except in web). Most of the time it isn't such a good approach. But if you are talking about a type of data that is like search engines results you must use it. But keep in mind that most users won't exceed page 10 in search engines results.
Filtering: I would suggest doing it on your server side for such a huge amount of data (SQL Server here), or as I said first filter the whole 200k to reduce the amount on server side and then filter it (for user) in order to find something, on the client side. You might also find the following link helpful:
http://www.codeproject.com/KB/WPF/DataGridFilterLibrary.aspx
Sorting: Again I would suggest server-client solution but you might also find following links helpful:
http://blogs.msdn.com/b/jgoldb/archive/2008/08/26/improving-microsoft-datagrid-ctp-sorting-performance.aspx
http://blogs.msdn.com/b/jgoldb/archive/2008/08/28/improving-microsoft-datagrid-ctp-sorting-performance-part-2.aspx
http://blogs.msdn.com/b/jgoldb/archive/2008/10/30/improving-microsoft-datagrid-sorting-performance-part-3.aspx
Many people don't use default SortMemberPath of WPF datagrid just because it uses reflection on every single record and this will highly reduce the performance of the sorting process.
Hosein
Here is a very good sample of Data Virtualization (Not UI Virtualization):
http://www.codeproject.com/KB/WPF/WpfDataVirtualization.aspx
Althogh it doesn't support the LINQ IQueryable objects directly but you can use this sample as it is. Of course I'm now wokring to improve it to work with IQueryable objects directly. I think it's not so hard.
Wow, 200K rows is a lot of data. Paging sounds like a good idea. Try to decide how many rows per page you want, say 50. Upon showing the screen the first time, show only the first 50. Then give the user the option to move between pages.
Sorting might be trickier this way though.
Virtualization can be another option, sadly, I have yet to work with virtualization.
Sometimes you may have only ~30 visible rows to load and if those rows + whatever columns are expensive to load due to their number and complexity of the each cell (it's template, or how many wpf elements it has), none of the above comments really make a difference. Each row will take it's sweet time to load!
What helps is to stagger or lazily load each row on the UI, so that the user sees that the ui is doing something rather than just freezing for ~10+ seconds..
For simplicity, assuming that the datagrid ItemSource="{Binding Rows}", and Rows is IEnumerable, where Row is some class you created : add a property IsVisible to Row (don't forget to raise property changed, of course)
you could do something like this:
private void OnFirstTimeLoad()
{
Task.Factory.StartNew(() =>
{
foreach (var row in ViewModel.Rows)
{
/*this is all you really need,
note: since you're on the background thread, make sure IsVisible executes on the UI thread, my utils method does just that*/
myUtils.ExecuteOnUiThread(() => row.IsVisible = true);
/*optional tweak:
this just forces Ui to refresh so each row repaint staggers nicely*/
Application.Current.Dispatcher
.Invoke(DispatcherPriority.Background, (SendOrPostCallback) delegate { }, null);
}
});
}
oh, and don't forget to trigger in XAML:
<DataGrid.ItemContainerStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Visibility" Value="{Binding Path=IsVisible, Converter={StaticResource BoolToVisibility}}"/>
........
Is your datagrid inside a Scrollviewer? Because it does the entire datagrid (all the rows) to be rendered. I had a similar problem and removing the scrollviewer solved the problem with the slow loading.
Question that you should be asking is:
Are users groing to look through 200K rows of data?
How much data is too much for the users? May be alert the user that the query returned too many rows and you are listing the first 1000
Is it worth your time & money to program paging, Data Virtualization etc if users do not look beyond the first 1000 rows.

Categories