Skip Navigation LinksHome > SSW Standards > Rules > SSW Rules to Better Windows Forms Applications

It was a pleasure to have you in the office again. I continue to be impressed by the quality of work and advice you provide. Mark Pigram - Contracts Online http://grosvenor.com.au
 

We believe that most applications will look the same. Some developers create "cool" apps (like the skinning on Windows Media Player) but we wouldn't. Maybe because we are boring, maybe because we mainly create business applications, but we always aim to create consistent user interfaces.

Do you agree with them all? Are we missing some? Let us know what you think.

Rules to Better Windows Forms Applications

Developer
Figure: A developer
  1. Do you know why you choose Windows Forms?
  2. Do you design a mockup UI first?
  3. Do you use code generators?
  4. Do you use red and yellow colors to distinguish elements in the designer?
  5. Do your applications support XP themes?
  6. Do you use inherited forms for consistent behaviour?
  7. Do you display consistent information?
  8. Do you encapsulate (aka lock) values of forms?
  9. Do you know when to use User Controls?
  10. Do you know how to design a user friendly search system?
  11. Do you use Validator controls?
  12. Do you use DataSets or create your own business objects?
  13. Do your Windows Forms have a StatusBar that shows the time to load?
  14. Do you not cache your lookup data in your window forms application?
  15. Do you use the designer for all visual elements?
  16. Do you always use the Visual Studio designer for data binding where possible?
  17. Do you avoid using MDI forms?
  18. Do you have a correctly structured common code assembly?
  19. Are your Data Access Layers compatible with Web Services?
  20. Do you log all errors (with SSW Exception Manager)?
  21. Do you implement trace logging (with Log4Net)
  22. Do you make a strongly-typed wrapper for App.config?
  23. Do you keep the standard .NET DataGrid?
  24. Do you replace the standard .NET Date Time Picker?
  25. Do you avoid 3rd party menus & toolbars?
  26. Do your List Views support multiple selection and copying?
  27. Do you use an image button for opening a web page taking action?
  28. Do you use "Ok" instead of "OK"?
  29. Do your forms have Accept and Cancel buttons?
  30. Do you name all your "OK" buttons to be an Action eg. Save, Open etc?
  31. Do you make Enter go to the next line when you have a multi-line textbox rather than hit the OK button?
  32. Do you make common controls with consistent widths?
  33. Do your Windows Forms applications support URLs?
  34. Do you include back & undo buttons on every form?
  35. Do you use NUnit to write Unit Tests?
  36. Do you save user settings and reuse them by default?
  37. Do you have ResetDefault() function to handle messed up user settings?
  38. Do you use Threading to make your user interfaces more responsive?
  39. Do you use two lines height to display file name in the text box?
  40. Do you use Status Control?
  41. Do you prevent users from running two instances of your application?
  42. Do you add a "(customized)" column in grid if there are default values?
  43. Do you know there should be a standard menu item "Check for Updates"?
  44. Do you use Web Service to send emails?
  45. Do your label beside input control(Textbox, Combobox, Datetime, etc...) have colon?
  46. Do you always use GridView instead of ListBox?
  47. Do you know how to make .Net wrapper work on both x64 and x86 platforms?
  48. Do you set the ScrollBars property if the TextBox is Multiline?
  49. Do you know how to run write application to run with UAC turn on?
  50. Do you use Auto wait cursor on your windows application?
  51. Do you make your add/delete buttons crystal clear?
  52. Do you always set FirstDayOfWeek to Monday on a MonthCalendar?
  53. Do you always set ShowToday or ShowTodayCircle to true on a MonthCalendar?
  54. Do you set PasswordChar to (*) on a TextBox on sensitive data?
  55. Anchoring and Docking - Do you use Anchoring and Docking (full) if you have a multiline textboxes?
  56. Anchoring and Docking - Do you use Anchoring and Docking (horizontal only) with single line textboxes?
  57. Do you know TextAlign should be TopLeft or MiddleLeft?
  58. Do you know other UI rules?
  1. Do you know why you choose Windows Forms?

    Almost everyone assumes today to use web forms for broad reach because of easy installation and cross platform compatibility. That is correct.

    In the old days (1995-2000) companies used Windows Forms, later (2000-2007) they rolled their own ASP.NET solution, however since then (2007+) SharePoint has become the default choice for an intranet. When you need something richer and you can control the environment, Windows Forms (needs .NET Framework) is still the choice. Silverlight (needs the Silverlight Runtime) is the way to go.

    1. Bandwidth - Presentation Layer
      Only the data is transferred from the server, not the presentation code. Web forms must download the data and the rendered UI taking up large bandwidth.
    2. Bandwidth - Compression
      Data transfer can be compressed and uncompressed to use less bandwidth.
      For example, using a Pkzip scale (1-9) of 6, we used the Open source algorithm 'Blowfish' to compress/encrypt 240K of data to 30K. i.e. 87% compression.
    3. Caching
      If you are going to the same record within a certain time period, Windows forms will retrieve the data from cache instead of calling the data service again.
      For example, when you click search on a Windows form, you don't have to do a request again if the search was done recently.
    4. Faster Server
      Because of the bandwidth advantages above, the server will make less requests and hence runs faster. The client has become thicker, using more processing power and capable of more complex business logic.
    5. Richer Interface
      The application's interface can be richer as you can design your own custom controls and do not need complicated resource-intensive and complex DHTML and JavaScript.
    6. More Responsive
      The interface will respond quicker to your clicks, no need to post a request for an interface response. i.e. no 10 second latency
    7. Better Development
      Development is much easier with quick feedback. There are no compliance issues to follow as in web development with browsers.
    8. More people are happy!
      By choosing windows forms you are making the developer, end user and accounts groups happier. The only group which may rather a Web solution is the network admins.
    Group Browser Based Rich Client
    Network Admins Yes No
    Developers No Yes
    End Users No Yes
    Accounts No Yes
    Figure: Table of who benefits from Windows Forms, and Web Forms
     
  2. Do you design a mockup UI first?

    I've seen so much time wasted on complex documentation. I'm all for detailed analysis of specific business logic, but most user requirements can be encapsulated in screen mock-ups of the application. Nutting down the detail in screen mockups with the client brings more ideas that produces a better end product. See Do you make dummy screens before you code?

    The database schema should be designed before the mockup UI is started. Code Generation should also be done at this stage.

  3. Do you use code generators?

    Code generators can be used to generate whole Windows and Web interfaces, as well as data access layers and frameworks for business layers, making them an excellent time saver. I dont care which one you use as long as you invest the time and find one you are happy with. The one important thing is they must have command line support and the files they generate should be recognizable as code generated by prefix or a comment like Dont touch as this was automatically generated code. Make it easy to run by putting all the command line operations in a file called '_Regenerate.bat'.

    Therefore I say a _Regenerate.bat file must exist under the solution items to recreate data access layer and stored procs.

    Regenerate
    Figure: The _Regenerate.bat file under solution items

    The built in Data Form Wizard in Visual Studio .NET is not any good. I prefer other code generators like:

    • CodeSmith - Good for generating strongly-typed collections
      Note: it also includes templates for Rocky Lhotka's CSLA architecture from a SQL Server database.
    • RAD Software NextGeneration - Generates an entire application including a Data access layer and stored-procedures
  4. Do you use red and yellow colours to distinguish elements in the designer?

    Use colors on incomplete is so useful in design time:

    • Red = Controls which are incomplete, e.g. An incomplete button
    • Yellow = Controls which are deliberately invisible that are used by developers e.g. Test buttons

    Usually these controls are always yellow. However sometimes new areas on forms are made red and visible, so you can get UI feedback on your prototypes. Since they are red, the testers know not to report this unfinished work as a bug.


    Figure: Invisible controls highlighted in yellow, and incomplete items highlighted in red

  5. Exception: when you have a placeholder form that pops up saying it is yet to be implemented

  6. Do your applications support XP themes?

    All applications should be compatible with the Windows XP user interface and should be fully themed. Applications that do not use XP themes look like they were designed only for an earlier version of Windows. Mixing themed and non-themed controls looks equally unprofessional.


    Figure: XP themes are not used (bad)


    Figure: XP themes are used (good)

    Implementing XP Themes

    We recommend using manifest file to support XP Themes in .NET. Follow this to use the manifest file.

    1. Set the FlatStyle Property in all our controls to "System"

      Button FlatStyle System Designer

      Figure: How to set the Button's FlatStyle Property
       
    2. Copy XPThemes.manifest file to your bin folder
      By default, you can get it from C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\XPThemes.manifest
    3. Rename "XpThemes.manifest" to "ApplicationName.exe.manifest"
    4. Rename XPThemes.manifest to ApplicationName.exe.manifest

    Note: in .NET 1.1 you can use Application.EnableVisualStyles to support XP Themes. This approach is not recommended because it can cause an 'SEHException' to be thrown and some common controls could disappear.  For more information see Bugs reported in Windows Forms Forums.

    We have a program called SSW Code Auditor to check for this rule.

    It checks that all your appropriate controls are set to FlatStyle.System and that you have included a manifest file in your solution.


  7. Do you use inherited forms for consistent behaviour?

    If you ask a new .NET developer (from the Access or VB6 world) what is the best thing about .NET Windows Forms, most of your answers will be "Form Inheritance" that allows them to keep a nice consistent look for all forms. If you ask them a couple of months later, they will probably tell you the worst thing about .NET Windows Forms is "Form Inheritance". This is because they have had too many problems with the bugs in the form designer regarding this feature. Many abandon them altogether and jump on the user control band wagon. Please don't I have a solution to this....

    I think if you can keep the level of form inheritance to a minimum, then you may not see the problem or at least you will experience the problem less. Anyway even if you do, stop whinging and just close down Visual Studio.NET and restart. You don't change the base form that often anyway.  

    Well how do you keep it to a minimum? Well make the first base form without any controls, only code (to make it as flexible as possible and avoid having a multitude of base forms).

    We try to keep the number of controls on inherited forms, and the levels of inheritance to a minimum, because it reduces the risk of problems with the Visual Studio Designer (you know when the controls start jumping around, or disappearing from the Designer, or properties getting reset on inherited copies or even the tab order getting corrupted). Designer errors can also occur in the task list if the InitializeComponent method fails.

    Every form in your application should inherit from a base form which has code common to every form, for example:

    • Company Icon
    • Remembering its size and location - Code sample to come in the SSW .NET Toolkit
    • Adding itself to a global forms collection if SDI (to find forms that are already open, or to close all open forms)
    • Logging usage frequency and performance of forms (load time)


    Figure: Base Form for all SSW applications with SSW icon

    a) Sorting out the StartPosition:

    1. CentreParent only for modal dialogs (to prevent multi-monitor confusion)
    2. CentreScreen only for the main form (MainForm), or a splash screen
    3. WindowsDefaultLocation for everything else (99% of forms) - prevents windows from appearing on top of one another

    b) Sorting out FormBorderStyle:

    1. FixedDialog only for modal dialog boxes
    2. FixedSingle only for the the main form (MainForm) - FixedSingle has an icon whereas FixedDialog doesn't
    3. None for splash screen
    4. Sizable for everything else (99% of forms) - almost all forms in an app should be resizable

    Form border style

    We have a program called SSW Code Auditor to check for this rule.

    c) Sorting out a base data entry form:

    1. Inherited from the original base form
    2. OK, Apply and Cancel buttons
    3. Menu control
    4. Toolbar with New, Search and Delete

    Data entry base form
    Figure: Base data entry form with menu, toolbar and OK, Cancel & Apply buttons

    Note: The data entry base form has no heading - we simply use the Title Bar

    We have a program called SSW .NET Toolkit that implements inherited forms

  8. Do you display consistent information?

    When you have a link in your application, use the same text layout as below and a "More" hyperlink to the same page with the same description. The resulting effect is when the user clicks on the "More" hyperlink, the page will begin with exactly the same information again. This ensures the user is never confused when navigating from your application to a link.



    Figure: See how the text in the application is reflected in the link

  9. Do you encapsulate (aka lock) values of forms?

    One useful feature of inherited forms is the ability to lock the value of certain properties on the inherited copy, e.g.:

    • Font - we want to maintain a consistent font across all forms
    • BackColor - changing the background color prevents the form from being themed
    • Icon - we want all of our forms to have the company Icon

    This can be achieved with the following code, which works by hiding the existing property from the designer using the Browsable attribute. The Browsable attribute set to False means "don't show in the the designer". There is also an attribute called EditorBrowsable, which hides the property from intellisense.

    C#:

    using System.ComponentModel;
    [Browsable(false)] // Browsable = show property in the Designer
    public new Font Font
    {
       get
       {
          return base.Font;
       }
       set
       {
          //base.Font = value; //normal property syntax
          base.Font = new Font("Tahoma", 8.25);
          // Must be hard coded - cannot use Me.
       }
    }

    VB.NET:

    Imports System.ComponentModel
    <Browsable(False)> _
    Public Shadows Property Font() As Font
       Get
          Return MyBase.Font
       End Get
       Set(ByVal Value As Font)
          'MyBase.Font = Value 'normal property syntax
          MyBase.Font = Me.Font
       End Set
    End Property

    Font property
    Figure: Font Property Visible

    Font property
    Figure: Font Property Hidden

  10. Do you know when to use User Controls?

    User controls allow you to have groups of elements which can be placed on forms.

    Bad: User controls can be really misused and placed in forms where they shouldn't  be. An example of that is shown below, under the components directory the user controls placed and used only once at a time during the application flow. There is much more coding responsibility on the developer to load those controls correctly one at a time inside the main form.

    Bad user control
    Figure: Bad use of user controls - all the forms in the application are user controls (bad)

    Bad User control
    Figure: Bad use of user controls - all of the controls on this form are on a user control, but are only used once (bad)

    Good: User Controls are best used for recurring or shared logic either on the same form or throughout the application. This encourages code reuse, resulting in less overall development time (especially in maintenance). Example, the figure below shows the good use of User Controls, the address control is repeated three times but coded once.

    Good user control
    Figure: User controls are only used for shared controls (good)
     

    User Control
    Figure: The Address User Control is repeated (good)

    Exception: User controls can be made for tab pages (e.g Tools->Options) and search pages. This allows the breakdown of complex forms, and development by different developers. (See figure below)

    Tab Format
    Figure: User controls are OK in tab pages (exception)

    As a summary. The pros of User Controls are:

    • You can use a user control more than once on the same form eg. Mailing Address, Billing Address
    • You can reuse logic in the code behind the controls e.g. Search control
    • User controls are less prone to visual inheritance errors
    • When used in a form with multiple tab pages - and each tab page potentially having a lot of controls, it is possible to put each tabpage into a separate user control
    • Reduce lines of generated code in the designer by splitting it into multiple files
    • Allow multiple persons to work on different complex tabpages

    However the cons are:

    • You lose the AcceptButton and CancelButton properties from the Designer eg. OK, Cancel, Apply. Therefore the OK, Cancel and Apply buttons cannot be on User Controls.

  11. Do you know how to design a user friendly search system?

    Bad search
    Figure: Search fields are on the same form as the data entry controls (bad)

    Search
    Figure: Search functionality on a dedicated form with a recently updated records and standard search and (good) (see the SSW .NET Toolkit for all the search screen captures)

    Therefore I believe search system should:

    1. Be separate from the data entry fields (on a different form) - this avoids confusion **IMPORTANT**
    2. Have a "Simple" tab this shows minimum fields, that is just one like google.
      eg. A customer calls, they said they were from Winkleton, but I'm not sure what that is. Do I put it in the Region, City or Address fields? so you need to simply search in all fields with one single text box.
    3. Have a "Recent" tab this shows the most recent records opened/updated
    4. Have a "Common" tab this shows the common fields
      Note: I prefer this over customers needing to learn prefixes like Googles (for example, "city:winkleton").
    5. Have an Advanced tab only for power users for building up a WHERE clause


    We have a program called SSW .NET Toolkit that implements this cool Search Control

  12. Do you use Validator controls?

    Validation is extremely important on a data entry form. There are two ways to do validation:

    1. ErrorProvider control
      The ErrorProvider control is code intensive. You must manually handle the Validating event of each control you want to validate, in addition to manually running the validation methods when the OK or Apply button is clicked.
      Private Sub productNameTextBox_Validating(ByVal sender As Object, _
         ByVal e As System.ComponentModel.CancelEventArgs) Handles _
         productNameTextBox.Validating
      
         ValidateProductName(False)
      
      End Sub
      
      Private Function ValidateProductName(ByVal force As Boolean) _
         As Boolean
      
         If Me.productNameTextBox.Text.Length = 0 Then
            Me.errorProvider.SetError(Me.productNameTextBox,
               "You must enter the Product Name.")
      
            If force Then
               MessageBox.Show("You must enter the Product Name.", _
                  Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning)
            End If
      
            Return False
         Else
            Me.errorProvider.SetError(Me.productNameTextBox, _
               String.Empty)
            Return True
         End If
      
      End Function
      
      Private Function ValidateInput() As Boolean
      
         Dim force As Boolean = True
         Dim isValid As Boolean = ValidateProductID(force)
      
         If Not isValid Then
            force = False
         End If
      
         isValid = ValidateProductName(force)
      
         If Not isValid Then
            force = False
         End If
      
         isValid = ValidateCategory(force)
      
         Return isValid
      
      End Function
      
      Private Sub okButton_Click(ByVal sender As Object, _
         ByVal e As System.EventArgs)
      
         If Me.ValidateInput() Then
            'Test
         End If
      
      End Sub
      Figure: Lots of code but no balloon tooltips (bad)
      Private Sub productNameTextBox_Validating(ByVal sender As Object, _
         ByVal e As System.ComponentModel.CancelEventArgs) _
         Handles productNameTextBox.Validating
      
         ValidateProductName(False)
      
      End Sub
      
      Private Function ValidateProductName(ByVal force As Boolean) _
         As Boolean
      
         If Me.productNameTextBox.Text.Length = 0 Then
            Me.errorProvider.SetError(Me.productNameTextBox, _
               "You must enter the Product Name.")
      
            If force Then
               If Me.balloonToolTip.IsSupported Then
                  Me.balloonToolTip.SetToolTip(Me.productNameTextBox, _
                     "You must enter the Product Name.")
               Else
                  MessageBox.Show("You must enter the Product Name.", _
                     Me.Text, MessageBoxButtons.OK,
                     MessageBoxIcon.Warning)
               End If
            End If
      
            Return False
         Else
            Me.errorProvider.SetError(Me.productNameTextBox, _
               String.Empty)
            Return True
         End If
      
      End Function
      
      Private Function ValidateInput() As Boolean
      
         Dim force As Boolean = True
         Dim isValid As Boolean = ValidateProductID(force)
      
         If Not isValid Then
            force = False
         End If
      
         isValid = ValidateProductName(force)
      
         If Not isValid Then
            force = False
         End If
      
         isValid = ValidateCategory(force)
      
         Return isValid
      
      End Function
      
      Private Sub okButton_Click(ByVal sender As Object, _
         ByVal e As System.EventArgs)
      
         If Me.ValidateInput() Then
            'Test
         End If
      
      End Sub
      Figure: Lots of code but balloon tooltips are used (good)

      Note: The component for balloon tooltips can be found in the SSW .NET Toolkit.

      The error provider has the advantage over the extended provider that it can be used with balloon tooltips. If you are not using balloon tooltips, however, the error provider should not be used.

      Error provider
      Figure: .NET ErrorProvider Control with a custom balloon tooltip
       

    2. SSW Extended Provider
      The SSW Extended Provider integrates with the ErrorProvider control to provide the same functionality, but requires no code to implement (everything can be done in the Designer).

      Extended provider
      Figure: SSW Extended Provider controls and properties on a TextBox
       
    We have a program called SSW .NET Toolkit that implements this cool Error Provider Control

  13. Do you use DataSets or create your own business objects?

    In .NET, there are two ways to pass data through the layers of your application. You can:

    • Use DataSet objects, OR
    • Write your own custom business objects

    There are two very different opinions on this matter amongst .NET developers...

    The PROs of the DataSet object:

    • Code Generation
      Strongly typed DataSet objects can be created automatically in Visual Studio. Custom business objects must be laboriously coded by hand.
    • CRUD functionality
      DataSets
      , when used with data adapters, can provide CRUD (Create, Read, Update, Delete) support. You must manually implement this functionality with custom business objects.
    • Concurrency
      Support for concurrency is part of the DataSet object. Again, you must implement this yourself in a custom business object.
    • Data binding
      It is difficult and time-consuming to write custom business objects that are compatible with data binding. The DataSet object is designed for data binding.

    The PROs of Custom Business Objects:

    • Better performance
      The DataSet object is a very heavy object and is memory-intensive. In contrast custom business objects are always much more efficient. Business objects are usually faster when manipulating data, or when custom sorting is required.
    • Business objects allow you to combine data storage (NOT data access) and business logic (e.g. validation) in the one class. If you use DataSet objects, these must be in separate classes.

    Microsoft's official word on this matter is explained in Designing Data Tier Components and Passing Data Through Tiers.  

    The Case for Business Objects
    At SSW we mostly choose datasets as we believe you get more for free. However all the the features you get in the dataset can be manually coded up in business objects

    E.g. For business objects you must manually code up the bindings, with datasets however you may use the designer for binding straight after designing the dataset. This layer should be code generated - so it doesn't matter much.
    In Visual Studio 2005, binding to business objects is supported in which case we might be swayed to use business objects.

    Exception: Real complex formssay 500,000 lines of C# code
    Datasets are a tool for representing relational data in an object oriented world. They are also slower across networks. Datasets are fantastic for maintenance forms (an editable grid with a couple of checkboxes and text boxes and a save button), but terrible for real complex forms. In a complicated scenario you might have a Customer object. An Order form has a reference to this customer object that it uses to display. When a process is run on the Customer invoked from the Order, you can simply pass a reference to the customer, and if something changes, fire an event back to the Order. If datasets were used, you would be either passing datasets around (which some may say is not very safe, or good OO) or pass an ID around and have the process load the data again.

    Also it appears .NET 2.0's BindingList makes binding extremely easy along with IEditableObject. But in most cases, you don't even need to implement these.

    Rocky Lhotka recently appeared on a .NET Rocks! episode and they had a big discussion of business objects versus datasets. The use of either must change on a case by case basis. Datasets do allow you to get more for free, but if one day management decide you need to do something a little out of the ordinary, there will be problems. In contrast, business objects take longer to write (this can be minimized with a good code generator and custom templates), but stand the test of time much better than Datasets.
     

  14. Do your Windows Forms have a StatusBar that shows the time to load?

    Every form should have a StatusBar that shows the time taken to load the form.
    Developers cant catch and reproduce every performance issue in the testing environment, but when users complain about performance they can send a screenshot (which would including the time to load). Users themselves also would want to monitor the performance of the application. This is one of Microsoft Internet Explorer's most appalling missing feature, the status bar only says 'Done.' when the page is loaded - 'Done: Load Time 14 seconds'. More suggestions for Internet Explorer can be found on the page SSW Microsoft Internet Explorer Suggestions.

    In the figure below, the time taken to load the form over a dialup connection is 61.1 seconds, this proves to the developer that the form is not useable over a dialup connection. In this particular case, the developer has called a 'select * from Employees' where it was not needed, only the name, password and ID is needed for this form.

    Note: Once the form is loaded and load time is shown, the status bar can be used to show anything useful as the form is being used.


    Figure: Another form with the StatusBar that shows the time to load - very slow on dialup. (good)

    To have a form with StatusBar looks like this:
    Add a StatusBar to the form, and add a StatusBarPanel to the StatusBar, then set the properties like below.


    Status bar panel
    Figure: Add StatusBarPanel to StatusBar

    private DateTime StartLoadTime = System.DateTime.Now;
    
    private void Form1_Load(object sender, System.EventArgs e)
    {
    	TimeSpan elapsedLoadTime = DateTime.Now.Subtract(StartLoadTime);
    	this.statusBarPanel1.Text = string.Format(
    	"Load time: {0} seconds",
    	Math.Round(elapsedLoadTime.TotalSeconds, 1));
    }

  15. Do you not cache lookup data in your Windows Forms application?

    To avoid unnecessary database look-ups, many developers cache lookup tables when creating a windows application. There are issue  that can arise as a result, mainly to do with the synching of the lookup data. If the database administrator decides to change the lookup tables, there is bound to be a user online using a static old version of the lookup data. This may result in sql exception, and data corruption. 

    Exception #1: If the application can be taken offline where the users will not access the database for a finite time, then it is recommended that you cache lookup data. However, we do not recommend caching of non-lookup data, i.e. products, clients or invoices.
    Note: This is a different scenario to complete offline caching; offline caching is recommended and should be implemented (e.g outlook & IE - [Work Offline].
    However, this rule is about combo boxes and list views which contain less than 100 records. There is not much benefit to caching lookup data as there is much more coding involved.

    Exception #2: If the application contains minor non-critical data. (eg. If you allow the user to customize the text displayed on forms (some people prefer 'Customer' while some prefer 'Client') and this is stored in a database)
    Depending on the frequency of this data being changed (and if the change is user dependant), you may want to:
    • Low frequency: Place an option to change this data in the application's installation process

    • High frequency: Cache the data and provide an option to refresh all cached data or disable caching all together. (e.g menu items View->'Refresh All' and Tools->'Options'->'Disable Caching').


    We would love to be proved wrong on this rule. We have 1000s of users on some of our applications, we have tried caching lookup data and we ended up with a lot more code containing exception handling and table refreshing than its benefit.
     
  16. Do you use the designer for all visual elements?

    The designer should be used for all GUI design. Controls should be dragged and dropped onto the form and all properties should be set in the designer.

    Read more about it in Rules to Better .NET Projects

  17. Do you always use the Visual Studio designer for data binding where possible?

    Basic data binding should always be done in the designer because the syntax for data binding is complex, and confusing for other developers reading the code.

    Figure: Simple data binding (binding to a single property) in the designer
     
    Figure: Complex data binding (binding to a list) in the designer

    When you need to handle the Format or binding events, you can still use designer data binding, as long as you hook in your events prior to filling data.

    private void Form1_Load(object sender, System.EventArgs e)
    {
       Binding currencyBinding = this.textBox1.DataBindings("Text");
       currencyBinding.Format += new 
          ConvertEventHandler(currencyBinding_Format);
       currencyBinding.Parse +=
          new ConvertEventHandler(currencyBinding_Parse);
    
       OrderDetailsService.Instance.GetAll(Me.OrderDetailsDataSet1);	
    }
    
    private void currencyBinding_Format(object sender, ConvertEventArgs e)
    {
       if(e.DesiredType == typeof(string))
       {
          e.Value = ((decimal)e.Value).ToString("c");
       }
    }
    
    private void currencyBinding_Parse(object sender, ConvertEventArgs e)
    {
       if(e.DesiredType == typeof(decimal))
       {
          e.Value = Decimal.Parse(e.Value.ToString(),
             System.Globalization.NumberStyles.Currency);
       }
    }

    //
    // Designer auto generated code.
    //
    private void InitializeComponent()
    {
    	this.cmbTumorQuad = new System.Windows.Forms.ComboBox();
    		
    	//
    	// cmbTumorQuad
    	//
    	this.requiredValidator1.SetCustomValidationEnabled(this.cmbTumorQuad, true);
    	this.cmbTumorQuad.DataBindings.Add(new System.Windows.Forms.Binding("SelectedValue",
    this.dvOccMain, "TumorQuadrant")); this.cmbTumorQuad.DataSource = this.dvTumorQuad; this.cmbTumorQuad.DisplayMember = "Description"; this.requiredValidator1.SetDisplayName(this.cmbTumorQuad, ""); }

    Figure: DataBinding in Designer (good)

     

    private void DataBind()
    {
    	ChangeBinding(txtRuleName.DataBindings,	"Text", jobRules, "RuleData.RuleName");
    	ChangeBinding(cmbFileFilter.DataBindings, "Text", jobRules, "RuleData.FileFilter");
    	ChangeBinding(txtSearchString.DataBindings, "Text", jobRules, "RuleData.SearchString");
    	ChangeBinding(txtCreatedBy.DataBindings, "Text" , jobRules, "RuleData.EmpCreated");
    }
    	
    protected Binding ChangeBinding(ControlBindingsCollection bindings, string propertyName, 
    object dataSource, string dataMember, ConvertEventHandler eFormat, ConvertEventHandler eParse) { Binding b = bindings[propertyName]; if ( b != null ) bindings.Remove(b); b = new Binding(propertyName, dataSource, dataMember); bindings.Add(b); return b; }

    Figure: DataBinding in Code (bad)

     

    private void DataBind()
    {
        //Header
        picRuleType.Image = Core.GetRuleTypeImage((RuleType)rule.RuleType, 48);
        ruleNameTextBox.Text = rule.RuleName;
    
        //General Tab
        notesTextBox.Text = rule.RuleDescription;
        ruleUrlTextBox.Text = rule.RuleURL;
    
        //Search Tab
        cboRuleType.SelectedValue = (RuleType)rule.RuleType;
        searchForTextBox.Text = rule.SearchString;
        shouldExistComboBox.SelectedIndex = (rule.ShouldExist == true ? 0 : 1);
    
        //Change History Tab
        createdByTextBox.Text = rule.EmpCreated;
        dateCreatedTextBox.Text = rule.DateCreated.ToString();
        lastUpdatedByTextBox.Text = rule.EmpUpdated;
        dateLastUpdatedTextBox.Text = rule.DateUpdated.ToString();
    }
    

    Figure: Set controls' values in Code (bad)

    See our SSW Rules to Better Websites - Development - Do you always use the Visual Studio designer for data binding where possible?
  18. Do you avoid using MDI forms?

    MDI (Multiple Document Interface) forms should be avoided in most modern data-centric applications because they:

    • Are a hangover from the days of Windows 3.1 and Access 2.0
    • Constrained within a smaller window
    • Only show as one window on the taskbar
    • Have no multiple monitor support (the killer reason)


    Figure: VS.NET with tabs is cool for developers, but not for the average knowledge worker (bad)


    Figure: Word 2003 in MDI mode (bad)


    Figure: Word 2003 with Default Settings (good)

    Me.IsMdiContainer = true;
    
    ClientForm frm = new ClientForm();
    frm.MdiParent = this;
    frm.Show();
    Figure: Bad code example - using MDI forms

    ClientForm frm = new ClientForm();
    frm.Show();
    Figure: Good code example - not using MDI

    MDI forms have the advantage that the MDI parent form will have a collection MdiChildren which contains all of its child forms. This makes it very easy to find out which forms are already open, and to give these forms focus. Accomplishing this with an SDI application requires you to:

    • A global collection of forms
    • A line of code on the load and closed events of each form which adds / removes the form from the global collection

    But what about tabs?
    As developers, we love to use tabs similar Visual Studio.NET (figure below) and  browsers such as Mozilla and CrazyBrowser. Tabs are great for developers, but standard business applications (e.g Sales Order System) should be developed as SDI (Single Document Interface). This is because users are used to Outlook and other office applications, which don't use MDIs at all. If the users want to group windows, Windows XP lets you "Group Similar Taskbar Icons".
  19. Do you have a correctly structured common code assembly?

    Your common code assembly should be divided into the following sections:

    • Common (e.g. SSW.Framework.Common)
      • Code which is not UI specific
      • Example: Code to convert a date into different formats
    • CommonWindows (e.g. SSW.Framework.WindowsUI)
      • Example: Base forms which are the same for all products, wizard frameworks
    • CommonWeb (e.g. SSW.Framework.WebUI)
      • Example: Generic XML-based navigation components

    For more information see Do you have a consistent .NET Solution Structure?.

  20. Are your Data Access Layers compatible with Web Services?

    Data Access Layers should support not only direct connections to SQL Server but also connections through web services.
    Many applications are designed for use with a database connection only. As users decide to take the application some where else away from the database, the need for web services arises.


    Figure: Options form showing choice of connection (good)

    There are three ways to implement this:

    1. Lots of if statements (really messy - most people try this first)
    2. Interfaces (Implements statement in VB)
    3. Factory pattern (best - most flexible and extensible approach)

    All database applications should be web services ready as the future direction is to use web services only, because even locally a web service connection is not much slower than direct connection.  The performance difference shouldnt be substantial enough to require a double code base.

  21. Do you log all errors (with SSW Exception Manager)?

    All unhandled exceptions should be logged to provide developers with sufficient information to fix bugs when they occur. There are two options we for logging exceptions:

    The Microsoft Exception Management Application Block
    Microsoft provides full source code for the EMAB, which is fully extensible with custom logging target extensions. We decided to customize the EMAB to produce the SSW Exception Management Block, which logs exceptions to a database using a web service, allowing us to keep a history of all exceptions.


    Figure: Exception Reporting Web Service
     

    Your code should not contain any empty catch blocks as this can hamper exception handling and debugging.

    We have a program called SSW Code Auditor to check for this rule.
    We have a program called SSW .NET Toolkit that implements Exception Logging and Handling
  22. Do you implement trace logging (with Log4Net)

    By using logging, the developer has access to more information when a particular error occurs like which functions were called, what state is the application currently in and what certain variables are. This is important as a simple stack trace will only tell you where the error occurred but not how it occurred.

    Log4Net is an open-source logging library for .NET based on the Log4J library. It provides a simple to use library to enable logging in your application. It provides several logging options such as:

    • XML File (Recommended)
    • Text File
    • Database
    • Rolling log file
    • Console

    Log4Net also provides different levels of tracing - from INFO to DEBUG to ERROR - and allows you to easily change the logging level (through the config file)


    We have a program called SSW Code Auditor to check for this rule.

  23. Do you make a strongly-typed wrapper for app.config?

    If your application accesses properties in app.config, you should provide a strongly typed wrapper for the app.config file. The following code shows you how to build a simple wrapper for app.config in an AssemblyConfiguration class:

    using System;
    using System.Configuration;
    
    namespace SSW.Northwind.WindowsUI
    {
       public sealed class AssemblyConfiguration
       {
          // Prevent the class from being constructed
          private AssemblyConfiguration() { }
    
          public static string ConnectionString
          {
             get
             {
                return
                   ConfigurationSettings.AppSettings["ConnectionString"].
                   ToString();
             }
          }
       }
    }

    Unfortunately, the Configuration Block does not automatically provide this wrapper.

  24. Do you keep the standard .NET DataGrid?

    Note: In Visual Studio 2003 the standard DataGrid has some limitations. It was ugly compared to a ListView and did not support combo box or button columns, making it useless for many applications.
    Now in Visual Studio 2005 we have this great new DataGridView control which solves these problems.
    If you still want more then you need a 3rd party control. We recommend these (in this order):

    1. Janus GridEx
    2. Developer Express XtraGrid
    3. Infragistics Wingrid
    4. ComponentOne TrueDBGrid

    For more Details have a look at our Best 3rd Party Controls for Windows Forms


    Figure: The standard .NET DataGrid in 2003 was ugly and missing combos (bad)


    Figure: Infragistics UltraGrid is better as you get combos (better)


    Figure: Janus Grid is even better. A great datagrid has easy grouping, just like Outlook (good)


    Figure: The great new Visual Studio 2005 much improved DataGridView

  25. Do you replace the standard .NET Date Time Picker?

    A good replacement for the standard Date Time picker is the UltraDatePicker by Infragistics.

    The main reason for the use of the UltraDatePicker over the standard .NET one is because the .NET one does not take null for a date value.  

    This is a lot of hassle for DataBinding.  The windows form DataBinding will try to put null into the bound field, when:

    1. The bound data is DBNull

    2. The current row is removed (i.e., there is no more data in the DataTable)

    If you set the property "Nullable" to false in UltraDatePicker, the same issues appears again.

    Date/Time picker property
    Figure: Set "Nullable" to true to allow DBNull values from bound DataRows

    So the solution is to allow null, but where the field is required, make sure the validation picks it up and asks the user to enter a value when saving the form.

     
  26. Do you avoid 3rd party menus & toolbars?

  27. The menu & toolbar controls in Visual Studio .NET 2003 do not allow you to have icons in your menus or have alpha-blended toolbar icons. They also do not provide an Office-2003 like look. However, we have tried several third party menu and toolbar controls and all of them had serious bugs, e.g.

    We love 3rd party controls, a lot of developers spend a lot of time implementing these tools to make their applications sweeter, but we found that there is not enough benefit in implementing these controls.

    I am very keen on 3rd party controls, but only where they add real value. Knowing about Visual Studio 2005 which provides Office 2003 style menus and toolbars with the new ToolStrip control mean I will wait in this case....Another worry is upgrading from these 3rd party controls will be difficult)

    Whidbey tool strip designer
    Figure: Visual Studio 2005's new controls

    However, it would be better if VS 2005 stored the details of menus and toolbars in an XML file.

  28. Do your List Views support multiple selection and copying?

    List Views such as in SSW Diagnostics can present a wealth of information to the user. But too often, users are unable to copy this information to paste into a support email because the list view doesn't support copying. Instead, the user has to frustratingly retype the information with the risk of introducing errors.

    Bad example
    Figure: List view with only single selection and no copying (Bad)

    Good example
    Figure: list view with multiple selection and copying (Good)

    Make it easier for the user by enabling the "MultiSelection" property of a ListView and providing a right click menu with a "Copy" item that copies to the clipboard.

  29. Do you use an image button for opening a web page taking action?

    Opening a specific web page (that the user is aware of) from a windows application should always be in the form of a hyperlink. Below is a simple example of a hyperlink simply opening a web page containing just more information or help.

    Opening Web Options
    Figure: Simple hyperlink not taking action

    However if you are taking action then opening the page (e.g concatenating the URL, etc) then you must have an image button to illustrate the action which will be taken.

    Here is a compilation of a few bad examples for this:


    Figure: Hyperlink (bad)
    Figure: Hyperlink on a button (Bad)
    Figure: Normal button (bad)

    But when it requires some form of action (e.g. generating reports, passing and processing values), use a button with an image.


    Figure: XP button with image (good)

    Note: Screenshot contains XP button because the .Net 1.1 button does not support images, however the default button in .NET 2.0 supports images.
    e.g. EdwardForgacs.Components.WindowsUI.dll

  30. Do you use "Ok" instead of "OK"?



    Figure: Good example with button text "OK"


    Figure: Bad example with button text "Ok"
     
    We have a program called SSW Code Auditor to check for this rule.
  31. Do your forms have Accept and Cancel buttons?

    If you have a button in a form you must have an accept or a cancel button. As a result user can use "Enter" and "Esc" to control the form.


    Figure: Next button is set as the accept button (good)
     
    We have a program called SSW Code Auditor to check for this rule.

    Note: Our Rule will just test the buttons on the Base form and ignore all the inherit forms, because for more

    reusable code, the Accept and Cancel buttons should be in the base form.


  32. Do you name all your "OK" buttons to be an Action eg. Save, Open etc?

    If your form has an "Ok" button it should be renamed to be an Action. For Example: Save, Open etc.

    SkypeToolsOptions
    Figure: Good Example with "Save" button which is an action

    VSToolsOptions.png
    Figure: Bad Example with "OK" button which is not an action

    We have a program called SSW Code Auditor to check for this rule.

  33. Do you make "Enter" go to the next line when you have a multi-line textbox rather than hit the OK button?

    If you have a multi-line textbox in a form, you should make the "Enter" key go to the next line in the text box, rather than cause it to hit the OK button.

    Developer Notes Screen
    Figure: "Enter" button causes OK button to be pressed instead of going to next line in the multi-line text box. (bad)

    Developer Notes Screen
    Figure: "Enter" button goes to the next line in the text box (good)


    It can be done by assigning "True" value to AcceptsReturn and Multiline options in properties bar.
    Developer Notes Screen
    Figure: Developer Notes properties details.

  34. Do you make common controls with consistent widths?

    There are a few common controls we always use in our products. For example, DateTime and Ellipsis Button. We need a standard for the width so the controls should be more consistent.

    Note: Controls on base forms will be made to be 'protected' rather than 'private', especially so that inherited forms of different sizes don't mess up.

     Bad control

    Figure: Control sizes are not consistent. (bad)

      Good control
    Figure: Control sizes are all standard and consistent. (good)

     Bad control

    Figure: Non-standard size for Add & Delete buttons. (bad)

      Good control

    Figure: Standard size (75 X 23 pixels) for Add & Delete buttons. (good)

    We have a program called SSW Code Auditor to check for the following two rules:

    • Rule - C#/VB.NET UI- Button Height and Width - for Standard Button (75 x 23 pixels) – Level 2: all buttons < 6 characters: Check the standard size (75 X 23 pixels) for buttons with the word length less than or equal to six characters, except the following buttons.

    • Rule - C#/VB.NET UI- Button Height and Width - for Standard Button (75 x 23 pixels) – Level 1: the action buttons: Check the standard size (75 X 23 pixels) for the following action buttons.


      • Add
      • Delete
      • Edit
      • OK
      • Close
      • Cancel
      • Save
      • Browse
      • Select
      • Test
      • Next
      • Back
      • Remove
      • Refresh (Exception to the rule as it has 7 letters)

  35. Do your Windows Forms applications support URLs?

    Aside from ease of installation, what is the one thing a web browsers has over a Windows Forms application? - a URL!
    With a Windows Forms application, you typically have to wade through layers of menus and options to find a particular record or "page". However, Outlook has a unique feature which allows you to jump to a folder or item directly from the command line.

    Run Shortcut
    Figure: Outlook can automatically jump to a specified folder or item from a command line

    Outlook Address bar
    Figure: Outlook address bar (Web toolbar) shows you the URL for every folder

    We believe that all applications should have this capability. You can add it to a Windows Application using the following procedure:

    1. Add the necessary registry keys for the application
      • HKEY_CLASSES_ROOT\AppName\URL Protocol = ""
      • HKEY_CLASSES_ROOT\AppName\Default Value = "URL:Outlook Folders"
      • HKEY_CLASSES_ROOT\AppName\shell\Default Value = "open"
      • HKEY_CLASSES_ROOT\AppName\shell\open\command\Default Value = "Path\AssemblyName.exe /select %1"
       
    2. Add code into your main method to handle the extra parameters.

      C#:
       
      public static void Main(string[] args)
      {
         ...
      
         if(args.Length > 0)
         {
            string commandData = args[1].Substring(args[1].IndexOf(":") +
              1).Replace("\"", String.Empty);
      
            Form requestedForm = null;
      
            switch(commandData)
            {
               case "Client":
               {
                  requestedForm = new ClientForm();
                  break;
               }
               // Handle other values
               default: // Command line parameter is invalid
               {
                  MessageBox.Show("The command line parameter specified" +
                     " was invalid.", "SSW Demo App",
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
      
                  // Exit the application
                  return;
               }
            }
      
            requestedForm.Show();
      
            // Show the main form as well
            MainForm mainForm = new MainForm();
            mainForm.Show();
      
            // Give the requested form focus
            requestedForm.Focus();
      
            Application.Run(mainForm);
         }
         else // No command line parameters
         {
            // Just show the main form
            Application.Run(new MainForm());
         }
      }

      VB .NET:

      Public Shared Sub Main()
         ...
      Dim args As String = Microsoft.VisualBasic.Command()
      
         If args.Length > 0
            Dim commandData As String = _
               args.Substring(args.IndexOf(":") + 1).Replace("""", "")
            Dim requestedForm As Form = Nothing
      
            Select Case commandData
               Case "Client"
                  requestedForm = New ClientForm()
      
               ' Handle other values
      
               Case Else ' Command line parameter is invalid
      	 MessageBox.Show("The command line parameter specified " &_
                  "was invalid.", "SSW Demo App", MessageBoxButtons.OK, &_
                  MessageBoxIcon.Error);
      
               ' Exit the application
               Exit Sub
            End Select
      
            requestedForm.Show()
      
            ' Show the main form as well
            Dim mainForm As MainForm = New MainForm()
            mainForm.Show()
      
            ' Give the requested form focus
            requestedForm.Focus()
      
            Application.Run(mainForm);
      
            Else ' No command line parameters, just show the main form
            Application.Run(new MainForm())
         End If
      End Sub

      Sample code implementation in the SSW .NET Toolkit.


  36. Do you include back & undo buttons on every form?

    Following on from including a URL, almost every form should have a Back and an Undo button which takes you back to the previous screen, or reverses the last action. This is just like Outlook (see figure below), it has a Back button to take you to the previous folder and an Undo button.




    Figure: Back & Undo buttons in Outlook Advanced toolbar (good)

    Note: Back button should only be implemented if different views can be shown in the same window.
    Note #2: You dont put Undo buttons on non data entry forms such as a Print Preview form.

    The list of forms/URLs and the order in which they have been accessed should be stored in a DataSet held in memory (like IE) - not saved to disk.
    For example:

    Menu Action Undo Back
    Cut Remember: Remember Text and Cursor Position
    Cut To Clipboard
    Return to Remember n/a
    Save Record Remember old values
    Execute procCustomerSave
    Close Form
    Return to Old values Reopen form

    Sample code implementation in the SSW .NET Toolkit.
     

  37. Do you use NUnit to write Unit Tests?

    When anyone sends you a bug that happen in their environment do a unit test. Just because the code runs on your machine it doesnt mean it will work on the users machine. E.g permissions issues - you are an admin while the user is only a simple user, registry & io reads might fail, NUnit will test for this and let you know.

    Unit testing is a valuable tool when maintaining code, particularly in a team environment where you may have to fix a bug in someone else's code. Unit Tests ensure that you do not introduce new bugs in someone else's code, or code that you have not looked at for a while. We like NUnit because it is free, we have found that it is easy for developers to learn and it integrates well with Visual Studio. Visual Studio .NET 2005 integrates Unit Testing with Visual Studio Team System. We will use this when Visual Studio 2005 is released.

    You should always try to write unit tests for:

    1. Code that is a core component to your application
    2. Any regular expressions (as these change it is easy to change functionality and cause errors)
    3. Any external factors that your program relies on like hyperlinks to websites

    One important test that should be implemented (if your setup package or build script doesn't pick it up) is to validate that your application installs all required DLLs. .NET loads DLLs just in time (JIT) - which means that a missing DLL is will not generate an error unless it is required.

    Example: You may have a shared project that your application uses. Another developer adds a reference to that project - unbeknownst to you. You build the application with no errors, and the application passes basic user testing. Problem is that the user did not run the tutorial component - which is missing from the setup package. Users who run the tutorial report runtime errors. You can resolve this issue by creating a unit test to check that all DLLs are included in the setup as per the sample code implementation in our KB article Q1852329.


    Figure: Unit Tests accessible from the help menu

    Unit tests should also be accessible from the Help menu to assist in troubleshooting when your users call up tech support. For more information see Rules to Better Interfaces.

    Note: Unit testing also works with Web projects.

    For more information on unit testing see Rules to Better Unit Tests

    Also see Suggestions to TFS

    We have a program called SSW .NET Toolkit that implements these Unit Tests
  38. Do you save user settings and reuse them by default?

    There should always be default values in your application if you allow users to change the settings. This will help your users to have a better first time experience and insure the application work as expected.

    However when the users change settings for their own preference, it is better to save these settings and give user has a better return experience, your application looks smarter in this way.

    ReuseUserSettings
    Figure: Save user setting

  39. Do you have a ResetDefault() function to handle messed up user settings?

    In our development life cycle, developers always have different Settings to the user's settings. Because of this, debug settings won't always work on the remote machine.

    In order to have settings.config, we also have a defaults.config. This is good because this gives a chance for the user to roll back bad settings without reinstalling the application. The application can also roll back the settings it automatically. Below is the code that what we do.

    VB.NET

      Public Sub RuneXtremeEmail(ByVal state As Object)
    
            If Environment.MachineName <> Configuration.MachineName Then
    
                resetSettings()
    
            Else 
    
    We have a program called SSW Code Auditor to check for this rule.
    We have a program called SSW .NET Toolkit that implements this rule.

    Note: in Access we do like this

      Private Sub Form_Load()
      If Nz(DLookup("CurrentComputerName", "ControlLocal", "ID=1"), "") <> CurrentComputerName Then
        	Me.ctlCurrentComputerName.Value = CurrentComputerName
        
      Else ...
        

  40. Do you use Threading to make your user interfaces more responsive?

    Threading is not only used to allow a server to process multiple client requests - it could make your user interfaces responsive when your application is performing a long-running process, allowing the user to keep interactive.


    Figure: Unresponsive UI because no threading code(bad)

    		private void Form1_Load(object sender, EventArgs e)
    {
    
                this.ValidateSQLAndCheckVersion();// a long task
    }
    		
    	
    Code: No threading code for long task


    Figure: Responsive UI in progress (good)


    Figure: Responsive UI completed (good)

    	private void Page05StorageMechanism_Load(object sender, EventArgs e)
    	{
    
                this.rsSetupOk = false;
    
                this.databaseSetupOk = false;
    
                this.NextButtonState.Enabled = false;
    
                CheckDatabase();
    
                CheckReports();
    
    	}
    
    	public void CheckDatabase()
            {
                         if(sqlConnectionString == null)
                         {
    
                               OnValidationFinished(false,false);
    
                         }
    
                         if(upgradeScriptPath ==null && createScriptPath == null)
    
                         {
    
                               OnValidationFinished(false,false);
    
                         }
                         Thread thread = new Thread(new ThreadStart(this.ValidateSQLAndCheckVersion) ) ;
    
                         thread.Name = "DBCheckingThread";
    
                         thread.Start();
    
             }
    Code: Threading code for long task


  41. Do you use two lines height to display file name in the text box?

    The height of a text box should be twice of the font height to display file name in full.


    Figure: Text box displaying full file name
     
  42. Do you use Status Control?

    SSW Status Control provides graphical UI for status checking result.


    Figure: Warning message in status control
     

    Figure: Successful message in status control


    Figure: Failed message in status control

    We have a program called SSW .NET Toolkit that uses status control.
  43. Do you prevent users from running two instances of your application?

    In some cases, running two instances of an application at the same time may cause unexpected result. See below code in one of our products - SSW Exchange Reporter to solve this issue:

    try
    {
    	Process current = Process.GetCurrentProcess();
    	Process[] processes = Process.GetProcessesByName( current.ProcessName);
    			
    	if ( processes.Length>1 )
    	{
    		DialogResult userOption = MessageBox.Show(  
    		Application.ProductName+" is already running on this machine. "+Environment.NewLine+Environment.NewLine+
    		"Please click: "+Environment.NewLine+
    		  
    		" - 'Retry' to exit the other instance and try again, or "+Environment.NewLine+
    		" - 'Cancel' to exit now."+Environment.NewLine,
    		Application.ProductName+" "+(new Version(Application.ProductVersion)).ToString(2),
    		  
    		MessageBoxButtons.RetryCancel, MessageBoxIcon.Warning);
    		switch(userOption)
    		{
    			case DialogResult.Cancel: return;
    			  
    			case DialogResult.Retry:
    			foreach(Process currProcess in processes)
    			{
    				if ( currProcess.Id != current.Id)
    				{
    					currProcess.Kill();
    				}
    			}
    			break;						
    		}
    	}	
    }
    catch(Exception ex)
    {
    	TracingHelper.Trace(null, Loggers.WindowsUILogger, TracingLevels.DEBUG, "Cannot get process information, Excpetion occured.", ex) ;
    
    	DialogResult result = MessageBox.Show(
    	"Exchange Reporter cannot detect process information. This may be caused by disabled 'Performance Counter' on your machine. "+Environment.NewLine+
    	"In such case, Exchange Reporter cannot ensure there is only one instance running. "+
    	Environment.NewLine+
    	"You may continue to run Exchange Reporter, however, please make sure you have only one instance of Exchange Reporter running. "+
    	Environment.NewLine+
    	"Multiple instances will cause unexpected behaviour. "+
    	Environment.NewLine+Environment.NewLine+
    	"Please click 'OK' to continue, or click 'Cancel' to quit."
    	, Application.ProductName+" "+(new Version(Application.ProductVersion)).ToString(2),
    	MessageBoxButtons.OKCancel,
    	MessageBoxIcon.Warning);
    
    	if ( result == DialogResult.Cancel)
    	{					
    		return;
    	}
    }
    
    

    Code: avoid running two instances

  44. Do you add a "(customized)" column in grid if there are default values?

    Add a column and show "(customized)" in grid - that is an easier way to know if you have changed from the defaults

    Figure: you need to compare with the default values to know whether it is modified;
    Figure: a "(customized)" column is an easier way to know if you have changed from the defaults;
  45. Do you know there should be a standard menu item "Check for Updates"?

    A standard menu item "Check for Updates" should be available in the Help menu. Its function is running SSW Diagnostics to check updates and keep the system up to date easily. Do you allow users to check for a new version easily?

    Figure: "Check for Updates" menu item

    Here's the code to run Diagnostics.
    System.Diagnostics.Process.Start("http://us.ssw.com.au/ssw/diagnostics/download/SSWDiagnostics.application#SSWDiagnostics.application");
    
  46. Do you use Web Service to send emails?

    In a Windows application, if you need to send mail, please use a WebService to do this, because using WebService to send emails is safer.You don't need to store the email server configuration in your application.config file, which can be installed on the client and be exposed to someone who could take advantage of it.

    SmtpClient client = new SmtpClient();
    try
    {
        client.Host = "****.***.com.au";
        client.Port = 25;
        client.UseDefaultCredentials = true;
        
        MailAddress from = new MailAddress("test@ssw.com.au");
         
        string unrecAddy = "test@ssw.com.au";
        MailAddress to = new MailAddress(unrecAddy);
    
        MailMessage mailMessage = new MailMessage(from, to);
    
        mailMessage.Subject = "aaa";
        string strVer = "aaa";
        mailMessage.Body = "aaa";
    
        client.Send(mailMessage);
        
    }
    catch(Exception ex)
    {
        client.SendAsyncCancel();
        MessageBox.Show(ex.ToString());
    }
                            
    Bad Sample: Send mail without webserice
    Emailer.PubicFunction pf = new EmailWebServices.Emailer.Publiction();
    pf.SendMail("Test", textBox3.Text, textBox1.Text, textBox2.Text, false, "", null);
                            
    Good Sample: Send mail use webservice
  47. Do your label beside input control(Textbox, Combobox, Datetime, etc...) have colon?

    Figure: Label with colon
  48. Do Do you always use GridView instead of ListBox?

    Always choose a GridView (over a ListBox) because it can have:
    1. Multiple columns.
    2. Checkboxes in the header of the control, which enables users to easily check or uncheck all items.
    3. Add sub-controls added such as buttons, links, charts, and even customized controls to the Gridview. This means you get unlimited flexibility with the GridView.

    Bad Example: No header rows and no checkbox to check or uncheck all items. None of this can be done with the ListView.
    Good Example: A header row and a checkbox to control all items, and multiple columns give users a richer experience. This can all be done using a GridView.
  49. Do you know how to make .Net wrapper work on both x64 and x86 platforms?

    Sometimes, we need to use .Net wrapper to call Windows built-in forms for implementing special functionalities, for example, calling the Directory Object Picker dialog enables a user to select objects from the Active Directory. MSDN provides an article and an example(C++) on how to calling the Directory Object Picker dialog , and the CodePlex website gives a .Net version of implementation(C#). However, all of this implementations only work on x86 platform, and will crash on x64 platform, regarding to this problem, the keynote is to understand the difference of IntPtr in between x64 and x86 platforms.

        In x86 platform, IntPtr = Int32
        In x64 platform, IntPtr = Int64

    So, To fix the crash, we should re-write the code below:

    DSOP_SCOPE_INIT_INFO[] scopeInitInfo = new DSOP_SCOPE_INIT_INFO[2]; 
    IntPtr refScopeInitInfo = Marshal.AllocHGlobal(Marshal.SizeOf (typeof (DSOP_SCOPE_INIT_INFO)) * 2);
    Marshal.StructureToPtr (scopeInitInfo[0], refScopeInitInfo,true);
    Marshal.StructureToPtr(scopeInitInfo[1], (IntPtr)((int)refScopeInitInfo + Marshal.SizeOf(typeof(DSOP_SCOPE_INIT_INFO))), true);
    The code above always gets crash in x64 platform, because of an integer overflow and result in a segmentation fault in 64 bits.
    IntPtr refScopeInitInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DSOP_SCOPE_INIT_INFO)) * 2);
    int scopeInitInfoSize = Marshal.SizeOf (typeof (DSOP_SCOPE_INIT_INFO));
    int offset = scopeInitInfoSize;
    IntPtr scopeInitInfo = (IntPtr)(refScopeInitInfo.ToInt64() + offset);
    Convert all IntPtr to Int64 before the add operator (note: this will work for 32 bits as well, just less efficient).
    Directory Object Picker dialog crashs in x64 platform
    Figure: Calling the Directory Object Picker dialog causes crash on x64 platform when using the bad code above.
    Directory Object Picker dialog works x64 platform
    Figure: The Directory Object Picker dialog works on both x64 and x86 platforms well when using the good code above.
  50. Do you set the ScrollBars property if the TextBox is Multiline?

    If a TextBox has Multiline set to true, then the ScrollBars property should be set to "Both" or at least "Vertical".

    Bad Example: Multiline TextBox without "Vertical" scroll bar.
    Good Example: Multiline TextBox with "Vertical" scroll bar.
    Good Example: Set the ScrollBars property to "Vertical" if the TextBox is Multiline.
    We have a program called SSW Code Auditor to check for this rule.
  51. Do you know how to run write application to run with UAC turn on?

    Some applications may need to have administrator right for running the application, e.g. create a file, access system library, etc. It will be an issue for the application to run if UAC is turned on. Below is the step to solve the issue:

    1. Add App.Manifest into WindowsUI project. It should contain the below code:
      <?xml version="1.0" encoding="utf-8"?>
      <asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" 
        xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
        <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
          <security>
            <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
              <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
            </requestedPrivileges>
          </security>
        </trustInfo>
      </asmv1:assembly>
                          
      App.Manifest
    2. Change the project settings for WindowsUI to use the newly created App.Manifest.
      Use the newly created App.Manifest
  52. Do you use Auto wait cursor on your windows application?

    It can be extremely tiresome to have to continually remember to set and unset the wait cursor for an application. If an exception occurs you have to remember to add a try finally block to restore the cursor, or if you popup a message box you must remember to change the cursor first otherwise the user will just sit there thinking the application is busy.

    Bad Example: Cursor set manually.
    Good Example: Implemented Auto wait cursor.
    We use AutoWaitCursor Class that automatically monitors the state of an application and sets and restores the cursor according to whether the application is busy or not. All that抯 required are a few lines of setup code and your done. see this great blog on how to use AutoWaitCursor. If you have a multithreaded application, it won't change the cursor unless the main input thread is blocked. Infact you can remove all of your cursor setting code everywhere!

  53. Do you make your add/delete buttons crystal clear?

    You don't want someone hitting a delete button by accident. You don't want a use clicking delete execting a record to be deleted and 10 are deleted.

    Aim to make your delete button red and add the count into button text, so the user will be empowered before hitting that fateful delete button.

    Bad Example: The user wants to click 'OK' but there is not a 2nd check
    Good Example: Apple got the delete button perfecton the iPhone. It is red + the count of the selected items is clear
    Good Example: When adding multiple records, do a quick count of the selected items in DataGridView
  54. Do you always set FirstDayOfWeek to Monday on a MonthCalendar?

    It is always good idea to set FirstDayOfWeek property to Monday to initialize it instead of leave it with the dafault value

    Bad Example: The user doesn't set the FirstDayOfWeek to Monday
    Good Example: The user set the FirstDayOfWeek to Monday
  55. Do you always set ShowToday or ShowTodayCircle to true on a MonthCalendar?

    It is always good idea to set ShowToday or ShowTodayCircle to true to increase the user experience on MonthCalendar control

    Bad Example: The user doesn't set ShowToday or ShowTodayCircle to true
    Good Example: The user set ShowToday or ShowTodayCircle to true
  56. Do you set PasswordChar to (*) on a TextBox on sensitive data?

    If you want to work with sensitive data on textboxes is always good practice to set PasswordChar to (*)

    Bad Example: The user doesn't set PasswordChar to (*)
    Good Example: The user set PasswordChar to (*)
  57. Anchoring and Docking - Do you use Anchoring and Docking (full) if you have a multiline textboxes?

    If you add a multiline text box in a form, you should add anchoring and/or docking properties to allow it to expand when the form is resized.

    Wrong settings in the designer
    Bad Example: Wrong settings in the designer
    Set Anchor Property
    Good Example: Set Anchor property to Top, Bottom, Left, Right in the designer
    Multiline textbox with the wrong anchoring and docking
    Bad Example: Multiline textbox with the wrong anchoring and/or docking properties
    Multiline textbox with the correct anchoring and docking
    Good Example: Multiline textbox with the correct anchoring and/or docking properties

    We have a program called SSW Code Auditor to check for this rule

  58. Anchoring and Docking - Do you use Anchoring and Docking (horizontal only) with single line textboxes?

    If you add a text box in a form you should add anchoring and/or docking properties to allow it to grow as the form widens, but not as it increases in height.

    Wrong settings in the designer
    Bad Example: Wrong settings in the designer
    Set Anchor Property
    Good Example: Set Anchor property to Top, Bottom, Left, Right in the designer
    Textbox with the wrong anchoring and/or docking
    Bad Example: Textbox with the wrong anchoring and/or docking properties
    Textbox with the correct anchoring and docking
    Good Example: Textbox with the correct anchoring and/or docking properties

    We have a program called SSW Code Auditor to check for this rule

  59. Do you know TextAlign should be TopLeft or MiddleLeft?

    Text Align TopLeft or MiddleLeft values are always a good practice in terms or layout orientation and UI consistency.

    Figure: Bad example - TextAlign is set to BottomRight
    Figure: Bad example - Form with TextAlign is set to BottomRight
    Figure: Good example - TextAlign is set to TopLeft
    Figure: Good example - Form with TextAlign is set to TopLeft

    We have a program called SSW Code Auditor to check for this rule.

  60. Do you know other UI rules

I'm sold - What more can you do? you do?

SSW Software Auditing Services have helped to improve lots of software ranging from developer utilities to large enterprise systems. Contact us and let our experts help you to audit your software.

Acknowledgments

Adam Cogan


Benefit from our knowledge and experience!

SSW is the industry leader in Custom Software Development, Software Auditing & Developer Training.

Call us on +61 2 9953 3000 or email us for a free consultation

What does it cost? I’m not in Australia. Can you still help?