I've been thinking some more about an earlier blog that concerned an error that only manifested itself after deployment, and even then only on certain machines. I've decided it makes sense to place the call to InitializeComponent inside a try/catch. A colleague of mine had a similar problem during a demonstration. When he navigated to a particular page the application failed with a useless "The application had failed" error message, yet it works find in his development environment. It was very embarrassing.
In both cases the call to InitializeComponent was throwing an exception because of a subtle error in XAML. The exception was not being thrown inside Visual Studio even though it should have been. Both of us could have debugged the problem more easily with a proper error message. Rather than allowing the default .Net unhandled exception processing which gives a useless error message in Windows 7 and no error message at all (the application just dies) in Windows 8, I decided to put all my calls to InitializeComponent inside a try/catch and display a MessageBox before the application dies.
Start Visual Studio 2012 and create a new WPF project called TryInitializeComponent.
Add an empty WPF User Control called UserControl1 to the project and give it a little content so it looks like this.
<UserControl x:Class="TryInitializeComponent.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="Hi">
</Grid>
</UserControl>
Do a build so that we can reference it in MainWindow. Now we will deliberately construct some bad XAML like this...
<Window x:Class="TryInitializeComponent.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TryInitializeComponent"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:UserControl1 x:Key="UserControl1"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<UserControl Grid.Row="0" Content="{StaticResource UserControl1}"/>
<UserControl Grid.Row="1" Content="{StaticResource UserControl1}"/>
</Grid>
</Window>
You would expect this to include the user control twice so when we run the application we should see the word "Hi" twice. If you run it, you will see this isn't what happens. We only see "Hi" once. This is a clue we did something wrong - but we don't get a compile time or run time error.
If you deploy this application there is about a 50/50 chance it will work the same as in Visual Studio. On the other installations you will get an error message saying the application has failed, then it will die. This is the Windows 7 standard unhandled exception processing. In Windows 8 the application dies with no error message at all.
This is what I'm doing on all my pages and user controls now.
namespace TryInitializeComponent
{
public partial class MainWindow : Window
{
public MainWindow()
{
try
{
InitializeComponent();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
No comments:
Post a Comment