This article develops these key points:
- Developing a New Send Error Report screen in XAML
- Build an Error object that keeps all the stack trace info, so that we can pinpoint
an error.
- Build a mechanism to send the error info to the intended group (developer/administrator)
As usual, Necessary comments are provided as and when required. I have marked each
step. And also key points in red.
STEP 1: Developing a XAML Send Error Report
Below is the XAML window that would behave as a “Send Error Report” Window. Note
that I have used the FlowDocumentReader control for a professional look and set
few styles on it. You can do lot more!
<Window x:Class="Utilities.UI.UCExceptionWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Error!" Topmost="True" FontSize="12" FontFamily="Calibri"
Loaded="Window_Loaded" Name="ExceptionWindow" WindowStyle="ToolWindow"
ResizeMode="NoResize" WindowStartupLocation="CenterScreen" SizeToContent="WidthAndHeight">
<Window.Resources>
<Style x:Key="FlowDocumentReaderStyle" TargetType="{x:Type FlowDocumentReader}">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Offset="0.0" Color="#CC99CCFF" />
<GradientStop Offset="1.0" Color="White" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Name="BaseGrid">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="350"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Height="50" Name="LabelOuterException" Grid.Row="0" Grid.ColumnSpan="2" BorderBrush="LightGray" BorderThickness="1"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" ClipToBounds="False"
Content="an error has occured in the application. please send the error report to the
development team.">
</Label>
<Expander Header="details" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Left"
Margin="0,0,0,0" Name="expanderDetails" Width="80" Height="23"
VerticalAlignment="Top" Expanded="expanderDetails_Expanded" Collapsed="expanderDetails_Collapsed"></Expander>
<Button Margin="100,0,0,0" Content="send report" Name="ButtonSendReport" Height="23" VerticalAlignment="Top"
Click="buttonSendError_Click" Grid.Column="1" Grid.Row="1" Width="80">
</Button>
<Button Margin="0,0,0,0" Grid.Column="1" Grid.Row="1" Name="ButtonDontSend" HorizontalAlignment="Right" Width="80" Height="23"
VerticalAlignment="Top" Click="buttonDontSend_Click" Content="dont send">
</Button>
<FlowDocumentReader BorderThickness="1" Grid.ColumnSpan="2" Grid.Row="2"
Name="detailsViewer" OverridesDefaultStyle="False"
Style="{StaticResource FlowDocumentReaderStyle}" Height="10"
VerticalAlignment="Bottom" BorderBrush="LightGray"
IsTwoPageViewEnabled="False" IsScrollViewEnabled="True"
Visibility="Collapsed"
FontSize="12" FontFamily="Calibri" Zoom="1" Margin="0,5,0,0"
/>
</Grid>
</Window>
STEP 2: Add the code-behind. Note the use of the custom ExceptionReportEntity object. That holds the entire info
on an Error.
using System;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
namespace Utilities.UI
{
/// <summary>
/// Interaction logic for UCExceptionWindow.xaml
/// </summary>
public partial class UCExceptionWindow : Window
{
/// <summary>
/// _exceptionReportEntity
/// </summary>
private ExceptionReportEntity _exceptionReportEntity;
/// <summary>
/// Constructor
/// </summary>
public UCExceptionWindow()
{
InitializeComponent();
}
/// <summary>
/// Overloaded Constructor
/// </summary>
/// <param name=" exceptionReportEntity "></param>
/// <param name="handlingInstanceId"></param>
public UCExceptionWindow(ExceptionReportEntity exceptionReportEntity)
{
InitializeComponent();
_ exceptionReportEntity = exceptionReportEntity;
}
/// <summary>
/// Load event of the Exception Window
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (_exceptionReportEntity!= null)
DisplayError();
}
/// <summary>
/// Sends the error Report to the Web Service
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonSendError_Click(object sender, RoutedEventArgs e)
{
this.Cursor = Cursors.Wait;
try
{
//Call your web method here. To send an email.
Infrastructure.SMTPEmailer.SendEmail(
_exceptionReportEntity.FormattedErrorSubject,
_exceptionReportEntity.FormattedErrorMessage
);
}
catch (Exception ex)
{
//Do what you want with the error.
}
finally
{
this.Cursor = Cursors.None;
this.Close();
}
}
/// <summary>
/// Displays the Exception Message in the Details View
/// </summary>
private void DisplayError()
{
Paragraph paragraph = new Paragraph();
paragraph.Inlines.Add(_exceptionReportEntity.FormattedErrorMessage);
FlowDocument document = new FlowDocument(paragraph);
detailsViewer.Document = document;
}
/// <summary>
/// Does not send the Message, closes the window
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonDontSend_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
/// <summary>
/// Expands the Detail View Section
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void expanderDetails_Expanded(object sender, RoutedEventArgs e)
{
detailsViewer.Height = 110;
detailsViewer.Visibility = Visibility.Visible;
}
/// <summary>
/// Collapses the Detail View Section
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void expanderDetails_Collapsed(object sender, RoutedEventArgs e)
{
detailsViewer.Height = 0.0;
detailsViewer.Visibility = Visibility.Collapsed;
}
}
}
STEP 3: Code for the Exception Report Entity.
#region Namespaces
using System;
using System.Text;
using System.Xml.Serialization;
#endregion Namespaces
namespace Utilities
{
/// <summary>
/// Entity for reporting errors
/// </summary>
[Serializable]
public class ExceptionReportEntity
{
private string _userName;
private string _applicationEnvironment;
private string _applicationVersion;
private Guid _handlingInstanceId;
private InternalException _exception;
/// <summary>
/// Gets/Sets the Handling Instance ID
/// </summary
public Guid HandlingInstanceId
{
get
{
return _handlingInstanceId;
}
set
{
_handlingInstanceId = value;
}
}
/// <summary>
/// Gets/Sets the Exception
/// </summary
public InternalException Exception
{
get
{
return _exception;
}
set
{
_exception = value;
}
}
/// <summary>
/// Gets/Sets the error generator User name
/// </summary
public string UserName
{
get
{
return _userName;
}
set
{
_userName = value;
}
}
/// <summary>
/// Gets/Sets the user's Application Environment
/// </summary
public string ApplicationEnvironment
{
get
{
return _applicationEnvironment;
}
set
{
_applicationEnvironment = value;
}
}
/// <summary>
/// Gets/Sets the user's Application Version
/// </summary>
public string ApplicationVersion
{
get
{
return _applicationVersion;
}
set
{
_applicationVersion = value;
}
}
/// <summary>
/// Returns the Formatted Error Message
/// </summary
/// <returns</returns
public string FormattedErrorMessage
{
get
{
return GetFormattedException(Exception, false);
}
}
/// <summary>
/// Gets the exception information, recursively.
/// </summary>
/// <param name="ex">The exception that occurred.</param>
/// <returns>Formatted exception message from the exception</returns>
public static string GetFormattedException(InternalException ex, bool isRecursive)
{
StringBuilder exceptionString = new StringBuilder();
if (isRecursive)
exceptionString.AppendFormat("************ Inner Exception ************{0}", Environment.NewLine);
else
exceptionString.AppendFormat("*************** Exception ***************{0}", Environment.NewLine);
exceptionString.AppendFormat("Exception Type:{0}{1}{2}", Environment.NewLine, ex.ExceptionType, Environment.NewLine);
exceptionString.AppendFormat("Exception Message:{0}{1}{2}", Environment.NewLine, ex.Message, Environment.NewLine);
exceptionString.AppendFormat("Stack Trace:{0}{1}{2}", Environment.NewLine, ex.StackTrace, Environment.NewLine);
exceptionString.AppendFormat("Source:{0}{1}{2}", Environment.NewLine, ex.Source, Environment.NewLine);
//recurse into inner exceptions
if (ex.InnerException != null)
{
exceptionString.Append(String.Format("{0}{1}", GetFormattedException(ex.InnerException, true), Environment.NewLine));
}
return exceptionString.ToString();
}
/// <summary>
/// Returns the Formatted Subject
/// </summary
/// <returns</returns
public string FormattedErrorSubject
{
get
{
string formattedErrorSubject = string.Format
("Exception Occured. Information submitted by {0} [{1},{2}]",
UserName, ApplicationVersion, ApplicationEnvironment);
return formattedErrorSubject;
}
}
}
/// <summary>
/// Internal Exception.
/// </summary>
[Serializable]
[XmlRoot("ExceptionInfo")]
public class InternalException
{
private InternalException _innerException;
private System.Exception _exception;
private string _exceptionType;
private string _exceptionMessage;
private string _exceptionSource;
private string _exceptionStackTrace;
private string _exceptionHelpLink;
public InternalException ()
{ }
public InternalException(System.Exception ex)
{
_exception = ex;
_exceptionType = ex.GetType().AssemblyQualifiedName;
_exceptionMessage = ex.Message;
_exceptionSource = ex.Source;
_exceptionStackTrace = ex.StackTrace;
_exceptionHelpLink = ex.HelpLink;
if (ex.InnerException != null)
_innerException = new InternalException(ex.InnerException);
}
[XmlElement("ExceptionTypeName")]
public string ExceptionType
{
get
{
return _exceptionType;
}
set
{
_exceptionType = value;
}
}
[XmlElement("Message")]
public string Message
{
get
{
return _exceptionMessage;
}
set
{
_exceptionMessage = value;
}
}
[XmlElement("Source")]
public string Source
{
get
{
return _exceptionSource;
}
set
{
_exceptionSource = value;
}
}
[XmlElement("StackTrace")]
public string StackTrace
{
get
{
return _exceptionStackTrace;
}
set
{
_exceptionStackTrace = value;
}
}
[XmlElement("HelpLink")]
public string HelpLink
{
get
{
return _exceptionHelpLink;
}
set
{
_exceptionHelpLink = value;
}
}
[XmlElement("InnerException")]
public InternalException InnerException
{
get
{
return _innerException;
}
set
{
_innerException = value;
}
}
[XmlIgnore()]
public System.Exception OriginalException
{
get
{
return _exception;
}
set
{
_exception = value;
}
}
}
}
STEP 4: Add the following code in the error handling place of your application. Pass
in the Exception object. It will do the rest.
/// <summary>
/// Shows the error message.
/// </summary>
/// <param name="exception">The exception.</param>
private static void ShowErrorMessage(Exception exception)
{
ExceptionReportEntity exceptionReportEntity = new ExceptionReportEntity ();
InternalException ex = new InternalException(exception);
exceptionReportEntity.Exception = ex;
exceptionReportEntity.UserName = //Pass the user name.
exceptionReportEntity.ApplicationVersion = //Pass the application version.
exceptionReportEntity.ApplicationEnvironment = //Pass the application environment.
UCExceptionWindow exceptionWindow = new UCExceptionWindow(exceptionReportEntity);
exceptionWindow.ShowDialog();
}
That’s it.
Cheers