When a user control is instantiated the call chain is something like this:
UserControl.Constructor
UserControl.InitializeComponent()
Application.LoadComponent()
In my experience its the final call which actually consumes all the time. That is the step where the XAML file (stored in BAML format) is loaded and converted into actual objects for use.
I have found two general approaches to reducing or eliminating the time taken here.
The following simple refactoring is easy to do and in the cases I've worked on may reduce load time by about 75% (very roughly!) Typically you would start out having something like this:
Typical UserControl
starting point
MyControl.xaml
file:
<UserControl x:Class="MyNamespace.MyControl" ... >
<Grid ... >
...
</Grid>
</UserControl>
Corresponding MyControl.xaml.cs
file:
namespace MyNamespace
{
partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
// other stuff...
}
}
}
Refactoring 1: Control
with xaml/xaml.cs files
From that starting point, modify it to derive from Control
instead of UserControl
and set the ControlTemplate
explictly:
MyControl.xaml
file:
<Control x:Class="MyNamespace.MyControl" ... >
<Control.Template>
<ControlTemplate>
<Grid ... >
...
</Grid>
</ControlTemplate>
</Control.Template>
</Control>
Corresponding MyControl.xaml.cs
file:
namespace MyNamespace
{
partial class MyControl : Control
{
public MyControl()
{
InitializeComponent();
// other stuff...
}
}
}
For whatever reason in all the examples I had tried, just doing this knocked off about 75% (roughly) of the load time. This can be a very quick and easy refactoring that has a substantial advantage.
Visual Studio is perfectly happy with this xaml / xaml.cs usage even though its a little nonstandard.
However note that the XAML will still be loaded and processed in each instantiation of the control. This is avoidable however, see the next section.
Refactoring 2: Control
with resource dictionary
Even better, in terms of performance, is to refactor the ControlTemplate
into a resource dictionary. This totally eliminates the per-control instance XAML load and (in my experience) results in almost no delay at all.
Replace the original .xaml
file with "MyControlResources.xaml
" such as the following:
<ResourceDictionary ... >
<ControlTemplate x:Key="MyControlTemplateKey">
<Grid ... >
...
</Grid>
</ControlTemplate>
<Style TargetType="{x:Type MyNamespace:MyControl}">
<Setter
Property="Template"
Value="{StaticResource MyControlTemplateKey}"
/>
</Style>
</ResourceDictionary>
This resource dictionary needs to be added to a merged dictionary in app.xaml
.
Replace the original xaml.cs
file with a plain MyControl.cs
file containing:
namespace MyNamespace
{
class MyControl : Control
{
public MyControl()
{
// other stuff...
}
}
}
Note that there is no longer any call to InitializeComponent()
. The style system is used to apply the control template whose XAML is loaded only once.
This is a somewhat more substantial refactoring but still relatively easy to carry out as usually few other code modifications are needed. Bindings originally made by ElementName
to the top level control directly should be changed to use TemplateBinding
.
You could organize the control template & style resources in other ways, this just shows an approach which directly corresponds to the original organization of code for the user control. Personally I like to have one resource dictionary corresponding to one control.