Click here to Skip to main content
15,867,777 members
Articles / Desktop Programming / Windows Forms

The DesignSurface (Extended) Class is Back, Together with a DesignSurfaceManager (Extended) Class and a Form Designer Demo!

Rate me:
Please Sign up or sign in to vote.
4.93/5 (36 votes)
24 Feb 2010CPOL6 min read 92.7K   7.6K   84   41
Use all the design facilities of the DesignSurfaceExt class together with a Toolbox and PropertyGrid classes.

Demo_For_pFDesigner.png

Abstract

This article describes DesignSurface/DesignSurfaceManager inherited classes which add to the .NET classes some design facilities (TabOrder, UndoEngine, SnapLines/Grid alignment, ContextMenu with Cut/Copy/Paste commands, Toolbox, and Propertygrid). These classes are hosted inside DLL assemblies and are ready to use inside any .NET solution.

Introduction

Some people asked me how they could use the DesignSurfaceExt class together with a DesignSurfaceManager class, and for sample code demonstrating how to implement drag and drop operations on the DesignSurface; therefore, I resolved to write a second article explaining these issues. My previous article is available here: DesignSurfaceExt, and introduces you to the DesignSurfaceExt class which extends the .NET DesignSurface class. I extended this class further, improving its design facilities (TabOrder, UndoEngineSnapLines/Grid alignment) with a drag and drop facility and adorning the DesignSurface with a Toolbox and a Propertygrid-like User Control. The code is developed like Chinese boxes: each project encapsulates another one, and each one has a demo form which uses the classes to implement what can be perceived as a Form Designer. Finally, I wrote another User Control to host all these design facilities (exposed via an Interface). Feel free to use the code and to modify it as you like for building your own Form Designer.

Background

It is necessary to be familiar with C# programming. It'd useful to know the concepts used by the .NET designer and to read my previous CodeProject article: DesignSurfaceExt.

How to Use DesignSurfaceExt and DesignSurfaceManagerExt

Inside the attached code, I have included lots of demo projects to show how to use the classes, so I shall spend no more time talking about the usage.

The Nuts and Bolts of the Classes

The classes are structured as Chinese boxes: each class adds design functionalities over another one. From the ground up:

DesignSurfaceExt extends the .NET DesignSurface, adding the following facilities: TabOrder, UndoEngine, Cut/Copy/Paste/Delete commands.

//- DesignSurfaceExt
//-     |
//-     +--|DesignSurface|
//-     |
//-     +--|TabOrder|
//-     |
//-     +--|UndoEngine|
//-     |
//-     +--|Cut/Copy/Paste/Delete commands|

Please refer to the project DemoConsoleForDesignSurfaceExt to see a "Designer", based on DesignSurfaceExt, in action.

DesignSurfaceExt2 extends the previous DesignSurfaceExt, adding Toolbox mechanisms and ContextMenu with Cut/Copy/Paste/Delete commands.

//- DesignSurfaceExt2
//-     |
//-     +--|Toolbox|
//-     |
//-     +--|ContextMenu|
//-     |
//-     +--|DesignSurfaceExt|
//-             |
//-             +--|DesignSurface|
//-             |
//-             +--|TabOrder|
//-             |
//-             +--|UndoEngine|
//-             |
//-             +--|Cut/Copy/Paste/Delete commands|

Please refer to the project "DemoConsoleForDesignSurfaceExt2" to see a "Designer", based on DesignSurfaceExt2, in action.

DesignSurfaceManagerExt manages a collection of DesignSurfaceExt2 instances; it adds a PropertyGrid to each DesignSurfaceExt2 instance:

//- DesignSurfaceExt2
//-     |
//-     +--|PropertyGridHost|
//-     |
//-     +--|Toolbox|
//-     |
//-     +--|ContextMenu|
//-     |
//-     +--|DesignSurfaceExt|
//-             |
//-             +--|DesignSurface|
//-             |
//-             +--|TabOrder|
//-             |
//-             +--|UndoEngine|
//-             |
//-             +--|Cut/Copy/Paste/Delete commands|

Please refer to the project "DemoConsoleForDesignSurfaceManagerExt" to see a "Designer", based on DesignSurfaceManagerExt, in action.

Finally the pDesigner- p(ico) Designer User Control handles all the major aspects of what is perceived as a "Designer" and encompasses the code to manage the above classes.

Please refer to the project "DemoConsoleForpDesigner" to see a "Form Designer", based on pDesigner, in action.

Points of Interest

Every class useful for designing has an accompanying interface. You can call the design facilities of the class through this interface, but if you prefer, you can ignore them and straightaway use the classes.

About DesignSurfaceExt, please see my previous article: DesignSurfaceExt.

About DesignSurfaceExt2: very often, what the user wants for a designing environment is a toolbox. This is usually a nice little list of controls which tell you what kind of controls you can either "point and click" or "drag and drop" on to your surface. The .NET design environment is expressly built to manage these aspects via the ItoolboxService interface. When you implement this interface, you can code most toolbox functionality. But, the key point is the implementation of IToolboxService.DeserializeToolboxItem() together with IToolboxService.SerializeToolboxItem() and IToolboxService.SetSelectedToolboxItem(), together with IToolboxService.GetToolboxItems(). These are the methods called whenever you deploy a control on your brand new design surface. I implemented what is strictly necessary in order to have these functionalities inside DesignSurfaceExt2 (if you launch the demo for this class, you will see that the controls can be sited with both "point and click" and "drag and drop" mechanisms, while if you comment IDesignSurfaceExt2.EnableDragandDrop(), you will lose the "drag and drop" mechanism).

Now, it's time to talk about DesignSurfaceManagerExt. This class is used to extend the .NET 2.0 class DesignSurfaceManager. MSDN states: "Using DesignSurfaceManager is optional, but it is recommended if you intend to have several designer windows." What is this class used for? Basically, it's a central point to manage a collection of DesignSurface objects. Since it is a container of DesignSurface objects, we can use it to contain every object which is a DesignSurface, that is to say, every object which is derived from DesignSurface and therefore also DesignSurfaceManagerExt instances. To do that, we override the DesignSurface CreateDesignSurfaceCore( IServiceProvider parentProvider) method. You can see that the overridden method simply returns a new instance of the DesignSurfaceExt2 class. Inside this method, you can do whatever you want. Anyway, the core concept of DesignSurfaceManagerExt is that "it provides common services that handle event routing between designers [MSDN]". So, pay attention to the private method named Init() inside DesignSurfaceManagerExt: inside the method, I add to the services collection of the DesignSurfaceManagerExt class, my PropertyGrid user-control (actually the PropertyGrid and its ComboBox), virtually letting it be available to each DesignSurface which is created via DesignSurfaceManagerExt. How? See the DesignSurfaceExt2 CreateDesignSurfaceExt2() public method where, after creating the DesignSurface, I put the code necessary to maintain in sync the PropertyGrid with whatever is selected in the current surface.

Another point of interest is the following. MSDN states: "The ActiveDesignSurface property should be set by the designer's user interface whenever a designer becomes the active window". What I understand by this phrase is that it's supposed that I have to implement by myself an Observer Pattern of UI event "the current surface is changed", because only my UI knows when the current surface is changed! Again, inside the Init() method, you can see how the ActiveDesignSurfaceChanged event is managed with a delegate which updates the PropertyGrid (and the ComboBox) by hand.

Finally, some more words about the pDesigner User Control (it stands for picoDesigner) and its interface IpDesigner. I coded them because the final user, when thinking about a Form Designer, expects to find something like this:

//-     +-------------+-----------------------------+-----------+
//-     |toolboxItem1 | ____ ____ ____              |           |
//-     |toolboxItem2 ||____|____|____|___________  +-----------+
//-     |toolboxItem3 ||                          | |     |     |
//-     |             ||                          | |     |     |
//-     |  TOOLBOX    ||      DESIGNSURFACES      | | PROPERTY  |
//-     |             ||                          | |   GRID    |
//-     |             ||__________________________| |     |     |
//-     +-------------+-----------------------------+-----------+

That is to say: a toolbox, a collection of DesignSurfaces which can be switched/added/deleted, and a PropertyGrid which tells her/him the properties of the control currently selected. Good, so here is the interface:

C#
public interface IpDesigner {
    ListBox Toolbox { get; set; }                       //- TOOLBOX
    TabControl TabControlHostingDesignSurfaces { get; } //- DESIGNSURFACES HOST
    PropertyGridHost PropertyGridHost { get; }          //- PROPERTYGRID
    
    //- DesignSurfaces management section ---------------------------
    DesignSurfaceExt2 ActiveDesignSurface { get; }
    DesignSurfaceExt2 AddDesignSurface<TT>(int startingFormWidth, 
       int startingFormHeight, AlignmentModeEnum alignmentMode, 
       Size gridSize ) where TT : Control;
    void RemoveDesignSurface ( DesignSurfaceExt2 activeSurface );
    
    //- Editing section  --------------------------------------------
    void UndoOnDesignSurface();
    void RedoOnDesignSurface();
    void CutOnDesignSurface();
    void CopyOnDesignSurface();
    void PasteOnDesignSurface();
    void DeleteOnDesignSurface();
    void SwitchTabOrder();
}//end_interface

The User Control pDesigner encompasses all the functionalities said above and the management of the collection of DesignSurfaces via a TabControl used as a concrete container for the surfaces. Here you can see how the TabControl SelectedIndexChanged event is used to propagate the syncing of the PropertyGrid.

The last point which is worthy to be mentioned concerns the service IComponentChangeService. This service is marked as "Non replaceable service", and you can use it to monitor the changes which happen inside the surface; i mean, when a control is added/deleted or simply changed. Adding a handler to the events fired by this service lets you set a fine grained behaviour of your designer according to what happens inside the DesignSurface.

Conclusion

As usual, feel free to use the code and to modify it as you like (and let me know your impressions)!

P.S.: Some of you complained that I posted a bunch of code but there is no accompanying article (by the way, Marc: your comment "very useful" is really a big reward for me). I agree with you, therefore with this update, I dug a little more into the source code to explain some issues ;)

History

  • 19 February 2010 - First submission of the article/code.
  • 22 February 2010 - Updated the "Points of Interest" section of the article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Italy Italy
I started coding in 1984 on a ZX Spectrum. From the Sinclair Basic I jumped straight to C++ (which I use since 1992). I wrote code in assembly 386, ANSI C++ (I love it) and VisualBasic 6.0 (urgh!!!); nowadays I write code in C# 4.0!

I was born in 1968 in Italy and I live in the north west of my country (near Venice). I work as a Project Leader/Senior Developer.

Computer Science interests: I like coding 3D-GameEngine and OpenGL applications.

Sport and hobbies: Martial Arts, Latino Dance, travels, reading and writing novels.

Comments and Discussions

 
GeneralGreat Work - A Question on Control events Pin
David Ballantyne17-May-10 6:07
David Ballantyne17-May-10 6:07 
GeneralRe: Great Work - A Question on Control events Pin
roks nicolas29-Jan-13 0:02
professionalroks nicolas29-Jan-13 0:02 
QuestionHow do I check If is control is dropped/created within a Parent control or in RootComponent ? Pin
Ruchit S.9-Apr-10 3:35
Ruchit S.9-Apr-10 3:35 
GeneralMaybe this article will help Pin
Jason Song22-Feb-10 4:41
Jason Song22-Feb-10 4:41 
GeneralVery useful! [modified] Pin
Marc Clifton21-Feb-10 2:21
mvaMarc Clifton21-Feb-10 2:21 
GeneralExcellent. 5++. Pin
VDmitrovsky20-Feb-10 23:45
VDmitrovsky20-Feb-10 23:45 
GeneralRe: Excellent. 5++. Pin
VDmitrovsky20-Feb-10 23:57
VDmitrovsky20-Feb-10 23:57 
GeneralRe: Excellent. 5++. Pin
VDmitrovsky24-Feb-10 22:24
VDmitrovsky24-Feb-10 22:24 
Alas, I was too quick. Un/Redo engine seems to not work at all in DemoConsoleForDesignSurfaceManagerExt sample. In other samples its work is unstable, and varies in different scenarios.

dva.
GeneralRe: Excellent. 5++. Pin
Mazen el Senih7-Apr-12 3:14
professionalMazen el Senih7-Apr-12 3:14 
GeneralMy vote of 1 Pin
Paw Jershauge20-Feb-10 12:01
Paw Jershauge20-Feb-10 12:01 
GeneralGreat Pin
Igor I. Golovin19-Feb-10 23:04
Igor I. Golovin19-Feb-10 23:04 

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.