We have a 3- tier architecture(UI,BL,DAL) WPF application. I need to handle loading of WPF and DevExpress datagrid with large number of rows. The BL method will return an observable collection of objects which is to be binded to the WPF datagrid. If the number of records are vergy large then the UI become un-responsive. So we need to implement a solution such that show a progress bar with the percentage of the work completed as the BL method executes the query and process the data. Here I need to get the total number of records immediately when the query is executed and after processing the each row I need to show the current index of the item processing in the lable like "processing 1/2000 documents".
What is the best way to achieve the above feature. We are using MVVM pattern. Whether I need to change the way we do the fetching and processing the records in BL (currently the fectch and processing (Map value from datareader to custom object) inside a BL method). Alternatively I am looking for loading the rows in the datagrid in a paged manner as the user scrolls the datagrid.
Any links for samples is appreciated.
Edit:
#Big Daddy your solution
1)Add new properties to view model to get the TotalCount and PercentComplete.
2)Pass the viewmodel to the Search method.
3)Use the BGW to update the properties.
The above seems to a workable solution. But I am eager to know whether any other way of solving this without dependednt of the viewmodel. Any design patterns avaialble for this kind of operation?
You can use a BackgroundWorker to handle this. It will not block the UI while getting your data and will keep the UI responsive for the user. The BackgroundWorker has a ProgressChanged event that works very well with ProgressBars.
In the projects I've worked on, I've implemented BWs in a client-service/repository. Your view-model can make the calls into this class(es). The methods here will instantiate the BWs, do the work, report progress, and return the data. You'll need to report the progess on the UI, so set-up a property in your view-model and bind your ProgressBar's value to it, like this:
View-Model
private int _percentCompleted;
[DefaultValue(0)]
public int PercentCompleted
{
get { return _percentCompleted; }
set
{
_percentCompleted = value;
RaisePropertyChanged(() => this.PercentCompleted);
}
}
View
<ProgressBar x:Name="SourceBatchUploadProgressBar"
Style="{StaticResource GreenProgressBar}" Grid.Column="1" Grid.Row="1"
Value="{Binding PercentCompleted, UpdateSourceTrigger=PropertyChanged}"
Height="25" Width="200" Margin="5,0,10,5"/>
You'll need to have the BW's ReportProgress event update the PercentCompleted property for this to work. Something like this:
BackgroundWorker.RunWorkerCompleted += (o, e) =>
{
...Update progress
};
If you'd like more details, let me know.
So you have all the rows. What processing are you referring to? Rendering? The displayed status of rendering would always be 100% as it does not render until it is done rendering.
It takes the UI a long time to render 2000 rows. There is a lot of formatting and sizing going on.
Break up the UI to display like 40 rows per pages with next and previous page buttons.
If DevExpress datagrid does not support virtualization then look for a grid that does. For speed I like ListView GridView.
Wow a check. You are listening. I give you more.
So get the whole list of IDs at once if that is fast (and you can get a total count). Then just create the 40 objects on demand. If you want to get fancy create the next 40 on a BackGroundWorker. Disable the Next button when you start the BackGroundWorker and enable it in the Complete.
Related
I've spent some time researching and observing the best practices to avoid blocking the UI thread when attempting to process data on the UI thread. Specifically, I utilize the async/await where I can. However, when populating a DataGrid via Binding, I noticed my UI freezes up after command processing is completed and processing is passed back to the UI.
XAML
<DataGrid ItemsSource="{Binding EndpointModel.DataView}"
AutoGenerateColumns="True" IsReadOnly="True">
</DataGrid>
DataModel command execution:
public async void CommandExecute()
{
...
JsonData = await accessor.GetDataAsync(new Endpoint.Params().Universe(universe)
.WithStart(start)
.WithEnd(end));
// Creates a very large DataTable within my display (30 x 350)
var grid = EndpointModel.CreateDataGrid(JsonData);
EndpointModel.DataView = grid.AsDataView();
}
I've stepped through the code to observe processing times, placed debugger messages and processing seems normal. The await statement takes about 1.5 seconds and the grid processing at the end is a few ms. Yet, when I return from the 'CommandExecute()', it takes about 3-5 seconds before the UI is responsive. The data populates fine - it just takes forever. I don't know if this is expected or whether I have control over this.
thanks.
WPF DataGrid is known for its performance issues. You can try to go through answers from this thread.
Basically, most recommended option is: using Grid. Especially if rows counts thousands.
Or, if you want to keep datagrid, there are such ways to decrease loading time as
EnableColumnVirtualization = true
EnableRowVirtualization = true
Also set fixed width and/or height, datagrid may try to recalculate required width and height every time there is data updated. Id check other threads to optimize your datagrid to your needs.
I'm trying to improve the performance of my treeview in WPF, when you open a node with 6000 children, it takes about 13 seconds currently to display this. I am using an observablecollection for the child collection, with a datatemplate bound to the TransactionViewModel type which has about 7 columns, each pulling in a piece of data from the view model.
The transactionviewmodels for the 6000 children are created and instantiated, but as you haven't displayed any of them visually yet, the first time you expand the node, it takes the 13 seconds to display. if you then contract and expand the node, it displays instantly with zero time to display/load. The only difference I can see is that the first time each of the bound dependency properties of the TransactionviewModel has it's getter called by the XAML binding, and when you re-expand the second time, none of this happens as nothing has changed so WPF doesnt call the getters again and presumably just holds the bound information in memory for when you expand it the second time.
So the visual drawing of the control is instant, but the first time you open it (even though the 6000 transactionviewmodel objects are already fully loaded into the child collection), purely the rendering of the rows is what's taking the time.
Interestingly if I alter the datatemplate to not bind to ANY dependency properties on the viewmodel object and just output a blank grid, it still takes 8 seconds to load. So even without any data binding calls, the tree viewer takes 8 seconds to render 6000 rows. 5 seconds extra then gives you about 5 bound data columns per row, so that is a small cost compared to the basic rendering.
8s to render 6000 blank rows seems very high to me. Are there any major reasons why this might be happening or things to be aware of in rendering XAML into a treeview from data templates? Ive tried using just an empty datatemplate - ie not even a blank grid inside it and it still takes 7 seconds.
Given that it then collapses and expands instantly, why is it taking so long the first time when it isn't even rendering any XAML or calling any data bindings?
Also asynch calls are not a solution as my problem is not GUI responsitivy but time taken to load data. The user needs to have the data quicker than they are getting it right now.
Many thanks
It looks to me like you need to enable virtualization in the TreeView.
From Optimizing Performance: Controls:
By default, UI virtualization is enabled for the ListView and ListBox
controls when their list items are bound to data. TreeView
virtualization can be enabled by setting the
VirtualizingStackPanel::IsVirtualizing attached property to true
If a TreeView contains many items, the amount of time it takes to load may cause a significant delay in the user interface. You can improve the load time by setting the VirtualizingStackPanel.IsVirtualizing attached property to true. The UI might also be slow to react when a user scrolls the TreeView by using the mouse wheel or dragging the thumb of a scrollbar. You can improve the performance of the TreeView when the user scrolls by setting the VirtualizingStackPanel.VirtualizationMode attached property to Recycling.
How to: Improve the Performance of a TreeView
XAML:
<TreeView Height="200" ItemsSource="{Binding Source={StaticResource dataItems}}" x:Name="myTreeView"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"/>
Programmatically:
myTreeView.SetValue(VirtualizingStackPanel.IsVirtualizingProperty, true);
myTreeView.SetValue(VirtualizingStackPanel.VirtualizationModeProperty, VirtualizationMode.Recycling)
Your problem is likely not rendering but layout - it has to instantiate a lot of UI elements to find the size of them so that it can properly size a number of UI elements (sliders), and this takes time. Rendering likely does not enter into this at all.
I've got a new Problem here. I've got a MainWindow in which is a ContentGrid and in this is a Frame. Now I've created different Pages which can be shown in this "content-area". In One of these pages there is a Datagrid bound to a CollectionViewSource which Source is a Database (via EntityFramework). Now, when a Change on this Database-Table happens (solved via ServiceBroker and SQLDependency, firing works fine) The Datagrid have to update.
Now the Problem:
The "Dependency_OnChange"-Event is working in the MainWindow-Thread. When i try to access the CollectionViewSource of the Page to Update it (cvs.View.Refresh) i get an Exception that this is not possible because of another Thread which own this CVS.
I've tried it with different Dispatching like:
Application.Current.Dispatcher.Invoke((Action)(()=>
{
cvs.Source = _db.Table.OrderByDescending(nr => nr.Date).Take(200);
cvs.View.Refresh();
}));
This Codeblock doesn't brings an Exception but i wont update the UI too... It seems that it does nothing.
Can anyone help me?
You data grid will update if your LINQ query evaluates. Right now it just specifies the LINQ IEnumerable but is not evaluating it.
cvs.Source = _db.Table.OrderByDescending(nr => nr.Date).Take(200).ToList();
should do the evaluation of the LINQ for you...
Although I must say your cvs.View.Refresh() call is very expensive as it causes entire grid to refresh. You may have to consider a better design here.
Why dont you set the dataGrid.ItemsSource = _db.Table.DefaultView as the item source to the datagrid directly. I guess if your table updates (and peforms _db.Table.AcceptChanges();) the view would automatically notify the changes to the grid and grid would possibly update itself faster!
But thats just my opinion as I am not aware of your threading context here. But still do try and let me know.
I am creating a custom DataGrid by deriving the traditional tookit based WPF DataGrid. I want a functionality in the grid to load items one by one asynchronously, wherein as soon as ItemsSource is changed i.e. a new collection is Set to the ItemsSource property or the bound collection is Changed dues to items that rae added, moved or removed (wherein the notifications comes to the data grid when the underlying source implements INotifyCollectionChanged such as ObservableCollection).
This is because even with virtualising stackpanel underneath the datagrid takes time to load (2-3 seconds delay) to load the data rows when it has several columns and some are template based. With above behavior that delay would "appear" to have reduced giving datagrid a feel that it has the data and is responsive enough to load it.
How can I achieve it?
Thx
Vinit.
Sounds like you are looking for data virtualization', which typically means creating your own custom type that resembles IList, and doing a lot of work to hydrate objects after-the-fact.
You will end up having your data that the grid is displaying look something like this:
Index 0: new MyDataObject(0);
Index 1: new MyDataObject(1);
And MyDataObject implements INotifyPropertyChanged.
In the constructor, you do the logic necessary to time, schedule, or interpret when the real results should be read. Until then, you return rather empty data... null and string.Empty from your properties.
Then, once the data becomes available (ideally in a background thread, read from wherever - your own local data, or a database or web service), then you can update the real underlying property values and fire the property change notifications so that the UI gets properly loaded then.
It's a little too complex to just jump into, so some searching will help. Hope this gets you started.
I try to programmatically update the selected items in a listbox, with extended selection mode. Here is how I do it:
foreach (var selectedItem in ItemsForSelection)
{
_myList.SelectedItems.Add(selectedItem);
}
My problem is, that when the number of the selected items is big, the update is very slow.
The root of the problem is that listbox doesn't derrive from MultiSelector, which can be tweaked to perform a fast bulk update, by using the methods BeginUpdateSelectedItems and EndUpdateSelectedItems.
Is there a way to get a similar result in a listbox?
Is there a BeginUpdate and EndUpdate method available in the ListBox...
_myList.BeginUpdate();
foreach (var selectedItem in ItemsForSelection)
{
_myList.SelectedItems.Add(selectedItem);
}
_myList.EndUpdate();
That is assuming that _myList is a ListBox...The pair for Begin/End Update methods freezes the WM_PAINT message and unfreezes respectively thereby making it flicker free and fast.
After adding a chunk of items, try pumping the dispatcher by pushing a dispatcher frame into the dispatcher.
http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherframe.aspx
There is a SetSelectedItems(IEnumerable) available on ListBox you could use. This function wraps selection changes in a SelectionChange.Begin/End (unfortunately only available internally of course) which ought to result in only a single selection change event going out.
Note that SetSelectedItems is protected, so you'll have to use your own ListBox derivative to call it. Odd choice, that.