Click here to Skip to main content
15,879,326 members
Articles / Programming Languages / C#

Using Extension Methods to Create Form Extensions

Rate me:
Please Sign up or sign in to vote.
3.44/5 (4 votes)
5 Feb 2009Ms-PL3 min read 46.1K   405   20   11
This article demonstrates the use of the Extension Methods language feature of C# 3.0.

JonasButt Windows Forms Extensions

Introduction

The provided source code demonstrates the use of the Extension Methods language feature of C# 3.0.

I consider myself to be a junior .NET (C#) developer. Not long ago, I started programming small to midsize Windows Forms applications using the .NET framework and Visual Studio. I recently upgraded to .NET 3.5 and Visual Studio 2008, and I'm now particularly interested in the C# language and its (recently added) features.

On one programming project, I needed to create a form containing some controls. The form had to support resizing, and I noticed my controls remained at the same location when the form was resized. I played around with the Dock property and the Anchor property of the control, and this solved the problem in some cases. I also tried using the FlowLayoutPanel and TableLayoutPanel, but I wasn't very satisfied with the results. I decided I had do the moving of the controls myself, so I wrote this code:

C#
#region Resizing Support Methods

private Size previousSize = Size;

private void theForm_ResizeBegin(object sender, EventArgs e)
{
    previousSize = Size;
}

private void theForm_SizeChanged(object sender, EventArgs e)
{
    previousSize = Size;
}

private void theForm_Resize(object sender, EventArgs e)
{
    int widthDifference = Size.Width - previousSize.Width;
    int heightDifference = Size.Height - previousSize.Height;

    MoveControl(findButton, widthDifference, 0);
    MoveControl(aboutButton, widthDifference, heightDifference);
    MoveControl(helpLabel, 0, heightDifference);
    MoveControl(saveToFileButton, widthDifference, heightDifference);
}

private static void MoveControl(Control control, int x, int y)
{
    control.Location = new Point(control.Location.X + x, control.Location.Y + y);
}

#endregion

This works fine, but this code was duplicated for each form class in the application. Furthermore, I figured that this code was not supposed to be in the form class itself, but separated in some generic way to improve reuse. Using two Extension Methods, I was able to dramatically simplify code in the form class itself. In the section "Using the code" below, you can see how easy it is to add this support for any form.

Below is the class diagram for the form extension code.

JonasButt Windows Forms Extensions Class Diagram

Background

The following text is extracted from the official C# 3.0 language specification. It is Copyright of Microsoft Corporation 1999-2007. All Rights Reserved. Tip: download the full C# 3.0 specification here.

When the first parameter of a method includes the this modifier, that method is said to be an Extension Method. Extension Methods can only be declared in non-generic, non-nested static classes. The first parameter of an Extension Method can have no modifiers other than this, and the parameter type cannot be a pointer type. The following is an example of a static class that declares two Extension Methods:

C#
public static class Extensions
{
    public static int ToInt32(this string s) {
        return Int32.Parse(s);
    }
    public static T[] Slice<t />(this T[] source, int index, int count) {
        if (index < 0 || count < 0 || source.Length – index < count)
            throw new ArgumentException();
        T[] result = new T[count];
        Array.Copy(source, index, result, 0, count);
        return result;
    }
}

An Extension Method is a regular static method. In addition, where its enclosing static class is in scope, an Extension Method can be invoked using the instance method invocation syntax (§7.5.5.2), using the receiver expression as the first argument. The following program uses the Extension Methods declared above:

C#
static class Program
{
    static void Main() {
        string[] strings = { "1", "22", "333", "4444" };
        foreach (string s in strings.Slice(1, 2)) {
            Console.WriteLine(s.ToInt32());
        }
    }
}

The Slice method is available on the string[], and the ToInt32 method is available on string, because they have been declared as Extension Methods. The meaning of the program is the same as the following, using ordinary static method calls:

C#
static class Program
{
    static void Main() {
        string[] strings = { "1", "22", "333", "4444" };
        foreach (string s in Extensions.Slice(strings, 1, 2)) {
            Console.WriteLine(Extensions.ToInt32(s));
        }
    }
}

Using the Code

To use the extension for a given form, you need to call the EnableResizeSupport() Extension Method in the constructor of that form. Next, for each control in your form you want the extension operate on, call the AddMovableControl() Extension Method. This method expects a reference to the control and the type of the move behaviour the control should have. These types, encapsulated in an enum named MovableControlType, are:

  • Both: the control moves both horizontally and vertically when the form is resized;
  • Vertical: the control moves vertically when the form is resized;
  • Horizontal: the control moves horizontally when the form is resized.
C#
public Form1()
{
    InitializeComponent();
    this.EnableResizeSupport();
    this.AddMovableControl(secondFormButton, MovableControlType.Both);
    this.AddMovableControl(exitButton, MovableControlType.Both);
}

Points of Interest

When first writing the code, the thought arose that the extension would probably work fine for one form, but would have unexpected results when used in multiple forms at the same time. This problem was due to the lack of state of the static class implementing the Extension Methods. This problem was overcome by adding a state using a Dictionary containing the references to the forms, the controls added by that form, and the previous size of the form. Each form is identified by its handle, which is the key for the Dictionary.

Critics might not be in favor of this solution (adding state to a static class), but in this case, it works out pretty neat. I'm open to receive constructive criticism regarding this implementation detail.

C#
internal class FormResizeExtensionState : Dictionary<string, >
{
}

internal class FormResizeExtensionStateEntry
{
    public Size PreviousSize
    {
        get;
        set;
    }

    public Form Form
    {
        get;
       set;
   }

    public MovableControls MovableControls
    {
       get;
       set;
   }

}


private static FormResizeExtensionState state = new FormResizeExtensionState();

public static void EnableResizeSupport(this Form form)
{
    string key = form.Handle.ToString();
    if (!state.ContainsKey(key)) {
        state.Add(
            form.Handle.ToString(),
            new FormResizeExtensionStateEntry 
            {
                Form = form,
                MovableControls = new MovableControls(),
               PreviousSize = form.Size
            }
        );
        form.ResizeBegin += new EventHandler(form_ResizeBegin);
        form.SizeChanged += new EventHandler(form_SizeChanged);
        form.Resize += new EventHandler(form_Resize);
    }
}

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Software Developer The Code Architect
Netherlands Netherlands
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralHm ... Pin
jovdb6-Feb-09 9:12
jovdb6-Feb-09 9:12 
Rant.... Pin
eitherxor5-Feb-09 13:41
eitherxor5-Feb-09 13:41 
GeneralLet's see if I'm understanding this right Pin
supercat95-Feb-09 12:54
supercat95-Feb-09 12:54 
GeneralRe: Let's see if I'm understanding this right Pin
PIEBALDconsult5-Feb-09 15:21
mvePIEBALDconsult5-Feb-09 15:21 
GeneralRe: Let's see if I'm understanding this right Pin
Moim Hossain5-Feb-09 21:12
Moim Hossain5-Feb-09 21:12 
GeneralRe: Let's see if I'm understanding this right Pin
PIEBALDconsult6-Feb-09 4:00
mvePIEBALDconsult6-Feb-09 4:00 
Why? I know about Extension Methods. There are things I don't like about them, but I have written a few.
GeneralRe: Let's see if I'm understanding this right Pin
supercat96-Feb-09 6:39
supercat96-Feb-09 6:39 
GeneralRe: Let's see if I'm understanding this right Pin
PIEBALDconsult6-Feb-09 7:14
mvePIEBALDconsult6-Feb-09 7:14 
GeneralRe: Let's see if I'm understanding this right Pin
supercat96-Feb-09 9:20
supercat96-Feb-09 9:20 
GeneralRe: Let's see if I'm understanding this right Pin
PIEBALDconsult6-Feb-09 9:43
mvePIEBALDconsult6-Feb-09 9:43 
GeneralUmmm... Pin
PIEBALDconsult5-Feb-09 9:17
mvePIEBALDconsult5-Feb-09 9:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.