Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

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

0.00/5 (No votes)
24 Feb 2010 1  
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:

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here