September 2009 - Posts
Part 1
Yesterday I went back to my customer, tried to figure out if the whole concept of using data as the content of a ContentControl instead of holding a logical tree, is truly contribute to the whole system, and guess what? It is not!
Well it figured out that setting the content property with non-visual element only postponed the tree-traversal. We figured this out by trying to measure the total elapsed time between setting the content property to null and up until the view completely disappeared.
To measure this we called the Dispatcher.BeginInvoke method with Background priority. Note that I used different set of controls to put more pressure and to turn the Dispatcher.BeginInvoke invocation time negligible.
The results showed us that the direct technique is a little bit faster in total time, but much slower when setting the content property (in our case the total time is more important).
private void Button_Click(object sender, RoutedEventArgs e)
{
if (_placeholder.Content != null)
{
Stopwatch stopWatch = Stopwatch.StartNew();
_placeholder.Content = null;
long setContentTime = stopWatch.ElapsedMilliseconds;
Dispatcher.BeginInvoke(
DispatcherPriority.Background, (Action)delegate
{
stopWatch.Stop();
long unloadViewInclusiveTime = stopWatch.ElapsedMilliseconds;
long unloadViewExclusiveTime = unloadViewInclusiveTime - setContentTime;
LogTime(setContentTime, unloadViewInclusiveTime, unloadViewExclusiveTime);
});
}
}
private static void LogTime(
long setContentTime,
long unloadViewInclusiveTime,
long unloadViewExclusiveTime)
{
Debug.WriteLine(string.Format(
"Time to set ContentControl.Content property: {0}ms.", setContentTime));
Debug.WriteLine(string.Format(
"Exclusive Time to unload view: {0}ms.", unloadViewExclusiveTime));
Debug.WriteLine(string.Format(
"Inclusive Time to unload view: {0}ms.", unloadViewInclusiveTime));
}
This conclusion lead us to think of another way to reduce the unload overhead. So I came out with the following simple solution: Instead of using ContentControl as the view place holder, use Border!
Surprisingly or not, here are the results:
| Direct | Indirect | Efficient |
| 84 | 101 | 56 |
| 90 | 105 | 64 |
| 84 | 120 | 73 |
| 77 | 104 | 62 |
| 86 | 98 | 71 |
| 91 | 101 | 64 |
| 106 | 108 | 70 |
| 104 | 128 | 66 |
| 93 | 115 | 66 |
| 90 | 110 | 63 |
| Average 90.5 | Average 109 | Average 65.5 |
* Efficient is the Border test
As you can see in the table above, it looks like using a Border is somehow much faster than using a ContentControl.
Here is my tester.
Part 2
Background
Yesterday I came back from my customers’ place. He had performance issues with his WPF composite application. Each time he navigates away from a page, he pays about 1400ms! – Playing around we’d found that each time a page was changed, the composite framework he uses replaces the old page with the new page by simply updating the ContentControl.Content (placeholder) with the new page. This operation solely took about 800ms!
placeholder.Content = newPage;
In this post I would like to talk about:
- Performance problems you may encounter by using ContentControl incorrectly or not using it by the book.
- How to intercept them.
- How to solve them using ContentControl best practices.
First I want to talk about well-known techniques for displaying views in a composite application, using Prism or other libraries such as CAB/SCSF and MEF.
Working with patterns such as MVP, Presentation Model or MVVM, the view is separated from the logic by having at least two parts: View and Presenter. The view is usually injected/composed into the composite area (Region in Prism, Zone in SCSF and Import in MEF) directly or indirectly, where the place holder (or view host) is usually a ContentControl.
By saying directly, the ContentControl representing the Region/Zone is initialized with the view by setting the Content property with the instance of the view, where instance of the view is usually a UserControl or other WPF element.
By saying indirectly, the ContentControl is initialized with the view by setting the Content property with the instance of the presenter, where instance of the presenter is a simple CLR class (sometimes DependencyObject), and the view is a side-effect of the DataTemplate provided with the presenter (usually merged with the application or shell/window resources).
IMHO the second approach (indirectly) is much better, and there are several reasons for that. The most persuading reason is: performance.
So how performance is related to the ContentControl.Content issue?
This is exactly what I want to talk about. Let me start with a little demonstration.
The shell presenting the view using a ContentControl
<Window x:Class="ContentControlSweets.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Shell" Height="300" Width="300">
<Grid>
<TabControl>
<TabItem Header="View">
<ContentControl x:Name="_placeholder" x:FieldModifier="private"
Margin="16"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" />
</TabItem>
<TabItem Header="Other View"
Content="Other View Content"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" />
</TabControl>
</Grid>
</Window>
public partial class Shell : Window
{
public Shell()
{
InitializeComponent();
}
public object Placeholder
{
set { _placeholder.Content = value; }
}
}
The presenter
public class Presenter
{
public string Description { get; set; }
}
Direct view
DirectView.xaml
<UserControl x:Class="ContentControlSweets.DirectView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<TextBlock Text="{Binding Description}"
FontSize="24"
Foreground="Green"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</UserControl>
DirectView.cs
public partial class DirectView : UserControl
{
public DirectView()
{
InitializeComponent();
}
}
Direct view loading
private void ButtonDirect_Click(object sender, RoutedEventArgs e)
{
new Shell()
{
Placeholder = new DirectView()
{
DataContext = new Presenter()
{
Description = "Direct View"
}
}
}.ShowDialog();
}
Indirect view
IndirectView.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ContentControlSweets">
<DataTemplate DataType="{x:Type local:Presenter}">
<Grid>
<TextBlock Text="{Binding Description}"
FontSize="24"
Foreground="Blue"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</DataTemplate>
</ResourceDictionary>
Indirect view loading
private void ButtonIndirect_Click(object sender, RoutedEventArgs e)
{
var shell = new Shell()
{
Placeholder = new Presenter()
{
Description = "Indirect View"
}
};
// This can be done from XAML.
shell.Resources.MergedDictionaries.Add(
new ResourceDictionary()
{
Source = new Uri("IndirectView.xaml", UriKind.Relative)
});
shell.ShowDialog();
}
Performance test
Measuring the unload time of both techniques described above (using my machine), by setting the ContentControl.Content property with null, here are the results: direct-view 0ms, indirect-view 0ms.
As you may guess, it’s exactly the same!
Well, lets try to mimic a more realistic application by adding more controls to both views.
After adding 100 buttons and 100 text blocks to both views, and measuring the unload scenario again, here are the new results: direct-view 15ms, indirect-view 0ms.
After adding another 100 buttons and 100 text blocks to both views, and measuring the unload scenario again, here are the new results: direct-view 30ms, indirect-view 0ms.
Wow! It looks like working with direct views, the bigger the logical-tree is, the more time it takes to unload the view. Working with an indirect view takes ZERO time to unload the view.
How’s that?
The best way to know what’s happening there is to take a closer look at the ContentControl using Reflector.
Lets start by looking at the ContentControl.OnContentChanged method. This method is activated each time the ContentControl.Content property changes with a different value (in our case, setting the content to null, or just changing views).
protected virtual void OnContentChanged(object oldContent, object newContent)
{
base.RemoveLogicalChild(oldContent);
if (!this.ContentIsNotLogical)
{
if (base.TemplatedParent != null)
{
DependencyObject current = newContent as DependencyObject;
if ((current != null) && (LogicalTreeHelper.GetParent(current) != null))
{
return;
}
}
base.AddLogicalChild(newContent);
}
}
Commenting the RemoveLogicalChild method call significantly reduces the unloading time!
Lets take a closer look at this method.
protected internal void RemoveLogicalChild(object child)
{
if (child != null)
{
if (this.IsLogicalChildrenIterationInProgress)
{
throw new InvalidOperationException(SR.Get("CannotModifyLogicalChildrenDuringTreeWalk"));
}
FrameworkObject obj2 = new FrameworkObject(child as DependencyObject);
if (obj2.Parent == this)
{
obj2.ChangeLogicalParent(null);
}
IEnumerator logicalChildren = this.LogicalChildren;
if (logicalChildren == null)
{
this.HasLogicalChildren = false;
}
else
{
this.HasLogicalChildren = logicalChildren.MoveNext();
}
}
}
Keep digging inside, you’ll find that ContentControl recursively changes the logical parent of each element all the way down the tree. Having a very simple content such as our Presenter, this won’t happen, since in such case we don’t have a tree.
Conclusion
The best way to work with WPF ContentControl is to set its Content property with a simple CLR object (can be DependencyObject), which is not part of a logical tree, having a DataTemplate for displaying it.
Resources
You can download my test application from here.
Feel free to register my blog RSS feed to see more WPF performance sweets.
Part 2
Being an LOB, composite applications infrastructure junky lately, I’ve been working around with several data-binding models and mechanisms to bind the data with the view, in a very efficient way of course. Sometime it was easy, and sometime it was not! I have had to find several workarounds to overcome both architectural and performance issues.
In this post I would like to concentrate on Data Virtualization, and to compare it with UI Virtualization, which are very similar, yet different aspects of the same problem.
After reading this post you'll have an idea of what is Data Virtualization compared to UI Virtualization, and what it is good for.
UI Virtualization
To understand what UI Virtualization is, lets say that you want to display a HUGE amount of data entries in a WPF DataGrid, all at once. In such case, trying to bind a simple WPF DataGrid with, lets say, 100,000 or more items, you’ll have to wait one or two seconds for the initial binding.
public partial class Window1 : Window
{
public Window1()
{
var hugeCollection = new HugeCollection();
DataContext = hugeCollection;
InitializeComponent();
}
}
<Window ...>
<Grid>
<tk:DataGrid ItemsSource="{Binding}" />
</Grid>
</Window>
Well its really nothing!
Now lets change a little flag on the data grid.
<tk:DataGrid ItemsSource="{Binding}" VirtualizingStackPanel.IsVirtualizing="False" />
Now, before you run this app, if you use a lap-top (like me… ;), connect it to an AC power adapter first. Trust me, your laptop will need that.
As you can see, the CPU stuck on 100% and it takes eternity to see the data (I just had to kill the process).
What’s wrong? It’s only a little flag: VirtualizingStackPanel.IsVirtualizing="False".
Well, it’s indeed a little flag that makes a big difference. If this flag is true (which is the default) it instructs the VirtualizingStackPanel (which is the default layout panel in a DataGrid, ListView, ListBox and other controls) to use UI virtualization. Means: don’t create data grid rows that are not visible to the user (in other words, are not in view). So this gives you a clue for what happened when we turned off this flag. We told VisrtualizingStackPanel not to use UI Virtualization, hence the DataGrid control tried to create and layout 100,000 rows! And this is a CPU killer as you saw.
Data Virtualization
WPF internally implements UI Virtualization and it’s great. In the previous case, UI Virtualization is just fine. We’ve got 100,000 items loaded in memory, and all we’ve had to do is to bound them with the DataGrid.
But what if each entry takes more than 1MB?
100,000 * 1MB = 97GB – Huston, we have a problem!
Well, the fast answer of course would be: Don’t load all items at once, instead use chunks. And this is exactly what Data Virtualization is!
Data Virtualization implementation raises several problems, and there is no out-of-the-box solution for Data Virtualization in WPF.
Well, first problem is: How do you fake scrollbars, so the user will think that it has all the data, and will be able to scroll?
Second problem: How do you filter, sort, group data that doesn’t exist?
Third problem: How do you search data (like this search) that doesn’t exist?
These are all tough questions.
So what do you think? Do you have an easy way or acceptable solution to solve this problem?
Next post I’ll introduce a unique solution, but till then I’ll be very glad to hear your opinion on that.
I’ve published the Tweet Me! project and uploaded the spike for the Tweet Me! application.
The project page: http://tweetme.codeplex.com/
The download page: http://tweetme.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=32384
Please leave comments if you have any suggestions or wishes.
Enjoy!
When you write markup extensions, or any other control that may work differently at runtime then design time, you may want to check if you’re in design time to pick the correct logic.
In WPF, you can call the DesignerProperties.GetIsInDesignMode attached property. In Silverlight, you may use the HtmlPage.IsEnabled property. This will work from both Blend and Cider designers.
Example:
if (DesignerProperties.GetIsInDesignMode(textBox)
{
return "In Design Time Mode";
}
return runtimeValue;
If you don’t have the dependency object in your hand, you can pass an empty DependencyObject:
if (DesignerProperties.GetIsInDesignMode(new DependencyObject())
{
return "In Design Time Mode";
}
return runtimeValue;
Last weekend I’ve tried to find a very simple twitter application for sending messages very quickly over the twitter network, so I’ve googled something like:
“Best Twitter Application for very fast Tweeting”
And not surprisingly I’ve got tons of twitter applications, but neither was simple to use nor suited to my needs. So I’ve decided to create another twitter application, were “very fast tweeting” is its main goal.
So far I’ve created a new open source project in CodePlex, not yet published, and uploaded a very first preview spike code for my new app called Tweet Me!
Tweet Me! makes it easier for tweeter users to send tweets very fast from their PC by clicking a hot-key, typing the tweet and press enter. With Tweet Me! You'll no longer have to use a complex application for just sending your daily or hourly tweets.
The open source project is developed in C# and WPF, and actually speaking I’m looking for talented WPF developers that are willing to contribute (more details later).
Meanwhile, here are two screen shots from the very first preview application.
Ohhh I almost forgot, if you liked my idea, Tweet Me! and I’ll send you the preview.
And if you wish to contribute to this open source project, just leave a comment.
“Just tweet what are you doing…”