Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Design Time Architecture in .NET 2.0 - Part 1

4.67/5 (15 votes)
13 May 2009CPOL4 min read 50.7K   1.2K  
DesignSurface, IToolboxService, and INameCreationService.

Introduction

While using Visual Studio .NET, we rarely think about the things which makes it a WYSIWYG editor. I am targeting this series to have a working knowledge of the rich capabilities provided by Microsoft .NET 2.0 which enables us to make our own Layout Designers.

There is one more article available on CodeProject related to DesignSurface extension, which can be found here.

layout1.jpg

Background

I recently got a chance to work on a layout editor for a Windows application. The concept was simple, of having a GUI tool to create form layouts and save them in XML format for further processing. The interesting part here was how to get the VS.NET kind of feasibility and friendlessness while writing my own editor. The Microsoft .NET framework saved my day! Otherwise, I would have to have written every single thing from scratch. I am planning to publish an article every week so, please be patient for the next one.

Microsoft .NET and Designer Support

Microsoft .NET provides the System.Design.dll assembly. This assembly has some important classes which enables users to write their own layout designers. In this series, we are going to discuss about these one by one as we keep on adding more and more functionality to our layout designer.

The DesignSurface Class

Now, let's talk about the DesignSurface class. This is the base container for your layout designer. As we need a surface on which we can design our own form, the DesignSurface class enables the user to have his own container. Following is the definition of DesignSurface (extracted from meta data):

C#
public class DesignSurface : IDisposable, IServiceProvider
{
    public DesignSurface();
    public DesignSurface(IServiceProvider parentProvider);
    public DesignSurface(Type rootComponentType);
    public DesignSurface(IServiceProvider parentProvider, Type rootComponentType);
    .
    .
    .
    .
    .

For more information about this class, please refer to the MSDN documentation.

Following is a small code snippet for instantiating the DesignSurface class. This code creates a surface and adds it on the main form of the sample application.

C#
namespace L1
{
    public partial class DemoForm : Form
    {
        private DesignSurface m_designSurface;
        
        public DemoForm()
        {
            InitializeComponent();

            // Initialize our demo appliaction
            m_designSurface = new DesignSurface(typeof(Form));
        }

        private void DemoForm_Load(object sender, EventArgs e)
        {
            // Add the design surface
            Control designView = m_designSurface.View as Control;
            designView.Dock = DockStyle.Fill;
            this.Controls.Add(designView);
        }
    }
}

The code:

C#
// Initialize our demo appliaction
m_designSurface = new DesignSurface(typeof(Form));

Adds the DesignSurface by creating the base component, that is the Form. So, here, we are designing a form inside our application.

Services Provided by DesignSurface

We got a surface for designing the components and, for accessing the controls over this surface, we should have services. These services are provided to help us in accessing individual components on the surface, for getting events and other information like information about selected components and so on. There are two types of these services: Replaceable and Non-Replicable.

Replaceable Services

We can call these services as replaceable because we are able to plug-in our own implementations and replace these by our own components. Following are the replaceable services available. For replacing these services, we have to subclass the DesignSurface class as, in the constructor, it adds the default services. These services are accessible through the ServiceContainer property and can be replaced by overriding it and adding our own implementation.

  • IExtenderProviderService
  • IExtenderListService
  • ITypeDescriptorFilterService
  • ISelectionService
  • IReferenceService
  • DesignSurface
  • DesignerOptionService

Non-Replaceable Services

These services can not be replaced and are provided by default. Following is the list:

  • IComponentChangeService
  • IDesignerHost
  • IContainer
  • IServiceContainer

IDesignerHost Service

For our small demo application, we are going to make use of this service. The IDesignerHost service is used to access/manage the components that are available over the design surface. This is the non-replaceable service, and for getting the reference to the default implementation, we have to call the GetService() method of the design surface. Following is the code snippet:

C#
// Initialize our demo appliaction
m_designSurface = new DesignSurface(typeof(Form));
IDesignerHost designerHost = 
  m_designSurface.GetService(typeof(IDesignerHost)) as IDesignerHost;
Form templateForm = designerHost.RootComponent as Form;
// Set the default properties of our base template form
templateForm.Text = "Demo Form";

This code creates a DesignSurface, and by getting the reference to the IDesignerHost, we are able to access the components that are available on the surface. Here, we have the RootComponent which is a Form. Having a reference to the root component, we can modify its properties, and here, we are setting the Text property to "Demo Form".

Register Your Own Services - IServiceContainer

As the name suggests, service container is the container with services registered. The IServiceContainer interface provides the mechanism to add/modify/remove the registered services. For drawing controls on the design template, we have to override one service and that is the toolbox. For registering our own toolbox implementation, we need to have a reference to the service container, and it can be retrieved using the GetService() method of DesignSurface.

C#
//Get the service container
IServiceContainer serviceContainer = 
    m_designSurface.GetService(typeof(IServiceContainer)) as IServiceContainer;

Adding Controls on the Template Form - IToolboxService and ToolboxService

We got the form and we can register new services now. We will now look at how to add components on this form. For ease of use, .NET framework provides an interface: IToolboxService interface. For ease of development, the .NET framework has the ToolboxService abstract class which contains an implementation for the important methods. Now, we are going to create our own toolbox service named DemoToolboxService. This is more or less a dumb class for the basic implementation of the toolbox. We need it to get our small application working, and will discuss it in detail in my second article.

C#
namespace L1
{
    class DemoToolboxService : ToolboxService
    {
        // Categories
        CategoryNameCollection m_categoryNameCollection;

        protected override CategoryNameCollection CategoryNames
        {
            get
            {
                if (m_categoryNameCollection == null)
                    m_categoryNameCollection = 
                      new CategoryNameCollection(new string[] { "Controls" });

                return m_categoryNameCollection;
            }
        }

        protected override System.Collections.IList 
                  GetItemContainers(string categoryName)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        protected override System.Collections.IList GetItemContainers()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        protected override void Refresh()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        protected override string SelectedCategory
        {
            get
            {
                return "Controls";
            }
            set
            {
                // Need not to set any thing right now
            }
        }

        protected override ToolboxItemContainer SelectedItemContainer
        {
            get
            {
                return new ToolboxItemContainer(new ToolboxItem(typeof(Button)));
            }
            set
            {
                // Need not to set any thing right now
            }
        }
    }
}

In DemoToolboxService, we have only one category of controls, and that is "Controls". The user is not able to select any thing, and can draw only buttons as, the toolbox is not yet implemented.

Goof Ups!

While using this application, did you note??? Where is the name on the button? How do we provide it? Try to find it! Or wait until the next article.

License

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