Rich Newman

April 29, 2007

Using HSL Color (Hue, Saturation, Luminosity) to Create Better-Looking GUIs (Part 1)

Filed under: .net, c#, Color, dotnet, GUI, HSL, technology, User Interface — richnewman @ 10:52 pm

Introduction

Business applications often have multiple screens that simply present grids of data. One way of making these applications more user-friendly is to use different colors on the backgrounds for such screens. Then you can talk about the ‘blue screen’ rather than the ‘intra-day warehousing report screen’. However, if you are not careful with your color schemes it’s quite easy to end up with a very ugly application that is tiring on the eyes. This article discusses how to use the Hue-Saturation-Luminosity color model to produce better color schemes.

RGB – Red, Green, Blue

As I’m sure you’re aware, colors in .NET are normally specified using the RGB (Red, Green, Blue) color model. This is a simple additive model: we define the intensity of each component of the color (red, green and blue) on a scale of 0 to 255, where 255 represents full intensity. The individual color values are then added to give the overall color. This gives us the set of colors we are used to working with:

0,0,0 – Black (R=0, G=0, B=0)
255,0,0 – Red
0,0,255 – Blue
0,255,0 – Green
0,255,255 – Cyan
255,0,255 – Magenta
255,255,0 – Yellow
255,255,255 – White

RGB reflects the way in which colors are actually displayed on a computer screen. A pixel on a screen is made up by combining intensities of red, green and blue, usually with 8 bits (256 values) for each of red, green and blue.

Drawbacks of RGB

Whilst RGB is easily understood, it isn’t necessarily all that easy to use it to specify sets of colors that go well together. As an example see the set of screens below. These colors were taken from an actual business application. The developers had tried hard to find sets of colors that went well together. This color scheme isn’t particularly bad, but several screens are clearly too bright and garish, and the color scheme isn’t consistent across the screens.

Original Color Scheme

HSL – Hue, Saturation, Luminosity

HSL (Hue, Saturation, Luminosity) is an alternative way of specifying color values. In fact, it’s an alternative way of specifying the same color values as we get with RGB: there is a simple mapping between any color specified using RGB and one using HSL. That is, you don’t get any extra colors with HSL.

HSL is a more intuitive way of dealing with color. Roughly, hue corresponds to the actual color, saturation corresponds to the intensity of the color, and luminosity corresponds to brightness. Microsoft treats these attributes as being on a scale from 0 to 240 (for no good reason that I can see).

Hue

Hue is reasonably self-explanatory, and starts at 0 for red. As its value increases it goes through the colors of the rainbow. Roughly the values are:

Red 0
Orange 20
Yellow 40
Green 80
Cyan 120
Blue 160
Violet 180
Magenta 200
Pink 220

(Those of you wondering why that isn’t the ‘normal’ sequence for the rainbow, and in particular where indigo is, see http://en.wikipedia.org/wiki/ROY_G._BIV)

Luminosity

Luminosity is like the brightness control on a computer or television screen. Maximum luminosity (240) corresponds to white for any color, whilst minimum luminosity (0) corresponds to black. The middle value (120) corresponds to the pure color.

Saturation

Saturation is in some ways more like contrast, although for a single color contrast doesn’t make a lot of sense. Minimum saturation (0) corresponds to gray, whilst maximum saturation (240) corresponds to the pure color.

Examples

So for example, our hue could be pure red, which in the RGB space would have values (255, 0, 0). In the HSL space this corresponds to a hue of 0 (= red), a saturation of 240 (= maximum saturation, no gray), and a luminosity of 120 (= ‘normal’ brightness). The HSL equivalents of the RGB values given above are:

0,0,0 – Black (H=0, S=0, L=0)
0,240,120 – Red
160,240,120 – Blue
80,240,120 – Green
120,240,120 – Cyan
200,240,120 – Magenta
40,240,120 – Yellow
0,240,240 – White

Part 2

Part 2 will follow. This will discuss Microsoft’s support for HSL, show how to use the HSL color space to create good-looking grid user interfaces, and will give a C# HSLColor class that can be used in place of the usual Color class in your applications.

April 22, 2007

Reasons Not to Use TableAdapters in .NET 2.0

Filed under: .net, c#, dataset, dotnet, table adapter, technology — richnewman @ 8:06 pm

Introduction

We have decided to ban the use of TableAdapters on our project for the time being. This is because there is no way that we can see of doing centralized connection management using them. Every TableAdapter is capable of creating its own new Connection object. There’s no easy way of overriding that so that whenever a TableAdapter is instantiated it will call out to use a Connection provided by another component. In general we don’t want connections to be available in the TableAdapters component at all.

If anyone can help with this it would be appreciated.

We can get the behaviour we want with DataAdapters (indeed we already do have this in place). I will explain in more detail why this is below.

Why is Connection Management Important?

I am personally paranoid about this because several years ago I was given responsibility for a project that had a number of fundamental problems. The most entertaining of these was that immediately after logging on to the system any given user would have twelve database connections. This number then grew over time. Users could have hundreds of simultaneous connections. You can imagine how well the application ran.

This was exceedingly difficult to address. Connections would be cached and then just re-used assuming they were open. So closing one could easily mean an exception later in execution when the code tried to access the database. We had to attempt to trawl through the code finding every place the database was accessed. This could be almost anywhere because the layering was so poor.

Current solution with Data Adapters

Ever since then I’ve made sure that the projects I’m working on have only one database connection at most open at any given time. The best way to do this is not to let developers anywhere near the connection string, but to give them a series of methods that handle it for them. So we have a separate library that does connection management. This exposes public methods like ExecuteScalar and ExecuteNonQuery which work exactly like the .NET equivalents, except that no connection is necessary.

Whenever this library gets a call of this kind it gets the connection string (which involves decrypting a password in the configuration file), creates a connection, executes the SQL passed in, and closes the connection. Neither the connection nor the connection string is exposed outside of the library.

Similarly we have public methods called FillDataSetUsingDataAdapter and UpdateDataSetUsingDataAdapter that take a DataSet and a DataAdapter and fill or update on the correct database connection regardless of what connections are on the adapter. We can pass the generic DataSet type in to these methods even though our DataSets are strongly-typed because the strongly-typed DataSets inherit from System.Data.DataSet. All our DataAdapters are of type OleDbDataAdapter and we can work with that in the methods.

Advantages of this Approach

Note that there’s no SQL at all in the connection management library. All the SQL is written in a separate ‘data access’ library, which, as mentioned, has access to these methods but not to the correct connection to the database. This means developers are not tempted to just instantiate a connection and use it.

Furthermore, developers can create DataAdapters and typed DataSets using Microsoft’s code generation tools against any copy of the database. Although these will get cached in the data access library, in production code the connections will be redirected to point at the production database. There’s no danger of mistakenly accessing a development database in production.

Finally all of our calls to set up connections go through one method, and all of our database calls go through one small set of routines. This lets us run specific code whenever we connect to a database. For example we have a read-only database. If the code tries to write to it then the exception is intercepted and a sensible error message put into it so the user will know what’s going on. We can also do database logging, and ensure that all connections are immediately closed after they are used (assuming that’s what we want: we can just as easily turn this off).

Table Adapters

At present we can’t see how we can do a similar thing with table adapters, at least not without writing custom code every time we set one up (which rather defeats the point of them).

Firstly they are types generated on the fly (inheriting System.Component) so we can’t pass them into a separate library in the same way as Data Adapters. We could give them a custom interface and pass them in, or just extract the underlying adapter and pass that in, but this isn’t giving us any advantages over a data adapter and involves more code.

So we’re being pushed towards making the connection string available in the data access component where they will be set up, which isn’t very satisfactory. In my earlier article I was discussing the difficulty of doing custom password decryption in this scenario, but in fact we can achieve this by a call at application start up that constructs the connection string and sets it on the Settings class (which needs to be extended with a setter to do this).

There is an alternative to making the connection string generally available to database code as above. This is to extract the table adapters into yet another library that does have access to the connection string, whilst leaving the existing data access component alone. This doesn’t get rid of the problem that our connections are not now all set up in the connection manager though.

Conclusion

Table adapters don’t seem to be giving us many advantages when compared with data adapters, and are giving us problems with our connection management. For the time being we’ll leave them alone.

April 15, 2007

A Beginner’s Guide to calling a .NET Library from Excel

Introduction

It’s actually very easy to call a .NET library directly from Excel, particularly if you are using Visual Studio 2005. You don’t need Visual Studio Tools for Office. However there doesn’t seem to be an easy guide on the internet anywhere. MSDN help is quite good on the subject, but can be a little confusing. This article is an attempt to redress the situation.

This article was updated 24th August 2007 to cover Excel 2007 and to clarify the issues with intellisense.

A Basic Walk Through

We’ll start by walking through a very basic example. We’ll get Excel to call a .NET method that takes a string as input (for example “ World”) and returns “Hello” concatenated with that input string (so, for example, “Hello World”).

1. Create a C# Windows class library project in Visual Studio 2005 called ‘DotNetLibrary’. It doesn’t matter which folder this is in for the purposes of this example.

2. To call a method in a class in our library from Excel we need the class to have a default public constructor. Obviously the class also needs to contain any methods we want to call. For this walk through just copy and paste the following code into our default class file:

using System;
using System.Collections.Generic;
using System.Text;
 
namespace DotNetLibrary
{
    public class DotNetClass
    {
        public string DotNetMethod(string input)
        {
            return "Hello " + input;
        }
    }
}

That’s it: if you look at existing articles on the web, or read the MSDN help, you might think you need to use interfaces, or to decorate your class with attributes and GUIDs. However, for a basic interop scenario you don’t need to do this.

3. Excel is going to communicate with our library using COM. For Excel to use a COM library there need to be appropriate entries in the registry. Visual Studio can generate those entries for us.

To do this bring up the project properties (double-click ‘Properties’ in Solution Explorer). Then:
i) On the ‘Application’ tab click the ‘Assembly Information…’ button. In the resulting dialog check the ‘Make assembly COM-visible’ checkbox. Click ‘OK’.
ii) On the ‘Build’ tab check the ‘Register for COM interop’ checkbox (towards the bottom: you may need to scroll down).

4. Build the library.

5. Now start Excel and open a new blank workbook. Open the VBA code editor:
i) In Excel 2007 this is a little difficult to find. You have to get the Developer tab visible on the Ribbon if it’s not already set up. To do this click the Microsoft Office Button (top left of the screen), then click Excel Options (at the very bottom). Check the ‘Show Developer tab in the Ribbon’ checkbox in the resulting Options dialog. Click OK. This adds ‘Developer’ to the end of the ribbon menu: click this. Then click the ‘Visual Basic’ icon at the left end of the ribbon.
ii) In earlier versions of Office (2003, XP, 2000) just go to Tools/Macro/Visual Basic Editor on the menu bar.

6. We now need to include a reference to our new library. Select ‘References’ on the Visual Basic Editor’s ‘Tools’ menu. If you scroll down in the resulting dialog you should find that ‘DotNetLibrary’ is in the list. Check the checkbox alongside it and click ‘OK’.

7. Now open the code window for Sheet1 (double click Sheet1 in the Project window). Paste the VBA code below into the code window for Sheet1:

Private Sub TestDotNetCall()
Dim testClass As New DotNetClass
MsgBox testClass.DotNetMethod(“World”)
End Sub

8. Click anywhere in the code you’ve just pasted in and hit ‘F5’ to run the code. You should get a ‘Hello World’ message box.

Getting Intellisense Working in Excel

Whilst the VBA code above compiles and executes, you will discover that intellisense is not working in the code editor. This is because by default our library is built with a late binding (run-time binding) interface only. The code editor therefore doesn’t know about the types in the library at design time.

There are good reasons for only using a late-bound interface by default: with COM versioning libraries can become difficult with early-bound interfaces. In particular, if you change the early-bound interface by adding, for example, a method in between two existing methods you are likely to break existing clients as they are binding based on the order of the methods in the interface.

For similar reasons you are heavily encouraged to code your interface separately as a C# interface and then implement it on your class, rather than using the default public interface of the class as here. You then should not change that interface: you would implement a new one if it needed to change.

For more on this see:

http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.classinterfaceattribute(vs.80).aspx
http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.classinterfacetype(VS.80).aspx

However, we can build our library to use early bound interfaces, which means intellisense will be available. To do this we need to add an attribute from the System.Runtime.InteropServices namespace as below:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
 
namespace DotNetLibrary
{
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class DotNetClass
    {
        public DotNetClass()
        {
        }
        public string DotNetMethod(string input)
        {
            return "Hello " + input;
        }
    }
}

If you change your code as above it will expose an ‘AutoDual’ interface to COM. This means it is still exposing the late-bound interface as before, but now also exposes an early-bound interface. This means intellisense will work.

To get this working:

1. Save your workbook and close Excel. Excel will lock the DotNetLibrary dll and prevent Visual Studio from rebuilding it unless you close it. Remember you need to save your new code module. If you are using Excel 2007 you will need to save as type Excel Macro-Enabled Workbook (*.xlsm). In earlier versions you can just save as a standard xls.

2. Go back into Visual Studio, change the DotNetClass as shown above, and rebuild the library.

3. Re-open your Excel spreadsheet. Once again if you are using Excel 2007 there is an extra step: you need to explicitly enable macros. A warning bar will appear beneath the ribbon saying the ‘Macros have been disabled’. Click the ‘Options’ button next to this, select ‘Enable this content’, and click OK.

4. Get the VBA code window up again (see item 5 above).

5. Excel can get confused about the interface changes unless you re-reference the library. To do this go to Tools/References. The DotNetLibrary reference should be near the top of the list now. Uncheck it and close the window. Now open the window again, find the library in the list, and re-check it (trust me, you need to do this).

6. Now run the code and it should still work (put a breakpoint in the routine and hit F5).

7. Enter a new line in the routine after the ‘MsgBox’ line, and type ‘testClass.’. When you hit the ‘.’ you should get an intellisense dropdown which shows that DotNetMethod is available. See below.

Intellisense in Excel

Let me re-iterate that this works and is fine for development, but for release code you are better off using the default late binding interfaces unless you understand the full versioning implications. That is, you should remove the ClassInterface attribute from your code when you do a release.

Deployment

In the example here we are using Visual Studio to register our .NET assembly on the workstation so that Excel can find it via COM interop. However, if we try to deploy this application to client machines we’re not going to want to use Visual Studio.

Microsoft have provided a command-line tool, regasm.exe, which can be used to register .NET assemblies for COM interop on client workstations. It can also be used to generate a COM type library (.tlb) separate from the main library (.dll), which is considered good practice in general.

As usual with .NET assemblies you have the choice of strong-naming your assembly and installing it in the GAC, or of not strong-naming it and including it in a local path. If you have strong-named your assembly and installed it in the GAC all you need to do is bring up a Visual Studio 2005 command prompt and run:

regasm DotNetLibrary.dll

If you have not strong-named your assembly you need to tell regasm.exe where it is so that it can find it to register it. To do this you need to run the command below, where c:\ExcelDotNet is the path where DotNetLibrary.dll can be found. This works fine, although it will warn you that you should really strong-name your assembly:

regasm /codebase c:\ExcelDotNet\DotNetLibrary.dll

Note that you can unregister an assembly with the /u option of regasm.

For more detail on this see http://msdn2.microsoft.com/en-us/library/tzat5yw6(vs.80).aspx

Debugging into .NET from Excel

You may want to debug from Excel into your class library. To do this:

1. Using Visual Studio 2005 bring up the Properties window for the class library.

2. Go to the Debug tab and select the ‘Start external program’ option under ‘Start Action’. In the textbox alongside enter the full path including file name to Excel.exe for the version of Excel you are using (usually in Program Files/Microsoft Office/Office).

3. On the same Debug tab under ‘Command line arguments’ enter the full path including file name to your test workbook (the .xls file, or .xlsm if you are using Excel 2007). Once you’re done it should something like below::

Project Properties for Excel

4. Now put a breakpoint in the code (in our example the sensible place is in method DotNetMethod) and hit F5 in the .NET project. The .NET code should compile and Excel should start with your workbook opened. If you now run the VBA code to call the .NET library again, as above, you should find that the code will break at the breakpoint you set in the .NET code.

Possible Problem with these Examples

One problem we have had with these examples is that Excel can get confused about which version of the .NET Framework to load if you have more than one version installed. If this happens you will get an automation error when you try to instantiate .NET objects at runtime from Excel. The .NET types will appear correctly in the Excel object browser.

The workaround for this is to tell Excel explicitly that the version of the .NET Framework that you are using is supported. To do this create a text file called Excel.exe.config and put it in the same directory as Excel.exe itself. The file should contain the text below (with the version number replaced with the .NET Framework version you are using):

<?xml version="1.0"?>
<configuration>
  <startup>
    <supportedRuntime version="v2.0.50727"/>
  </startup>
</configuration>
 

References:

Index page from MSDN
http://msdn2.microsoft.com/en-us/library/zsfww439.aspx

More on COM Interop from COM clients into .NET:
http://www.codeproject.com/vb/net/MusaExposingCOM.asp

A COM Class Wizard for C#
http://www.codeproject.com/csharp/cscomtemplate.asp

Guidelines for COM Interoperability from .NET
http://blogs.gotdotnet.com/heaths/archive/2005/03/09/391358.aspx

In Defense of regasm /codebase
http://weblogs.asp.net/savanness/archive/2003/05/29/7749.aspx

Excel/.NET versioning problems
http://krgreenlee.blogspot.com/2006/01/software-running-excel-with-net-11.html

April 8, 2007

Top-level Exception Handling in Windows Forms Applications

Introduction

In .NET exception handling has become a much simpler issue than in previous programming languages. For example, in many older languages it was considered good practice to put an exception handler in every routine, and also to return a value to indicate success or failure from each routine.

In .NET Windows Forms applications these techniques are no longer relevant, although for many developers this seems to be a well-guarded secret. In particular it is now possible (and considered good practice) to set up a top-level exception handler that will handle any unexpected exception on the main thread in a Windows application. This means that it is no longer necessary to have exception handlers in every routine.

This article will describe how to set up this top-level handler, and will discuss some of the issues in making it work effectively.

Unhandled Exceptions

So what happens if an exception occurs in our application and we don’t handle it?

In the development environment, the code will break and we’ll get a fairly friendly ‘Exception was unhandled’ dialog which allows us to investigate the exception.

However in a release build we’ll get the dialog below. We don’t really want our users wrestling with that.

UnhandledException2.jpg

Things get even worse if we run code on a background thread and an unhandled exception occurs there. We then get the dialog below if we are not running in debug. Quite why Microsoft would want to know about our threading bugs is a little beyond me, however.

UnhandledException3.JPG

So if we’re not putting exception handlers in all our routines how do we stop all this from happening? The answer is that Microsoft has provided us with a great mechanism that allows us to set up one exception handler that is hooked up at system start up and can handle all exceptions from the main thread in our code. We no longer have to write reams of boilerplate code to handle default exception handling scenarios.

The ThreadExceptionEventHandler

As discussed, for a Windows Forms application it is possible to set up an exception handler that catches any exception that occurs on the main GUI thread. To do this we need to handle the ThreadException event as shown in the attached code listing 1. Note that we need to hook up the handler for the ThreadException event BEFORE the Windows message loop is set up. In practice this means we need the line of code below before any form (including a splash screen) is displayed. If you make it the first line of code in your Main() method then you’ll find it works.

   Application.ThreadException += new ThreadExceptionEventHandler(new ThreadExceptionHandler().ApplicationThreadException); 

Here Application.ThreadException is an event in System.Windows.Forms, whilst ThreadExceptionEventHandler is the associated delegate in System.Threading. ThreadExceptionHandler is our own class that will handle all exceptions, and ApplicationThreadException is the method in it that does the handling.

If you set up and run the example code you will see that a form is shown with two buttons that can be clicked. The first throws an exception, whilst the second shows a new instance of the form. Whenever you click the ‘Throw Exception’ button on any form the exception generated is caught by our top-level exception handler.

Handling Exceptions in Debug

Furthermore, if you run the project in debug in Visual Studio the code will break immediately when the exception is thrown at the point that it is thrown. This is exactly the behaviour you want in general. If you’ve got an exception when developing you want to be able to see immediately what’s happened so that you can fix it. With other exception handling scenarios you are often floundering around trying to recreate the exception with breakpoints in appropriate places.

Note that if you want your top-level exception handler to execute after the code has broken in debug you can just hit F5 to continue. If you don’t want your code breaking on exceptions in debug at all, you can disable this behaviour in Visual Studio 2005 by going to the Exceptions option on the Debug menu and clearing the ‘Break when exception is:’ ‘User-unhandled’ checkbox under ‘Common Language Runtime Exceptions’. If you expand Common Language Runtime Exceptions you can enable or disable this behaviour for individual exception types.

Getting the ThreadException Event to Fire Correctly

It should be straightforward to get the ThreadException event firing in code listing 1. This is a very simple example after all. In practice getting this working can prove a little tricky. There are two rules to remember:

1. The handler needs to be set up BEFORE the Windows message loop is constructed.
This means it needs to be before Application.Run(MainForm). If you are showing a splash screen at start up, or even a ‘Do you want to upgrade to the new version?’ form before calling Application.Run, you need to put your Application.ThreadException += new ThreadExceptionEventHandler(… statement before the code to do those things is called.

2. The handler is not in force until the initialization of your main form has completed.
Any exception in the constructor of the main form, or in any code you execute prior to Application.Run(MainForm) will not be handled by the top-level handler. Of course, putting complex initialization code in the constructor of a form is a bad idea in general, not least because it gets run by the designer when you open the form in Visual Studio. However, this does mean that you may need a try..catch block around any initialization code that runs before the main form is launched in case it throws an exception.

Finally don’t forget that by default in debug you get the same ‘Exception was unhandled’ dialog whether the ThreadException event is hooked up correctly or not. It’s what happens when you hit F5 (or disable the ‘Break when exception is user-unhandled’ options) that tells you whether you’ve got it working – see the paragraph above.

Examples of how to handle these rules are given below.

Example 1: Getting the ThreadException to Fire Correctly with a Splash Screen

Consider the start up code in code listing 2.

The ThreadException handler will NOT work in this scenario. To fix this move the set up code for the handler to be the first line in routine Main().

See code listing 3

But remember that if DoLongRunningStuff() throws an exception it will NOT be caught by the top-level handler. To avoid unhandled exceptions at start up you may want to put a try..catch block around the start up code.

Example 2: Handling Exceptions Correctly with Main Form Initialization Code

You may have some initialization code you want to run in your MainForm class. As discussed, it’s not a good idea to put that in the constructor, not least because the ThreadException handler won’t catch any exceptions thrown in it. It’s better to have a separate Initialize() routine in general. But how do we call this so we have an exception handler in place? One approach is to re-use the try..catch block set up in Example 1 above. 

See code listing 4.

We now have a splash screen displayed throughout the time our initialization code is running, with an exception handler in place. All subsequent exceptions will be caught by our top-level exception handler.

Handling the Exception

For the example code our actual exception handler (ThreadExceptionHandler().ApplicationThreadException) simply shows a message box. For production code you will probably want a more sophisticated exception handler than this.

Close the Application?

Our top-level handler leaves the Windows message loop for the application running normally, assuming it can do so. So if an exception occurs in a routine you’ll get a sensible exception message, some options for dealing with it, and can then carry on using the system. In spite of arguments to the contrary in some blogs, I think that in a Windows Forms application this is normally the behaviour you’ll want. After the first release of a system (and assuming you’ve got appropriate levels of automated testing) it’s quite rare for a bug to get through that’s going to bring the entire application to its knees. Exceptions tend to occur because developers have forgotten basic scenarios, for example attempting to save a file that’s locked because it’s open, or handling a null field in a DataSet correctly. You really don’t want the user to have to restart the application just because they attempted to save a file they already had open. If a bug does get through that leaves the application unusable our user can always shut it down themselves: most users are accustomed to the standard ‘restart to fix the problem’ scenario.

Exceptions in Threads other than the Main GUI Thread

Note that the ThreadException event handler will NOT catch exceptions thrown on any threads other than the GUI thread. I won’t discuss this in detail here, but in general it makes sense to have specific exception handlers in the individual threads that deal with any exceptions in those threads. There is also an UnhandledException event that can be used to set up a top-level exception handler for thread exceptions. This works very much like the ThreadException event described here, the difference being it handles exceptions on threads other than the GUI thread. However, if there is an exception on a non-GUI thread the application will close after running the top-level handler. It is not possible to stop this happening: as mentioned we are better off trying to handle exceptions on the threads that generate them.

April 7, 2007

Top-level Exception Handling in Windows Forms Applications – Code Listing 1

Filed under: .net, c#, dotnet, exception handling, thread, threading — richnewman @ 12:13 pm

 

This code listing is available for download. 

 

This is a very basic code example of how to use the ThreadException event to handle exceptions occurring anywhere on the main GUI thread in a Windows Forms application.  My blog article ‘Top-level Exception Handling in Windows Forms Applications’ discusses this in more detail.

 

From looking at the forums it seems many people have problems getting the ThreadException event to fire correctly.  To make sure you can get my example working I have included quite detailed instructions: you probably don’t need these though!

 

1.  Create a new C# Windows Application in Visual Studio 2005, calling it ExceptionHandling1. 

2.  Replace the code in Program.cs with the code below.

 

using System.Windows.Forms;

using System.Threading;

namespace ExceptionHandling1

{

    static class Program

    {

        /// <summary>

        /// The main entry point for the application.

        /// </summary>

        static void
Main()

        {

            Application.ThreadException += new ThreadExceptionEventHandler(new ThreadExceptionHandler().ApplicationThreadException);

            Application.Run(new Form1());

        }

        /// <summary>

        /// Handles any thread exceptions

        /// </summary>

        public class ThreadExceptionHandler

        {

            public void ApplicationThreadException(object sender, ThreadExceptionEventArgs e)

            {

                MessageBox.Show(e.Exception.Message, “An exception occurred:”, MessageBoxButtons.OK, MessageBoxIcon.Error);

            }

        }

    }

}

 

3.  Replace the code behind the default Form1 with the code below (Form1.cs):

 

using System;

using System.Windows.Forms;

using System.Threading;

namespace ExceptionHandling1

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

        }

        private void throwExceptionButton_Click(object sender, EventArgs e)

        {

            ThrowException();

        }

        private void ThrowException()

        {

            // Note that in general you shouldn’t throw a generic ApplicationException

            // but should use a more specific exception class derived from it

            throw new ApplicationException(“Monkey exception”);

        }

        private void exceptionOnNewFormButton_Click(object sender, EventArgs e)

        {

            Form1 form = new Form1();

            form.Show();

        }

    }

}

 

4.  Replace the code in Form1.Designer.cs with the code below:

 

namespace ExceptionHandling1

{

    partial class Form1

    {

        /// <summary>

        /// Required designer variable.

        /// </summary>

        private System.ComponentModel.IContainer components = null;

        /// <summary>

        /// Clean up any resources being used.

        /// </summary>

        /// <param name=”disposing”>true if managed resources should be disposed; otherwise, false.</param>

        protected override void Dispose(bool disposing)

        {

            if (disposing && (components != null))

            {

                components.Dispose();

            }

            base.Dispose(disposing);

        }

        #region Windows Form Designer generated code

        /// <summary>

        /// Required method for Designer support – do not modify

        /// the contents of this method with the code editor.

        /// </summary>

        private void InitializeComponent()

        {

            this.throwExceptionButton = new System.Windows.Forms.Button();

            this.newFormButton = new System.Windows.Forms.Button();

            this.SuspendLayout();

            //

            // throwExceptionButton

            //

            this.throwExceptionButton.Location = new System.Drawing.Point(12, 12);

            this.throwExceptionButton.Name = “throwExceptionButton”;

            this.throwExceptionButton.Size = new System.Drawing.Size(75, 51);

            this.throwExceptionButton.TabIndex = 0;

            this.throwExceptionButton.Text = “Throw Exception”;

            this.throwExceptionButton.UseVisualStyleBackColor = true;

            this.throwExceptionButton.Click += new System.EventHandler(this.throwExceptionButton_Click);

            //

            // newFormButton

            //

            this.newFormButton.Location = new System.Drawing.Point(93, 12);

            this.newFormButton.Name = “newFormButton”;

            this.newFormButton.Size = new System.Drawing.Size(75, 51);

            this.newFormButton.TabIndex = 2;

            this.newFormButton.Text = “New Form”;

            this.newFormButton.UseVisualStyleBackColor = true;

            this.newFormButton.Click += new System.EventHandler(this.exceptionOnNewFormButton_Click);

            //

            // ExceptionHandlingForm

            //

            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);

            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

            this.ClientSize = new System.Drawing.Size(202, 79);

            this.Controls.Add(this.newFormButton);

            this.Controls.Add(this.throwExceptionButton);

            this.Name = “ExceptionHandlingForm”;

            this.Text = “Exception Handling”;

            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.Button throwExceptionButton;

        private System.Windows.Forms.Button newFormButton;

    }

}

 

5.  Run this code.  If you run the code in debug, when you hit the ‘Throw Exception’ button the code will break saying that an exception has occurred (unless you’ve disabled this).  If you now hit F5 to continue you will see that the top-level exception handler is being called.  If you run the code by double-clicking ExceptionHandling1.exe you will see that the top-level exception handler has been called directly.

The Shocking Blue Green Theme. Create a free website or blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 80 other followers