I've made a custom control, it's a FlowLayoutPanel, into which I put a bunch of other custom controls (just Buttons, each with three Labels and a PictureBox overlayed)
It works ok with around 100 buttons, but bump that up to 1000 and it's in trouble. Bump that up to 5000 and it just dies after 20 seconds.
I have very little custom code, and I make judicious use of suspend and resume layout.
So what am I doing wrong? I'm sure my (fairly rapid) computer should be able to handle a few thousand buttons and labels.
(I'm fairly new to C# GUI stuff, so perhaps I should be doing things completely different anyway.)
Edit 1:
This is pretty much the only custom code at the moment:
flowLayoutPanel1.SuspendLayout();
foreach (DataRow row in dt.Rows) // dt is from a DB query
{
flowLayoutPanel1.Controls.Add(new PersonButton(row));
}
flowLayoutPanel1.ResumeLayout();
and in the PersonButton constructor:
this.label1.Text = row["FirstName"].ToString().Trim() + " "
+ row["Surname"].ToString().Trim();
(There should also be a picture attached but I'm not sure if anyone can see it.)
Edit 2:
I guess I really should be using a DataGridView, or ListView, but I wanted more than just a line of text and a small icon per row; I wanted it to look similar to the downloads view in firefox (Ctrl + J). (See screenshot)
Thanks very much for all your input, BTW. I guess I'll have to rethink...
alt text http://img156.imageshack.us/img156/1057/capture.png
Can a C# WinForm app handle 1000 instances of any type of control? I am no WinForm Guru, but what you are expecting from your application might be unreasonable.
The fact that you want to show 1000+ controls of any type might be a sign that you are approaching the design of your software from the wrong direction.
You'll have to post some of the layout code or we won't be able to help much/at all.
Also, your #1 best bet is to profile your code. Profiling is the only surefire way to find out what exactly is performing slowly in your code. In my experience, this is especially true of UI code.
At 1000+ buttons your probably running perilously low on GDI resources and/or raw handles for your application.
Not sure what your application is supposed to do but a grid or a combo box may be the better option here.
The layout logic required for 5k controls will be too much for any type of wrapping panel. You might want to look into a different type of control designed the hold thousands of entries - something like a DataGridView.
The DataGridView has several different column types available that should work for the type of data you're displaying (images, buttons, labels). Since your database query looks to return a DataTable, you could simply bind that directly to your DataGridView and remove the loop.
It sounds like you seriously need to rethink your interface.
Like others have mentioned, that number of controls on a form will not be usable.
However I have done some experimenting looking at time to create new controls in code, even using reflection, and found that several hundred data-bound controls created on the fly in a flow-layout panel should be created in 1 to 2 seconds.
Posting more code samples may help get a better answer.
More info: I have just rerun my timing test again, 300 controls took 0.5 seconds, 400 took 1.9 seconds, 600 took 3 seconds, 1000 took 6 seconds.
It seems that there is a limit somewhere between 300 and 400 where resources start to get over utilised.
[Necromantic mode = ON]
You have 1000 row of data, but you only can show a few of them, so create only the controls that will be visible, and reuse them changing its content with the new data when you scroll.
Try this; drop this method in your code.
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000;
return cp;
}
}
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.
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.
I'm using a list box where there are on average about 500 thumbnails (items) that can be sorted and searched.
Since I'm using default databinding and search descriptors (which I've heard are slow due to reflection) the list takes a noticeable pause of a few seconds loading, sorting, and searching (the list dynamically updates based on the contents of the search box so the first one or two letters typed are really slow).
I don't think I can fully do away with reflection give the timeframe for the project, and speed isn't super essential, but I'd like some kind of graphical indication of the delay so it doesn't confuse the user. How could I do something like a website video loading screen where the listbox grays out and some kind of loading circle indicates it's processing until the list is ready? Or even just grayed out with the words "Loading..." for a few seconds could work. Any ideas?
Thanks in advance for your help and suggestions!!!
Silverlight-Controlkit comes with a very handy "busyindicator"-control... too bad there seems to be no such thing for WPF by default.
But I found this seemfully comparable control for you:
http://sweux.com/blogs/pombeiro/index.php/2009/12/01/a-busy-state-indicator-attached-behavior/
download-source:
http://gallery.expression.microsoft.com/en-us/BusyIndicator
Like Veer wrote, BackgroundWorker is probably your best bet.
For the graphical indication of progress and/or delay, take a look at
http://www.codeproject.com/KB/WPF/WPF_Loading_Wait_Adorner.aspx
It looks like pretty much exactly what you want to do.
Try BackgroundWorker
Use the DoWork method to update your list dynamically based on the search keyword
Use the ProgressChanged method to update your UI with some animation saying 'Loading'. A ProgressBar could be used
Use Dispatcher to access your list inside the DoWork method
Which control would be best for showing a huge (300.000+) list of filenames?
I've tried DataGridView, but it seems to be overkill and also slow.
Are there better alternatives?
None.
No USER will be able to handle a single list of 300.000+ entries in a meaningful way. Looks like your design is seriously flawed - do you really have to present the complete list?
Consider using a search box and let the users search the file names (use auto completion/suggestions like Google et.al.) or create a separate list for every starting letter (like most address books do). Or find another way to reduce the number of entries from which the user has to select.
The standard ListView control has a virtual mode designed specifically for your situation. I've used it with a million row list previously and it does the job well.
It is a true virtual mode. In other words memory allocation and list population time remains low regardless of the size of the overall list. This is unlike the DataGridView that really starts to slowdown and use memory on large lists.
To use virtual mode set:
VirtualListMode = true
VirtualListSize= 300000
(or whatever size your list currently is)
Then handle the RetrieveVirtualItem event to populate the list on demand from your list. You may also wnat/need to handle the CacheVirtualItems and SearchForVirtualItem events.
Set up pagination and restrict the number of rows displayed by the DataGrid. You can add a combobox to jump between pages. This is a standard solution.
also see this post https://stackoverflow.com/questions/2125963/need-help-in-gridview-and-table
Have you tried the ListView with report style? This is the control used by Windows natively in its file browsers.
Following on from gotch4's answer. Here is a good article from CodeProject on how to do paging with a DataGridView.
You might want to check out ObjectListView, specifically the VirtualObjectListView:
http://objectlistview.sourceforge.net/cs/index.html
I forget off hand what license it has been released under so you might want to look at that before using it in a commercial application.
I'm having some trouble with the Win32 listview, and I hope someone has some wisdom. When scrolling very quickly (via the wheel), occasionally, the listview appears to scroll, only to jump back to the selected item so it is in-view. In other words, the list undoes your wheel scroll to show you the selected item at the top/bottom of the list. I realize this sounds very dubious, as the common controls have been beaten to death the world over.
Here's how you can replicate:
Load up a listview in report mode with about 500 items or so (exact count doesn't matter, you just want several screens of data).
Select an item in the list and remember what you selected.
Rapidly scroll the list with the wheel downwards (toward you). We're trying to scroll the selected item out of view as fast as possible. You probably have to use more force than you usually use on the wheel. This won't always happen. You may have to try several times.
Either the scroll will complete normally, or it will change its mind at the end and pull you back to a view where the selected item is on the screen.
This happens with both regular and virtual listviews. I've tested Win32 and C# on Windows 7 Ultimate.
I made a small WinForms app that exhibits the behavior (requires .NET Framework 3.5). If you'd rather not run arbitrary executables from strangers (I understand), make a new WinForms app, drop a listview in report mode in, add a column, and populate the list in the form load event with 500 increasing integers:
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i < 500; i++)
{
this.listView1.Items.Add(i.ToString());
}
}
I've noticed this behavior and it does seem to be built into the list view control. I can't see any reason why the functionality would be useful. I created a thread on this at Sysinternals Forums a while back. It might be useful.
"I realize this sounds very dubious, as the common controls have been beaten to death the world over."
ListView hasn't quite been beaten to death but it's on life support. Use a different control.
is it a custom list view (overriding drawing ?)
by the way, I faced (from what I understand) the same issue with a custom Listbox and found some help here :
http://aviationxchange.net/wikis/winforms/net-color-listbox.aspx (end of the document).
Hope this could help.
I was facing the same problem but I think I found the cause: I use a tool that allows you to configure the mouse buttons (X-Mouse Button Control). By disabling it, the problem disappeared like magic.