WPF Rendering and Binding Performance Comparison






4.89/5 (4 votes)
WPF performance comparison in 3 ways of refreshing data
Introduction
In my workplace (a financial institution), responsiveness of Windows application is critical and there are many factors which will affect the performance. These factors include network, dependent external services and software design.
In this tip, I would like to investigate and compare the performance on different ways to refresh data in a typical WPF application.
Background
In this example, there is a window full of WPF controls (I have added multiple instances of the same user control for simplicity). The user control contains a typical set of WPF controls, including Label
, TextBox
, Slider
, ComboBox
, CheckBox
, DataGrid
. This is a typical MVVM design.
View
- DogDetails.xaml user controlViewModel
- DogVM.csModel
- Dog.cs
There are four buttons at the top of the application. The functionality of these four buttons are the same, it refreshes the whole window with another set of data (a list of Dog
). However, the implementation of the three buttons are different. The aim is to test and compare the performance of each of them.
1. Re-Render and Re-Bind
- Re-generate the data (list of
Dog
object) - Re-constructs all the
ViewModel
s, one perDogDetails
user control - Re-constructs all
UserControl
s and rebind toViewModel
s
2. Re-Construct VM and Re-Bind
- Re-generate the data (list of
Dog
object) - Re-constructs all the
ViewModel
s, one perDogDetail
s user control - Re-use
UserControl
s and rebind toViewModel
s
3. Re-use VM and Re-Bind
- Re-generate the data (list of
Dog
object) - Re-use
ViewModel
s and set theModel
referenced by eachViewModel
- Re-use
UserControl
s and rebind toViewModel
s
4. Re-Notify Only
- Re-generate the data (list of
Dog
object) - Re-use all the
ViewModel
s andUserControl
s by just switching theDog
object referenced byDogVM
and callNotifyPropertyChanged
for all fields
To measure the performance of rendering, I followed the advice from here.
Using the Code
Here are the various implementations of refreshing. They are all in MainWindow.cs:
1. Re-render control and Re-bind
public void RegenAndBind()
{
allVMs.Clear();
allDetailsControls.Clear();
start = DateTime.Now;
int i = 0;
for (int row = 1; row < _mainGrid.RowDefinitions.Count; row++)
{
for (int col = 0; col < _mainGrid.ColumnDefinitions.Count; col++)
{
//Reconstuct VM
DogVM thisvm = new DogVM();
thisvm.SetDog(allitems[i]);
//Reconstuct UserControl and Set the Grid position
var dogDetails = new DogDetails();
//Rebind
dogDetails.DataContext = thisvm;
//Add the user control to window
_mainGrid.Children.Add(dogDetails);
Grid.SetColumn(dogDetails, col);
Grid.SetRow(dogDetails, row);
//Keep References
allVMs.Add(thisvm);
allDetailsControls.Add(dogDetails);
i++;
}
}
}
2. Re-construct VM and Re-bind
public void ReconstuctVMAndbindOnly()
{
allVMs.Clear();
start = DateTime.Now;
int i = 0;
for (int row = 1; row < _mainGrid.RowDefinitions.Count; row++)
{
for (int col = 0; col < _mainGrid.ColumnDefinitions.Count; col++)
{
//Reconstuct VM
DogVM thisvm = new DogVM();
thisvm.SetDog(allitems[i]);
//Bind VM to each UserControl
allDetailsControls[i].DataContext = thisvm;
//Keep Reference
allVMs.Add(thisvm);
i++;
}
}
}
3. Re-use VM and Re-bind
public void RebindOnly()
{
start = DateTime.Now;
int i = 0;
for (int row = 1; row < _mainGrid.RowDefinitions.Count; row++)
{
for (int col = 0; col < _mainGrid.ColumnDefinitions.Count; col++)
{
allVMs[i].SetDog(allitems[i]);
allDetailsControls[i].DataContext = allVMs[i];
i++;
}
}
}
4. Re-notify only
public void NotifyOnly()
{
start = DateTime.Now;
int i = 0;
for (int row = 1; row < _mainGrid.RowDefinitions.Count; row++)
{
for (int col = 0; col < _mainGrid.ColumnDefinitions.Count; col++)
{
//Using the kept reference, just set the Dog object referenced by VM
//NotifyPropertyChange is handled by the VM
allVMs[i].SetDog(allitems[i]);
i++;
}
}
}
Points of Interest
Here's the performance figures I obtained from my moderate spec PC when the application is opened in full screen (Intel i5-2500K 3.3Ghz, 16GB ram, SSD on windows 7, VS2013)
I also note that having a DataGrid
in DogDetails.xaml has quite a big performance impact.
Here are the average of 10 refreshes for each implementation. (With DataGrid
in UserControl
)
- Re-render control and Re-bind: 1126.2ms
- Re-construct VM and Re-bind: 403.6ms
- Re-use VM and Re-bind: 373.8ms
- Re-notify only: 343.6ms
Here are the average of 10 refreshes for each implementation. (Without DataGrid
in UserControl
- comment the DataGrid
in DogDetails.xml):
- Re-render control and Re-bind: 680.1ms
- Re-construct VM and Re-bind: 144.4ms
- Re-use VM and Re-bind: 129.8ms
- Re-notify only: 96.6ms
Here's the summary of the findings:
DataGrid
rendering performance is quite poor, even when thedataset
is really small. I might investigate this further, maybe using a 3rd party grid.- Most of the performance gain is obtained by avoiding the reconstruction and building of the
UserControl
. - In this example, there is only a very small gain (around 10 to 30ms) after avoiding reconstruction of the
ViewModel
. This is because theViewModel
is really simple in this example. The benefit is highly depending on the complexity and performance of theViewModel
constructor. - Size of the window has a big effect on performance.