Rules to Better Windows Forms Applications

Do you agree with them all? Are we missing some? Email me your tips, thoughts or arguments. Let me know what you think.

1. Do you design a mockup UI first?
2. Do you use code generators?
3. Do you use red and yellow colors to distinguish elements in the designer?
4. Do your applications support XP themes?
5. Do you use inherited forms for consistent behaviour?
6. Do you encapsulate (aka lock) values of forms?
7. Do you know when to use User Controls?
8. Do you know how to design a user friendly search system?
9. Do you use Validator controls?
10. Do you use DataSets or create your own business objects?
11. Do you use the designer for all visual elements?
12. Do you always use the Visual Studio designer for data binding where possible?
13. Do you avoid using MDI forms?
14. Do you have a correctly structured common code assembly?
15. Do you include Exception Logging and Handling?
16. Do you make a strongly-typed wrapper for App.config?
17. Do you replace the standard .NET DataGrid?
18. Do you avoid 3rd party menus & toolbars?
19. Do your Windows Forms applications support URLs?
20. Do you include back & undo buttons on every form?
21. Do you use NUnit to write Unit Tests?
22. Are you Data Access Layers compatible with Web Services?
23. Do you use XP button for opening a web page taking action?
  1. 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 mock-ups 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.

  2. 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. At the simplest level, this can mean using the Data Form Wizard in Visual Studio .NET. Other code generators include:

    CodeSmith and
    RAD Software NextGeneration

  3. Do you use red and yellow colors 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

  4. 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. In .NET 1.0, applying themes was difficult because you had to create a manifest file and copy it to the run directory. This is no longer the case.
    There are two steps are all you need to do:

    1. Call Application.EnableVisualStyles() at the top of the Main method of the application.
    2. Set the FlatStyle property of each control to System.


    Figure: Bad - XP themes are not used


    Figure: Good - XP themes are used

  5. 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 eventhe 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

    We have a program called SSW Code Auditor. Rules to come:
    • CentreParent must be used with FixedDialog
    • FixedDialog must be used with CentreParent
    • Only one or two forms with CentreScreen
    • CentreScreen must be used with FixedSingle
    • FixedSingle must be used with CentreScreen
    • Only one Form with FormBorderStyle = None

    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


    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

  6. 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


    Figure: Font Property Visible


    Figure: Font Property Hidden

  7. Do you know when to use User Controls?

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


    Figure: Good - the Address User Control is repeated

    Pros:

    • 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 seperate usercontrol
    • Reduce lines of generated code in the designer by splitting it into multiple files
    • Allow multiple persons to work on different complex tabpages

    Cons:

    • 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.

    However, User Controls should not be used for every form in the application. They should only be used for elements which are shared between several forms, such as search and address controls.


    Figure: Bad use of user controls - all the forms in the application are user controls


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


    Figure: Good - user controls are only used for shared controls

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

    A search system should be separate from the data entry fields (on a different form), to avoid confusion, should have variable criteria, and should have advanced options which allow the user to search any field at all.


    Figure: Bad Search System (Controls are on the same form as the data entry controls)


    Figure: Good Search System  (see the SSW .NET Toolkit)

  9. 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
      Bad, lots of code but no balloon tooltips
       
      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
      Good, lots of code but balloon tooltips are used

      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.


      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).


      Figure: SSW Extended Provider controls and properties on a TextBox
       
  10. 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
      DataSet
      objects automatically 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.  

  11. 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, e.g.

    • Labels, TextBoxes and other visual elements
    • ErrorProviders
    • DataSets (to allow data binding in the designer)

    Things that do not belong in the designer:

    • Connections
    • Commands
    • DataAdapters

    However, and DataAdapter objects should not be dragged onto forms, as they belong in the business tier. Strongly typed DataSet objects should be in the designer as they are simply passed to the business layer. Avoid writing code for properties that can be set in the designer.


    Figure: Bad - Connection and Command objects in the Designer


    Figure: Good - only visual elements in the designer

  12. 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);
       }
    }

  13. Do you avoid using MDI forms?

    MDI 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)

    What about tabs, as used in VS .NET, and browsers such as Mozilla and CrazyBrowser? They are good for developers, but not users. Users are used to Outlook, which does not use MDIs at all. If the users want to group windows, Windows XP lets you "Group Similar Taskbar Icons".


    Figure: Bad Example (Word 2003 in MDI mode)


    Figure: Good Example (Word 2003 with Default Settings)

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

    ClientForm frm = new ClientForm();
    frm.Show();
    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
     
  14. 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?.

  15. Do you include Exception Logging and Handling?

    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:

    1. 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
       
    2. Log4Net is an open-source logging library for .NET base on the Log4J library. It is extremely capable, and will suit the logging needs of my current project, but I can't imagine a case where I wouldn't be able to find a place for this tool in future projects.

      One of the standout attribute of this library is its documentation. Its use of XML code comments, and extensive reference documentation is a credit to the authors, and has set the standard of how we would like our developers to achieve in our code.

      Log4Net allows you to pepper you code with log writing methods of different severities. You can then define in a separate XML .config file how reports on each severity error should be sent (in fact, even inside these severities you can decide how particular entries are reported). In our case "Info" log writes are sent to the Debug and Trace windows, Anything above "Error" is written to a SQL Server database, and "Critical" Errors are also emailed, and written to the Event Log. Because all of these settings are in a .config file, we can change it at runtime whilst the application is running on our production server (it has a file watcher to know when this config file has been edited).

    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. The regular expressions use to check this are:
    • For C#:
      • To find a Main method without EMAB:
        void/s+Main/([^)]*/)/s*{(?!/s*Application/.TreadException/s+/+=/s+new/s+(System/.Threading/.)?ThreadExceptionHandler/((Microsoft/.ApplicationBlocks/.ExceptionManagement/.)?ExceptionManager/.ThreadExceptionHandler/);).*/s+}
         
      • To find empty catch blocks:
        catch/s*(?:/(/s*Exception/s+[A-Za-z][A-Za-z0-9-]*/s*/))?/s*{/s*}
         
    • For VB:
      • To find a main method without EMAB:
        (<Reference/s+(Name/s+=/s+"SSW/.Framework/.ExceptionManagement")/s+(AssemblyName/s+=/s+"SSW/.Framework/.ExceptionManagement")/s+(HintPath/s+=/s+"[^"]*SSW/.Framework/.ExceptionManagement/.dll")/s+/>)
         
      • To find empty catch blocks:
        Catch/s+(?:/w+?/s+?As/s+?[a-z]*Exception)?/s*End Try
  16. 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.

  17. Do you replace the standard .NET DataGrid?

    The standard DataGrid in Visual Studio 2003 has some limitations. It is ugly compared to a ListView and does not support combo box or button columns, making it useless for many applications. (This is going to be fixed in Visual Studio 2005 with the new DataGridView control). However, Infragistics provide an XP-themed DataGrid which provides similar functionality to VS 2005's DataGridView.


    Figure: Infragistics UltraGrid


    Figure: Visual Studio 2005 DataGridView

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

    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.

    Instead of using the 3rd party controls, we are going to use the standard menu and toolbar until Visual Studio 2005 which provides Office 2003 style menus and toolbars with the new ToolStrip control, which it will be easy to upgrade to. Upgrading from these 3rd party controls will be difficult.


    Figure: Visual Studio 2005 Office 2003-like controls (ToolStrip designer)

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

  19. Do your Windows Forms applications support URLs?

    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.


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


    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

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

    Following on from including a URL, every form should have a back and an undo button which takes you back to the previous screen, or reverses you last action respectively. This is just like Outlook has a back button to take you to the previous folder on the Web toolbar (see above).

    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

    MenuActionUndoBack
    CutRemember: Remember Text and Cursor Position
    Cut To Clipboard
    Return to Remembern/a
    Save RecordRemember old values
    Execute procCustomerSave
    Close Form
    Return to Old valuesReopen form

    Sample code to come in the SSW .NET Toolkit.

  21. Do you use NUnit to write Unit Tests?

    Unit tests are 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.

    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.

  22. Are you Data Access Layers compatible with Web Services?

    Data Access Layers should support not only direct connections to SQL Server (best performance when running locally) but also connections through web services (best performance when running remotely). 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)

  23. Do you use XP button for opening a web page taking action?

    If it's simply opening a web page containing just more information or help then use hyperlink.

    Figure: Simple hyperlink

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

    Figure: XP button with image (good)
     
     
    Figure: Hyperlink (bad) Figure: Hyperlink on a button (bad) Figure: Normal button (bad)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值