Winforms – handling variable DPI

There are various recommendations on the web on how to best handle designing a winforms application for variable DPI. This includes:

  • Only using TableLayoutPanels / FlowLayoutPanels (do not set absolute coordinates)
  • Setting custom AutoScaleDimensions values to a magic number
  • Trying different AutoScaleMode values (dpi/font/none)
  • Do nothing.

None of these techniques has worked for every scenario.

Through trial and error, I stumbled across a technique that is simple to implement and doesn’t place constrains on the design layout (can use absolute coordinates). It all comes down to understanding a bug that currently exists in the .Net 4 winforms framework.

If the Form’s ControlBox is set to false in the designer for a fixed single dialogue, it incorrectly lays out the dialogue for a variable DPI scenario. The form’s title bar is not taken into account when determining the size of the form to be shown, so it is drawn slightly too short in height. There is a simple work around to this problem. In the designer, leave the Form’s ControlBox value as true, and in the Form’s Load event, set the ControlBox to false.

/// <summary>
/// Event handler for the form's Load event.
/// </summary>
/// <param name="sender">The sender of the event.</param>
/// <param name="e">Event arguments.</param>
private void Form_Load(object sender, EventArgs e)
{
    // When ControlBox is set to false at design time, in non-
    // standard DPI environments, the form's title bar is not
    // taken into account when determining the size of the form
    // to be shown, so it is drawn slightly too short in height.
    // So, we keep it enabled in the designer, and set it to false
    // here in the Load event.
    this.ControlBox = false;
}

To complete the solution so that winform works for variable DPI, you must also set the AutoScaleMode to either Font or DPI

? ??

Nullable value types are often required when working with event handlers that use the vanilla object class for parameter passing. e.g.

private void EventHandler(object dateTime)
{  
  DateTime? date_time_nullable = dateTime as DateTime?;
  DateTime date_time;
  if (date_time_nullable.HasValue)
  {
    // Use the value.
    date_time = date_time_nullable.Value;
  }
  else
  {
    // Set a sensible default value
    date_time = DateTime.Today;
  }

  // Do something with the date_time
}

This is quite a long winded process for determining the parameter that was passed into the function. There’s got to be a better way… and there is!

With the help of “? ??”, the above code can be written cleanly in one line.

private void EventHandler(object dateTime)
{  
  DateTime date_time = dateTime as DateTime? ?? DateTime.Today;

  // Do something with the date_time
}

Enum Polymorphism

There are times in code when you if available, enum polymorphism would be great. To help explain what I mean by this, consider the following scenerio.

Let’s define a simple library that allows a client to read and write strings to addresses. Some of these addresses are read only, whilst others may be both read and written.

public enum Address : byte
{
    // A read-only serial number (read)
    SerialNumber = 0x01,

    // A user-defined name (read and write).
    Name = 0x02
}

public static string Read(Address address);
public static void Write(Address address, string value);

Then a client can use this library as shown below.

static void Main(string[] args)
{
    // All good, can read from both addresses
    string serial_number = Read(Address.SerialNumber);
    string name = Read(Address.Name);

    // Good - can write to name address
    Write(Address.Name, "john smith");

    // BAD - run-time error!
    Write(Address.SerialNumber, "0123456789");
}

 

Run-Time vs Compile-Time Errors

This program will compile successfully, but will fail at run-time (when we attempt to write to the SerialNumber address). Instead, it would be better if we could force the compiler to help us find these errors.

For this simple scenerio, a simple code review may have discovered the error. But as the code expands, or other clients use the library, the error may not be so easily detected.

The question is, is it possible to design a better library to help the client discover these errors at run-time?

The answer is Yes.

Enum Polymorphism

If enum polymorphism was available, this would be simply solved by:

public enum AddressRead : byte
{
    // A read-only serial number
    SerialNumber = 0x01,
}

public enum AddressWrite : AddressRead
{
    // A user-defined name (read and write).
    Name = 0x02
}

public static string Read(AddressRead address);
public static void Write(AddressWrite address, string value);

This library now ensures that only addresses that are AddressWrite can be written to; and both the AddressRead and AddressWrite can be read.

Currently, the C-Sharp language does not support this concept of enum polymorphism… but let’s not let that stop us.

This code can be easily changed to work within the constraints of the C-Sharp language, and provide the compile-time support we endevour to seek.

Enum Polymorphism – written with classes

Below is the new library written using classes to define the types of available addresses; and a static class to hold all the enumerations.

public static class Address
{
    public static readonly AddressRead SerialNumber = new AddressRead(0x01);
    public static readonly AddressReadAndWrite Name = new AddressReadAndWrite(0x02);
}

public class AddressRead : byte
{
    public AddressRead(byte value)
    {
        this.Value = value;
    }
    public byte Value { get; private set; }
}

public class AddressReadAndWrite : AddressRead
{
    public AddressReadAndWrite(byte value)
        : base(value)
    {
    }
}

public static string Read(AddressRead address);
public static void Write(AddressReadAndWrite address, string value);

Now, for the given client code …

static void Main(string[] args)
{
    // All good, can read from both addresses
    string serial_number = Read(Address.SerialNumber);
    string name = Read(Address.Name);

    // Good - can write to name address
    Write(Address.Name, "john smith");

    // COMPILE-TIME ERROR !
    // Argument 1: cannot convert from 'Program.AddressRead'
    // to 'Program.AddressReadAndWrite'
    Write(Address.SerialNumber, "0123456789");
}

The code will now correctly fail at compile time.

Autoclose Message Box

There are times when you may want to display a MessageBox and after X seconds, if a user has not closed the message box, automatically close it.

Below is a simple example of how this may be achieved by creating a thread that runs an anonymous method. This anonymous method can directly access the outer-scope variables, in particular the EventWaitHandle used for signalling. The end result is a simple static method.

NOTE: There are many examples on the web for how this may be achieved, but nearly all of them require a class to hold state information.

public static MessageBoxAutoClose
{
  /// <summary>
  /// Displays a MessageBox to the user.
  /// </summary>
  /// <param name="title">The title for the MessageBox.</param>
  /// <param name="message">The message for the MessageBox.</param>
  /// <param name="autoCloseTimeSpan">The time to wait prior to auto-closing the message box.</param>
  public static void Show(string title, string message, TimeSpan autoCloseTimeSpan) 
  {
    // Create a thread that will auto-close the MessageBox 
    // after a period of time if a user hasn't clicked it.
    EventWaitHandle wait_handle = new AutoResetEvent(false);
    Thread auto_close_thread = new Thread(delegate()
    {
      try
      {
          // Wait to be signalled that the MessageBox is closed.
          if (!wait_handle.WaitOne(autoCloseTimeSpan))
          {
              // User failed to click okay - attempt to auto-close dialogue.
              IntPtr message_box_handle = Native.FindWindow(null, title);
              if (message_box_handle != IntPtr.Zero)
              {
                  Native.SendMessage(
                    message_box_handle, 
                    Native.WM_CLOSE, 
                    IntPtr.Zero, 
                    IntPtr.Zero);
              }
          }
      }
      catch
      {
          // Swallow all thread exceptions
      }
    });
  
    // Start the auto-close thread.
    auto_close_thread.Start();

    // Show the error message box.
    MessageBox.Show(message, title);

    // Notify the auto-close thread that we have shut-down the MessageBox.
    wait_handle.Set();
    auto_close_thread.Join();
    wait_handle.Dispose();
  }
}

This can be easily extended to match all the MessageBox.Show signatures.

For completeness, the Native methods are shown below.

/// <summary>
/// Wrapper class for the native method calls.
/// </summary>
internal class Native
{
  /// <summary>
  /// Retrieves a handle to the top-level window whose class name and window name
  /// match the specified strings. This function does not search child windows. 
  /// This function does not perform a case-sensitive search..
  /// </summary>
  /// <param name="className">Name of the class. If className is NULL, it finds any window
  /// whose title matches the windowName parameter. </param>
  /// <param name="windowName">Name of the window.</param>
  /// <returns>If the function succeeds, the return value is a handle to the window
  /// that has the specified class name and window name. If the function fails, the return value is NULL.</returns>
  [DllImport("user32.dll", SetLastError = true)]
  internal static extern IntPtr FindWindow(string className, string windowName);
  
  /// <summary>
  /// Sends the specified message to a window or windows.
  /// </summary>
  /// <param name="hwnd">A handle to the window whose window procedure will receive the message.</param>
  /// <param name="msg">The message to be sent.</param>
  /// <param name="wparam">Additional message-specific information wparam.</param>
  /// <param name="lparam">Additional message-specific information lparam.</param>
  /// <returns>The return value specifies the result of the message processing; it depends on the message sent.</returns>
  [DllImport("user32.dll", CharSet = CharSet.Auto)]
  internal static extern IntPtr SendMessage(IntPtr hwnd, uint msg, IntPtr wparam, IntPtr lparam);
}

SynchronizationContext Class

The SynchronizationContext class (System.Threading) provides a mechanism to invoke delegates on the same thread that the SynchronizationContext represents.

/// <summary>
/// Dispatches an asynchronous message to a synchronization context.
/// </summary>
/// <param name="d">The SendOrPostCallback delegate to call</param>
/// <param name="state">The object passed to the delegate</param>
public virtual void Post(SendOrPostCallback d, Object state)

/// <summary>
/// Dispatches a synchronous message to a synchronization context.
/// </summary>
/// <param name="d">The SendOrPostCallback delegate to call</param>
/// <param name="state">The object passed to the delegate</param>
public virtual void Send(SendOrPostCallback d, Object state)

The post and send methods take a SendOrPostCallback delegate that represents the method to be invoked on the same thread that the SynchronizationContext represents, and an object representing information to pass to that delegate.

The SynchronizationContext class provides a static property getter that gets the synchronization context for the current thread.

public static SynchronizationContext Current { get; }

This property is useful for propagating a synchronization context from one thread to another.

How do we use it?

The goal is to ensure that the caller thread into any background worker class creates the Synchronization context, which will then be used to signal the caller on the same thread as the caller when the operation is completed.

public class BackgroundWorker
{
    public event Action<object> WorkCompleted;

    public BackgroundWorker()
    {
        this.context = SynchronizationContext.Current;
        if (this.context == null)
        {
            context = new SynchronizationContext();
        }

        this.workerThread = new Thread(this.DoWork);
        this.workerThread.Start();
    }

    private void DoWork()
    {
        // ...
        // Do some work
        // ...

        // Signal the caller that we are done.
        object results = new object();
        context.Post(delegate(object state)
        {
            if (this.WorkCompleted != null)
            {
                this.WorkCompleted(results);
            }
        }, results);
    }

    private SynchronizationContext context;
    private Thread workerThread;
}

WarningThe above code is dangerous! The creation of a SynchronizationContext instance for a thread can lead to nasal demons. There are possible conditions which may lead to Form’s threads Synchronization context (known as WindowsFormsSynchronizationContext) not being used correctly.

AsyncOperationManager Class to the Rescue

The AsyncOperation and AsyncOperationManager class (System.ComponentModel) removes the requirement for accessing or creating SynchronizationContexts. They provide a thread-safe, context-safe environment to access a handle to the current threads SynchronizationContext, and then a safe means to use this for sending/posting information back to that thread’s context.

The AsyncOperation class wraps the SynchronizationContext, and is used for sending/posting messages.

Use the AsyncOperationManager to retrieve the AsyncOperation class which wraps the SynchronizationContext we’ll use for posting. This safely creates a SynchronizationContext object belonging to the thread in which this operation was called.

Below is the previous example re-written using these new classes.

public class BackgroundWorker
{
    public event Action<object> WorkCompleted;

    public BackgroundWorker()
    {
        this.asyncOperation = AsyncOperationManager.CreateOperation(null);

        this.workerThread = new Thread(this.DoWork);
        this.workerThread.Start();
    }

    private void DoWork()
    {
        object results = new object();
        this.asyncOperation.Post(delegate(object state)
        {
            if (this.WorkCompleted != null)
            {
                this.WorkCompleted(results);
            }
        }, results);
    }

    private AsyncOperation asyncOperation;
    private Thread workerThread;
}

Strong Typed Databinding

Win-Forms data-binding is done via a String parameter which specifies the member name to be bound, e.g.

// SOURCE DATA
class Person
{
    int Age { get; set; }
}

// SOME GUI APPLICATION
{
    // Create a TextBox and a Person.
    TextBox myTextBox = new TextBox();
    Person myPerson = new Person;
    myPerson.Age = 10;

    // Bind the age of that person to the textbox's text property.
    myTextBox.DataBindings.Add("Text", myPerson, "Age");
}

If a member name is changed (either manually or F2 – e.g), more often than not, the data-binding that is bound to that member will not be updated, the program will compile successfully, and the problem will not be found until run-time (hopefully found before the product is shipped). e.g. If we rename the Age property of the Person to AgeInYears, this program will compile successfully, but will fail at run-time.

Instead, it would be better if we could force the compiler to help us find these errors.

    // Instead of doing this.
    myTextBox.DataBindings.Add("Text", myPerson, "Age");

    // I would like to do this instead.
    myTextBox.Bind(tb => tb.Text).To(myPerson, p => p.Age);

With the aid of the PropertyNameHelper.GetPropertyName, we could do this with the following code.

public static class BindingHelper
{
    /// <summary>
    /// Returns a <c>BindingTarget</c> which can be used to bind a data source to a control.
    /// </summary>
    /// <typeparam name="T">The type of control to bind.</typeparam>
    /// <param name="controlToBind">The control to be bound.</param>
    /// <param name="expression">The expression representing the property of the control to bind to.</param>
    /// <returns>Returns the <c>BindingTarget</c> object which can be used to complete the binding process.</returns>
    public static BindingTarget Bind<T>(this T controlToBind, Expression<Func<T, object>> expression)
        where T : Control
    {
        // Simply return the binding target.
        return new BindingTarget(controlToBind, PropertyNameHelper.GetPropertyName<T>(expression));
    }
}

/// <summary>
/// Class that represents a control and property of the control, for data binding purposes.
/// </summary>
public class BindingTarget
{
    /// <summary>
    /// Creates a new <c>BindingTarget</c> instance.
    /// </summary>
    /// <param name="controlToBind">The control to bind to the data source.</param>
    /// <param name="propertyName">The property of the control to bind, to bind the data source to.</param>
    internal BindingTarget(Control controlToBind, string propertyName)
    {
        // Store away the control to be bound and the property name.
        this.controlToBind = controlToBind;
        this.controlPropertyName = propertyName;
    }

    /// <summary>
    /// Binds the <c>ControlToBind</c> to the data member of the specified data source.
    /// </summary>
    /// <typeparam name="T">The type of the data source to be bound to.</typeparam>
    /// <param name="dataSource">The data source to be bound to.</param>
    /// <param name="dataMemberExpression">The expression representing the data member of the data source to bind to.</param>
    public void To<T>(T dataSource, Expression<Func<T, object>> dataMemberExpression)
    {
        // Extract the name of the data member, (field or property), of the data source, from the expression.
        string data_member_name = PropertyNameHelper.GetPropertyName(dataMemberExpression);

        // Bind the control to be bound, to the data member of the data source.
        this.controlToBind.DataBindings.Clear();
        this.controlToBind.DataBindings.Add(this.controlPropertyName, dataSource, data_member_name);
    }

    /// <summary>
    /// The control to be bound to a data source.
    /// </summary>
    private Control controlToBind;

    /// <summary>
    /// The property name of the <c>ControlToBind</c> to bind the data source to.
    /// </summary>
    private string controlPropertyName;
}

Get string names of properties and fields

/// <summary>
/// Helper class to assist in getting the string names of properties and fields from a given class.
/// </summary>
public static class PropertyNameHelper
{
    /// <summary>
    /// Gets the name of the property represented by the expression for the given type.
    /// </summary>
    /// <typeparam name="T">The type of object being queried.</typeparam>
    /// <param name="expression">The expression.</param>
    /// <returns>Returns the string name of the property/member represented in the expression.</returns>
    public static string GetPropertyName<T>(this T dataObject, Expression<Func<T, object>> expression)
    {
        return PropertyNameHelper.GetPropertyName<T>(expression);
    }

    /// <summary>
    /// Gets the name of the property represented by the expression for the given type.
    /// </summary>
    /// <typeparam name="T">The type of object being queried.</typeparam>
    /// <param name="expression">The expression.</param>
    /// <returns>Returns the string name of the property/member represented in the expression.</returns>
    public static string GetPropertyName<T>(Expression<Func<T, object>> expression)
    {
        // Get the body part of the passed-in expression.
        Expression expression_body = expression.Body;
        if (expression_body == null)
        {
            throw new ArgumentException(string.Format("Expression '{0}' refers to a method, not a property.",
                expression.ToString()));
        }

        // Check if the body represents an expression accessing a class member.
        if (expression_body is MemberExpression)
        {
            // The expression represents a class member, so return the name
            // of the member that the expression represents.
            return (expression_body as MemberExpression).Member.Name;
        }

        // Check if the body represents a unary expression.
        if (expression_body is UnaryExpression)
        {
            // Get the operand of the unary expression, which should be
            // a member expression.
            MemberExpression operand = (expression_body as UnaryExpression).Operand as MemberExpression;
            if (operand == null)
            {
                // No operand was found, so return an empty string.
                return String.Empty;
            }

            // The operand contains the name of the member in the expression.
            return operand.Member.Name;
        }

        // Nothing found, so return an empty string.
        return String.Empty;
    }
}

Making Thread-Safe Calls on Windows Controls/Forms

InvokeRequired / BeginInvoke Method

To ensure a method is thread safe for windows controls/forms, it is common to implement the InvokeRequired/BeginInvoke pattern.

// No Parameter Example
void doSomething()
{
    if (this.InvokeRequired)
    {
        this.BeginInvoke(new Action(doSomething));
        return;
    }

    // Insert code that does work here
}

// Function with Parameter example.
void doSomething(int param1, string param2)
{
    if (this.InvokeRequired)
    {
        this.BeginInvoke(
            new Action<int, string>(doSomething), 
            new object[] { param1, param2});
        return;
    }

    // Insert code that does work here
}

Functionally, this works fine. The question is can we make it better? I believe the answer is yes.

Currently there are two main problems with this approach:

  • For a parametrized function, when updated, the corresponding delegate signature must also be updated.
  • Too often, I’ve seen the return statement missing within the if block leading to errors that are not found until run-time

The aim is to define a software architecture that will simplify the code, and improve code correctness.

Proposed Solution

A GUIExtensions static class has been created containing two static functions that allow us to execute the code on the thread that the control/form was created either synchronously or asynchronously.

using System.ComponentModel;
public static class GUIExtensions
{
  /// <summary> 
  /// Executes the delegate immediately if the current thread is the thread
  /// that created this object. Otherwise, asynchronously executes the delegate
  /// on the thread that created this object.
  /// </summary>
  /// <param="control">The control of interest</param>
  /// <param="code">The code to execute on the control's creation thread</param>
  /// <remarks>
  /// If you need to execute the code immediately, use the <code>UIThreadInvoke</code>
  /// method instead.
  /// </remarks>
  static public void UIThread(this ISynchronizeInvoke control, Action code)
  {
    // Determine whether the current thread is the creation thread of the control.
    if (control.InvokeRequired)
    {
      // Asynchronously execute the delegate on the thread that created this object.
      control.BeginInvoke(code, null);
      return;
    }

    // Just invoke the code.
    code.Invoke();
  }

  /// <summary>
  /// Executes the delegate immediately if the current thread is the thread
  /// that created this object. Otherwise, synchronously executes the delegate
  /// on the thread that created this object and marshals the call to the creating thread.
  /// </summary>
  /// <param name="control">The control of interest.</param>
  /// <param name="code">The code to execute on the control's creation thread.</param>
  static public void UIThreadInvoke(this ISynchronizeInvoke control, Action code)
  {
    // Determine whether the current thread is the creation thread of the control.
    if (control.InvokeRequired)
    {
      // Synchronously execute the delegate on the thread that created
      // this object and marshals the call to the creating thread.
      control.Invoke(code, null);
      return;
    }

    // Just invoke the code.
    code.Invoke();
  }
}

Example Usage

The best way to see the power of this proposed solution is to have a look at it in action.

The UIThread static functions may be called with anonymous functions (as shown below). Note that there are a couple of significant advantages of using this approach:

  • If the doSomething() signature changes, you do NOT have to update a inner-code delegate signature
  • You can use the function parameters directly
void doSomething(int param1, string param2)
{
    this.UIThread(delegate
    {
        // Insert code that does work here
        // NOTE: you can use param1, param2 directly here. 
    });
}

The UIThread static functions may also be called with lambda functions. This allows you to do quick in-line updates of GUI components within background threads.

void doSomething(int param1, string param2)
{
    // Insert code here that's doing something

    // Now update a GUI Text box on the correct thread
    this.UIThread(() => this.myTextBox.Text = "Some Text";);

    // Insert code here that's doing something  
}

Unit Testing your C# public interface

These days, it’s common practice to unit-test our code. Whether you’re a TDD, or simply doing white box / black box unit-testing post coding, the final act of having a unit test in place to test conformancy to certain requirements is good practice.

But what requirements should we be testing?

The common approach is to test the functionality of a unit of code, i.e. for a particular set of inputs, test for the expected output(s). This is good.

But with OO based-design, and win-forms data-binding, I believe we need to go one step further. We need to test the public interface declaration – the classes contract to the outside world. We need to test that the public interface for both the getters and setters of a class do not change scope or name.

Why do I say name? Win-Forms data-binding is done via a String parameter which specifies the member name to be bound.

If a member name is changed (either manually or F2), more often than not, the data-binding that is bound to that member will not be updated, the program will compile successfully, and the problem will not be found until run-time (hopefully found before the product is shipped).

So how can we test that our public interface has not changed? By using Reflection, we can write a unit-test helper class that will allow us to test the public interface declaration. The goals I’ve set for this PropertyTester is that it should:

  • Fail if a property’s getter or setter change’s scope
  • Fail if a property’s name changes
  • Fail if a property is not tested

Property Tester Helper Class

public class PropertyTester<T> : IDisposable
    where T : class
{
    /// <summary>
    /// Creates an instance of the <c>PropertyTester</c> class.
    /// This class allows you to check that the setters and getters for properties on the
    /// target are as expected. When used in a using statement, the Dispose call will
    /// confirm that all properties were tested.
    /// </summary>
    /// <param name="target">The target object of interest</param>
    public PropertyTester()
    {
        // Store away the target
        this.target = typeof(T);

        // Create a list of all the properties expected to be tested.
        const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
        this.targetPropertyList = new List<PropertyInfo>(target.GetProperties(flags));
    }

    /// <summary>
    /// Checks that the property getters and setters accessors for an object are as expected.
    /// </summary>
    /// <param name="memberName">The property of the object to be tested.</param>
    /// <param name="canRead">Whether we expect that we can read this property.</param>
    /// <param name="canWrite">Whether we expect that we can write to this property.</param>
    public void CheckGetterSetter(string propertyName, bool canRead, bool canWrite)
    {
        // Confirm that the getters and setters for the property are correct.
        // Get a handle to the Property of the interested target.
        PropertyInfo property = this.targetPropertyList.FirstOrDefault(p => p.Name == propertyName);
        if (property == null)
        {
            // If the test code fails here, then a Property name has changed on the target.
            // It is important that ALL GUI code that refers to this Property name is updated.
            // NOTE: The compiler may not find all of the GUI references for this Property name
            // as the ObjectListView class uses reflection for setting/getting values.
            this.failure = true;
            String failed_message = String.Format("Property \"{0}\" does not exist for object \"{1}\"", propertyName, target.ToString());
            Assert.Fail(failed_message);
        }

        // Confirm the existance of the public getter.
        if (canRead != (property.CanRead && property.GetGetMethod() != null))
        {
            this.failure = true;
            string failed_message = String.Format("Property \"{0}\": expected getter is {1}, actual getter is {2}.", propertyName, canRead, !canRead);
            Assert.Fail(failed_message);
        }

        // Confirm the existance of the public setter.
        if (canWrite != (property.CanWrite && property.GetSetMethod() != null))
        {
            this.failure = true;
            string failed_message = String.Format("Property \"{0}\": expected setter is {1}, actual setter is {2}.", propertyName, canWrite, !canWrite);
            Assert.Fail(failed_message);
        }

        // Remove properties with this name from the targetPropertyList list.
        // Note: We're removing all instances of properties with the name defined
        //   in the 'memberName' parameter as there could be more than one, as
        //   is the case when properties can be redefined in a sub-class with
        //   the 'new' keyword.
        this.targetPropertyList.RemoveAll(p => p.Name == propertyName);
    }

    /// <summary>
    /// Confirms that all property checker/getters were tested on the target.
    /// </summary>
    public void Dispose()
    {
        // If a failure hasn't occured during the test and the target property list
        // still contains objects, then this means some properties were not correctly tested.
        if ((this.failure == false) && (targetPropertyList.Count > 0))
        {
            string failed_message = "";
            foreach (PropertyInfo pinfo in this.targetPropertyList)
            {
                failed_message += String.Format("Property \"{0}\" was not tested for object \"{1}\"\r\n", pinfo.Name, this.target.ToString());
            }
            Assert.Fail(failed_message);
        }
    }

    /// <summary>
    /// The target object to be tested.
    /// </summary>
    private Type target = null;

    /// <summary>
    /// The list of properties for the target expected to be tested.
    /// </summary>
    private List<PropertyInfo> targetPropertyList = null;

    /// <summary>
    /// Whether a failure has been detected or not.
    /// </summary>
    private bool failure = false;
}

Example

To use this PropertyTester, let’s build a simple Person class

/// <summary>
/// Example Person class to demonstrate how to use the PropertyTester
/// <summary>
public class Person
{
    public string Name { get; private set; }
    public int Age { get; set; }
}

Then in your unit-testing code, all we need to do is

/// <summary>
/// Tests the properties getters and setters access to the Person Class.
/// </summary>
[TestMethod]
public void TestPropertyGetterSetter()
{
    // Create a PropertyTester to facilitate the testing of the getters/setters.
    using (PropertyTester<Person> propTester = new PropertyTester<Person>())
    {
        // Check the expected access to each property.
        propTester.CheckGetterSetter("Name", true, true);
        propTester.CheckGetterSetter("Age", true, false);
    }
}

Mutable & Immutable Classes

This post describes a method for creating a mutable and immutable version of the same class. At a first glance, there may seem to be two obvious ways to approach this problem; either:

  1. Create a base Mutable Class and derive a Immutable version of this class; or
  2. Create a base Immutable Class and derive a Mutable version of this class.

To help decide which may be the best design, we can refer back to the principles of object-oriented design. The Liskov Substitution Principle states that subtypes must be substitutable for their base types.

Don’t inherit unless you really are (and can be treated in all respects) like what you derive from.

Keeping this in mind, we can see that solution one would violate this principle. You can not treat an Immutable object as a Mutable object. i.e. you would not want a user to be able to cast an immutable object, to it’s parents-type, so that the user could then mutate that object.

A simple implementation of solution two is shown below.

    public class ImmutableClass
    {
        public int SomeProperty { get; protected set; }
        public string AnotherProperty { get; protected set; }
    }

    public class MutableClass : ImmutableClass
    {
        public new int SomeProperty
        {
            get
            {
                return base.SomeProperty;
            }
            set
            {
                base.SomeProperty = value;
            }
        }

        public new string AnotherProperty
        {
            get
            {
                return base.AnotherProperty;
            }
            set
            {
                base.AnotherProperty = value;
            }
        }
    }