XAML "Windows Send Error Report"

There is no control like the “Windows Send Error Report”, that can be used in our new age applications for the user to view the exception and send it to a administrator / developer group. We end up asking for screenshots and steps to reproduce. It would be great if we can see the entire stack trace and ascertain the problem quickly. And most of the end users don’t even know what that means.

This article develops these key points:

  1. Developing a New Send Error Report screen in XAML
  2. Build an Error object that keeps all the stack trace info, so that we can pinpoint an error.
  3. 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

 

By [)ia6l0 iii   Popularity  (1408 Views)