We are struggling with the slowness of the datagrid in wpf. Regardless of which collection we are using - List, BindingList, ObservableCollection, custom ObservableCollection, it's still very slow when responding to collection update (Clear, Add) and render on the screen.
The fastest solution we found is not to update the collection, but update the exsting objects in the collection, that DataGrid is bound to. In this case the grid is responding very fast, like old winforms grid.
We are creating 65535 rows in the grid, that will be enough for all our grids. When it comes to update the grid, we update the required number of top rows, and set visibility = hidden for the rest. As I said it works exteremely fast. But There are 2 issues that we cannot solve:
scroll bar, since we don't collapse the rows (it's slow) - it's always is set for 65535 row. Is there any way to limit the scroll, or grid size to the actual visible rows count?
Also, I noticed, that adding new rows into collection, if they don't need to be rendered immediately (they will be out of the visible area) is also very fast, so we can limit the minimum set to 50 rows (max visible rows), and then add/remove new rows as required. But this does not solve the problem with the scrolling.
Any other solutions are welcome.
Foreseeing the advise to enable virtualization - yes, we are using virtualization (for rows which are enabled by default, and for columns).
Update:
We are trying to display 20 columns x 50 rows of data. If we modify the source collection with Clear() and then Add(), the rendering time is about 1 second, which is not acceptable at all for us, as the UI is frozen for a second. I tried to resize the datagrid to 0 Height, and then set size back incrementally in the background thread, it unfreezes the UI, but overlook is ugly, and I haven't managed how to set datagrid back to fill the parent control. It seems after Height is set in code, there is no way back.
As in initial post, the alternate solution we found it not to modify the collection. Just limit the grid to 65535 rows, don't add or remove new items. It works really fast, but now we are facing problems in synchronizing scrolling, and sorting.
I found the datagrid is very frustrating control in WPF. The performance is below any reasonable limits.
What we are trying to achieve is responsiveness. It should not block UI more than several milliseconds, when loading the data.
XAML: nothing extraordinary
<DataGrid x:Name="TheGrid"
DockPanel.Dock="Top"
ItemsSource="{Binding Collection}"
EnableColumnVirtualization="True"
EnableRowVirtualization="True"
AutoGenerateColumns="False"
ColumnWidth="70"
RowHeight="20"
>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Header 1" >
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Field1, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Field1}" /></DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn >
..... and so on for 20 columns
Also, the datagrid is not inside a StackPanel (this makes it terrible slow).
Finally we have found how to limit the scrolling to any number of rows not hacking too much.
We created our own list for binding, which implements ICollection, which is used in ScrollView to get count of rows. We changed ICollection.Count to return count of visible rows, which we set before calling Measure, then we set it back to actual count. It works well, and it resolved all our problems with scrolling.
So, we have a grid, that is bound to list which has static number of rows, and we set visibility=Visible to n first rows that we want to show, the rest has visibility Hidden (not Collapesed, because Collapsing is very slow), and we limit the scroll to visible rows. The objects that are in list, can be updated from different threads, and we don't need to call Dispatcher, because WPF does it itself.
I have found to pages that might be useful to you.
The first is about caching the values in the datagrid:
http://msdn.microsoft.com/en-us/library/system.windows.uielement.cachemode(v=vs.100).aspx
The second it about implementing virtual mode for the datagrid:
http://msdn.microsoft.com/en-us/library/15a31akc.aspx
I hope it help you
Update:
Are you using a scroll view? Because if you are it will load all rows instead of the visible only. Try see the answer to this question: How to lazy-evaluate a wpf:DataGrid, retrieving data only as needed
I know it might not be the right solution because it still will take the same amount of time. But making it multithreading will solve the freezing problem. Then you could just let it fill data in the grid in the background and add them on the run one by one or add them all when finished.
Related
Well the question is relative to a DataGrid.
I have a table in a MySQL database. It has about 20 columns and stores about 32 thousand records.
So I normally load these in my test apps using a DataGrid and it usually takes two seconds or less for it to populate all the information with no lazy loading. (EF 6)
So I really just need one column and decided to use a ListView. Thing is I've only ever used it with very small tables so the lag wasn't even worth mentioning. Likewise with a TreeView.
It baffles me how a DataGrid can handle all this information almost seamlessly while the ListBox seems to feel the need to reload the contents with every change of selection using the arrow keys. It's only loading one column really.
It takes ages to load as soon as the UserControl is initialized and I'm thinking maybe it's me not optimizing my code properly. Perhaps the two are to be treated differently?
This is the XAML:
<ListBox ItemsSource="{Binding Principal, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="first" SelectedItem="{Binding SelectedUser, Mode=TwoWay, UpdateSourceTrigger=PropertyCHanged}" IsSynchronizedWithCurrentItem="True" Width="Auto"/>
And in the View Model, this is the property it's DataBound to:
// Private ObservableCollection Declaration
private ObservableCollection<principal> _principal;
// Public ObservableCollection Declaration
public ObservableCollection<principal> Principal
{
get
{
return _principal;
}
set
{
if (_principal != value)
{
value = _principal;
}
OnPropertyChanged("Principal");
}
}
That's pretty much it. My DataGrid binds to the exact same property with very similar XAML code but it will load all the columns in a tenth of the time it would take a Listbox or TreeView.
I've been at it for a while now but it just doesn't check out.
Anyone know why?
I find ListBox and ListView to be very fast.
Try
VirtualizingStackPanel.VirtualizationMode="Recycling"
Are you by any chance not constraining the size?
If you don't constrain the size then it will paint all the elements.
I have something like
<ScrollView>
<StackPanel Orientation="Vertical">
<TextBox>
<TextBox>
*** // Tons of TextBoxes
<TextBox>
<RadJumpList> // Just a ListBox from Telerik
</ScrollView>
For now, the whole page is scrollable (due to ScrollViewer) and RadJumpList is also scrollable itself (by definition). How to disable RadJumpList from scrolling?
PS: probably, too many "scroll"s per a question, but i guess its pretty intuitive :)
EDIT: from my experience, this is bad idea. If list has 100+ items, it can delay page loading for a several seconds list would be loaded whole at once. Better idea is to put all TextBoxes to the ListBox via dataTemplateSelector.
If you don't set the height of your RadJumpList and disable scrolling like ScrollViewer.VerticalScrollBarVisibility="Disabled".It will be ok.
Actually, even better way is to push all stuff into the list via ItemTemplateSelector.
Otherwise, if there would be 100+ items, page would load for a several seconds.
I have two DataGrids that have the same number of rows. There is also a hidden integer field/column on each row for maintaining a 1-1 correspondence between the two. When the user sorts, selects, or scrolls, the action on one DataGrid needs to be replicated on the other. I am most concerned about sorting, since I'm afraid the DataGrid's sort operations may interfere or may not reflect my own ordering behind the scenes. What's the best way to maintain these views?
(It is for a surveying program in which one grid has a list of x,y,z coordinates and the second list has the same points with a transformation applied. These are split between two tabs for reasons of screen real-estate.)
On large datasets sorting may be expensive. If screen real estate is the issue you can have one datagrid with one type of items and switch the item template to switch between both views. This way screen estate is retained, sorting isn't done twice and you have a very reliable way of keeping the sort between both views in sync.
there are many ways to achieve the effect, the best approach depends on the specific circumstances.
One approach would be to set two different <GridView>s as resources and select which one is used either in code or through Binding with a custom converter:
<Grid>
<Grid.Resources>
<GridView x:Key="DefaultView">
<GridViewColumn Width="120"....etc/>
</GridView>
<GridView x:Key="AlternativeView">
<GridViewColumn Width="50"....etc/>
</GridView>
</Grid.Resources>
<ListView x:Name="MyListView" ItemsSource="{Binding DisplayValues}" View="{StaticResource DefaultView}"/>
</Grid>
and in code behind you can switch the view, something like this:
MyListView.View = isDefault ? MyListView.TryFindResource("DefaultView") : MyListView.TryFindResource("AlternativeView")
It's also possible to use Binding and skip the code behind.
Another approach would be to make a user control for one of the column items where the user control can display the data in two alternative states.
Yet another possiblity is to use ItemTemplateSelector
I'm new to WPF, so maybe I'm doing something really stupid. But..
Also is my first question here.
I made a custom ProgressBar UserControl to use inside a DataGrid.
All look to work fine except that every time the DataGrid refresh (reordering a column) the UserControl resets, all properties and dependency properties change to their default values and the constructor is called again. Look like my control is destroyed and recreated on every DataGrid's refresh.
Anyway to avoid that behavior?
I need to keep the changes history.
edit:
<DataGridTemplateColumn Header="Progress" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:SegmentedProgressBar HorizontalAlignment="Stretch" SegmentedProgressCustomUpdate="{Binding SegmentedProgressCustomUpdate}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
It sounds as if the DataGrid is data binding again. That would cause the rows to potentially get recreated and, therefore, the user control to get reset. That said, if you are pulling the progress bar off of some external data, you could dynamically set all of the properties for the progress bar control in the row created event.
I have large list of categorize items for my small program.
Let's assume that I have a category named Candy and there are more than 100 items in the category Candy.
Here I query it by,
OleDbConnection dbConnect = new OleDBConnection("....");
dbConnect.Open();
string query = "SELECT ID, item FROM Items WHERE category == 'Candy'";
OleDBCommand executeCommand= new OleDBCommand(query, dbConnect);
OleDBDataReader reader = executeCommand.ExecuteReader();
The result might be many rows, where each row will have property of a button. A new button will be generated.
while(reader.Read()) {
// create multiple buttons for each row here..
}
Now the problem is, i want to show it in a WrapPanel with limited height and width.
Say that I can show only 10 buttons in the WrapPanel. However the buttons created are more than 50.
What is the best way to sort out this problem. I was thinking if I can modify query for 10 rows and create 10 buttons and another button with NEXT arrow will fetch another 10 buttons.
I am too much confused here. Please help me with the better solution.
If number of items doesn't get to the thousands, I think you'll be better off (much less code, much simpler code) if you let WPF managing the view for you. How do plan users to browse through the buttons? Is it a scroll bar?
Also, looking at your sample code, it appears you intend to Create buttons on the fly within the 'while' loop. Again, while this is possible way, this is not the "textbook" solution or the simple solution for creating multiple items based on a collection. The suitable paradigm is using a Data Binding to an array of items.
Here is an example:
Within your XAML file, define a <ItemsControl>. Give it a name (say ButtonsPanel) so you can reference it from the code behind. In the code behind prepare an array (or List, or anything derived from IEnumerable) with all the items you want to present, and set the ItemsSource of your element to this list.
ItemsControl is a very generic Items Control. You can define the ItemsPanel (i.e. the panel the contains all the items) as well as template for all the buttons.
<ItemsControl x:Name="ButtonsPanel" Height="80" Width="50">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
EDIT (following comment from user995387):
With Prev/Next buttons (a.k.a. paging) data binding is also the preferred paradigm. Within the data context of your control (or the code behind) have a ObservableCollection with the 10 items that you want to present. Uppon a click on the 'Next' button, either replace the entire collection, or the 10 items within the collection (Clear and 10 Adds), or the content of the items within the collection (in which case, the item has to fire INotifyPropertyChanged event) - whichever makes more sense to you. Choose the last option only if creating 10 new buttons (as opposed to update the content of each button) has performance penalty.