Library tutorials & articles
Hosting Windows Forms Designers
Starting Off
Starting Off
First we'll create our class, DesignerHost, and make sure we have a reference to System.Design.dll. A lot of the interfaces we'll be using are in there. We will accept an instance of something implementing IServiceContainer as the parameter to the constructor, and since we have to provide an IServiceContainer implementation ourselves we'll simply wrap that one. Since that's the easiest part we'll do it first.
public object GetService(System.Type serviceType)
{
return parent.GetService(serviceType);
}
public void AddService(System.Type serviceType,
System.ComponentModel.Design.ServiceCreatorCallback callback, bool promote)
{
parent.AddService(serviceType, callback, promote);
}
public void AddService(System.Type serviceType,
System.ComponentModel.Design.ServiceCreatorCallback callback)
{
parent.AddService(serviceType, callback);
}
public void AddService(System.Type serviceType, object serviceInstance, bool promote)
{
parent.AddService(serviceType, serviceInstance, promote);
}
public void AddService(System.Type serviceType, object serviceInstance)
{
parent.AddService(serviceType, serviceInstance);
}
public void RemoveService(System.Type serviceType, bool promote)
{
parent.RemoveService(serviceType, promote);
}
public void RemoveService(System.Type serviceType)
{
parent.RemoveService(serviceType);
}
At the same time as implementing IDesignerHost we will implement IContainer. This is the design container I spoke of before. All the code we're writing really revolves around the code that gets and instantiates designers for the objects added to the host.
IContainer.Add
This method has two overloads, one where a name is passed and one without. We'll just defer the latter to the former, passing null and dealing with it correctly. This is one of the methods with the real meat in.
The first thing we do after checking the passed component isn't null, is to check the component isn't already sited somewhere. If it is, we remove it from its current container. Then we generate a name for the component if we haven't been passed one, using INameCreationService. We'll come on to implementing this interface later. Next we make sure there isn't already a component with the passed name in the container.
The next stage is to give the new component a Site, which we do with our ISite implementation. I'll come on to that later. Giving it a site as early as possible is important, because that's how the component is able to request services from its environment. Then we make the important call to TypeDescriptor.CreateDesigner, which actually finds and created the designer associated with this component. If this is the first component being added we look for an IRootDesigner and set the rootComponent field.
Next comes a check to make sure we got a designer, and if so we initialize it. Remember the Initialize method of a designer that you override when making your own? This is where we actually call that method. After this we see if this component is an extender provider, and if so add it to the list we maintain. Yes, we have to write the functionality to keep track of those. All the magic is taken out of the designer architecture when you have to implement it yourself. Finally we add the component to our internal container instance.
public void Add(System.ComponentModel.IComponent component, string name)
{
IDesigner designer = null;
DesignSite site = null;
// Check we're not trying to add a null component
if (component == null)
throw new ArgumentNullException("Cannot add a null component " +
"to the container.");
// Remove this component from its existing container, if applicable
if (component.Site != null && component.Site.Container != this)
component.Site.Container.Remove(component);
// Make sure we have a name for the component
if (name == null)
{
INameCreationService nameService = (INameCreationService)GetService(
typeof(INameCreationService));
name = nameService.CreateName(this, component.GetType());
}
// Make sure there isn't already a component with this name in the container
if (ContainsName(name))
throw new ArgumentException("A component with this name already " +
"exists in the container.");
// Give the new component a site
site = new DesignSite(this, name);
site.SetComponent(component);
component.Site = site;
// Let everyone know there's a component being added
if (ComponentAdding != null)
ComponentAdding(this, new ComponentEventArgs(component));
// Get the designer for this component
if (components.Count == 0)
{
// This is the first component being added and therefore
// must offer a root designer
designer = TypeDescriptor.CreateDesigner(component,
typeof(IRootDesigner));
rootComponent = component;
}
else
{
designer = TypeDescriptor.CreateDesigner(component,
typeof(IDesigner));
}
// If we got a designer, initialize it
if (designer != null)
{
designer.Initialize(component);
designers[component] = designer;
}
else
{
// This should never happen
component.Site = null;
throw new InvalidOperationException("Failed to get designer for " +
"this component.");
}
// Add to our list of extenderproviders if necessary
if (component is IExtenderProvider)
{
IExtenderProviderService e = (IExtenderProviderService)GetService(
typeof(IExtenderProviderService));
e.AddExtenderProvider((IExtenderProvider)component);
}
// Finally we're able to add the component
components.Add(component.Site.Name, component);
if (ComponentAdded != null)
ComponentAdded(this, new ComponentEventArgs(component));
}
IContainer.Remove
Although not quite as complicated as IContainer.Add, this method is responsible for the cleanup process of removing a component from the design surface.
The first couple of check we do are to make sure the component passed isn't null, and to make sure the component actually belongs to our design surface. Then we wrap the rest of the method in ComponentRemoving and then ComponentRemoved calls so that classes listening to our IComponentChangeService implementation know what's going on.
We have to remove the component from our list of extender providers if necessary, then dispose of and remove the designer associated with it. After that, we set the component's Site to null and that's all that's needed.
public void Remove(System.ComponentModel.IComponent component)
{
ISite site = component.Site;
IDesigner designer = null;
// Make sure component isn't null
if (component == null)
return;
// Make sure component is sited here
if (component.Site == null || component.Site.Container != this)
return;
// Let the nice people know the component is being removed
if (ComponentRemoving != null)
ComponentRemoving(this, new ComponentEventArgs(component));
// Remove extender provider (if any)
if (component is IExtenderProvider)
{
IExtenderProviderService e = (IExtenderProviderService)GetService(
typeof(IExtenderProviderService));
e.RemoveExtenderProvider((IExtenderProvider)component);
}
// Remove the component and dispose of its designer
components.Remove(site.Name);
designer = (IDesigner)designers[component];
if (designer != null)
{
designer.Dispose();
designers.Remove(component);
}
// Let the nice people know the component has been removed
if (ComponentRemoved != null)
ComponentRemoved(this, new ComponentEventArgs(component));
// Kill the component's site
component.Site = null;
}
Related articles
Related discussion
-
Binary Studio | software development outsourcing Ukraine
by shane124 (4 replies)
-
Chart insertation in a windows form...
by pdhanik (1 replies)
-
Point of Sale Developers: Hardware & C# SDK
by ManiGovindan (7 replies)
-
help with the remote frame buffer protocol from real VNC
by poison (0 replies)
-
Need help making a complete program editable, C# or .net I think
by davelee (1 replies)
Related podcasts
-
A Practical Look at Silverlight 2 Part 1
Now that Silverlight 2 is at the Olympics and making a big splash, we wanted to explore this fascinating technology more. Microsoft Silverlight 2 is a cross-browser, cross-platform, and cross-device plug-in for delivering the next generation of .NET based media experiences and rich interactive ap...
Events coming up
-
Dec
9
GL.net Group Meeting - December 2009
Gloucester, United Kingdom
The beginning of this year holiday season will belong to mocks. Ronnie and Stephen will take us for a tour around exciting world of unit testing.
How would I go about saving a form that I have designed?
!--removed tag-->I have (am still) modifying this code to create a stand-alone Xaml generator for forms. The Xaml is structured for direct consumption by Windows Presentation Foundation. This was a great starting place.
In the course of my modifications and testing, I found a couple of bugs in the code which you may wish to correct in your source.
The Name property is listed twice in the property grid.
If you add a control (i.e. a Button), then change the name from the default (Button1) to anything else, then add another button, you get an unhandled exception. This does not occur if all like controls are added first then the names are modified. The cause is when the control is first added to the designer, it is added to the DesignerHost components collection using the default (Button1) control name as a key. When the Name property is updated, the components collection is not. When adding a second like control, the DesignerHost attempts to add it using the default name as well. However, since the first control was renamed, the default name of the second control is the same as it was for the first (Button1). An unhandled exception is thrown when the second control is added because the components collection already contains a control with the same key value.
I corrected this by attaching a handler to the PropertyValueChanged event of the PropertyGrid which removes the original entry for the control and readds it with the new name as the key whenever the Name property value is changed.
how to change Designer for design PocketPC components?
htx
v!tek
Hi Tim, great article. I am currently creating a designer in a similar manner to your article, used to create pdf files. I am using it to add only textboxes and pictures, and can save the data to a database. However I need to bring back the objects and add them to the form programmatically so the user can edit the objects (change the positions etc) Do you know how (or explain how) to add items to the designer surface programmatcally without using the drag and drop from the toolbox
Thanks
John Harry
Tim:
Thanks for your nice article.
How can I get the location of a control in the design surface. I want to show the coordinates when the user moves the control, but I can't figure how!
Did you figure it out how to persist the designer content? I would appreciate any help on this
Thanks
Sgirase
Hi!
That article is really great. It gave me useful hints to start my application in perfectmanner , which looks like an .Net IDE . but can you please tell me how to remove a control from the designer form ....
Hello Tim,
Thank you very much for this excellent article.
We have developed a graphical macro language in C# for our customers and one of the remaining hurdles that we have is the ability to edit the location of controls on a runtime form.
When the customer wants to edit our form, we scan our macro and generate controls on the form based upon the macro content.
We would then like to display that form with the controls where the customer can position and size those controls like you can in this Designer example. We would then iterate through the list of controls and remember their locations and sizes within the macro.
Can you please lead us in the right direction? We have been searching, trying, and reading many examples to no avail. It appears that you possess this knowledge and could help us. We are not inexperienced developers, I personally have been a software developer for 22 years in many languages.
For example, how would you modify this example so that controls can be placed on the form with out the user clicking on the designer surface?
Thank you very much for your reply,
Rick Wirch
Good question ... I am actually tasked with building a Web Forms Designer. I can load ASP.NET controls into my toolbox but of course can't drop those controls onto a Windows forms. Can somebody help me!!
Much appreciated -
Jon
Is it possible to use a treeview for that?
That article is really great.
It gave me the needed hints to step into this subject as deep as I wanted to. As i'm currently trying tom write a lightweight IDE for customumizing my own applications, i need to know, how the content auf the designer could be persisted and reloaded.
Is there any standard-mechanism for that, or will I have to implement my own handlers?
Any help would be appreciated!
Its possible to do something similar to your example using web forms designer?
This article is simply great. I wish to see more like this one.
Arthur
This thread is for discussions of Hosting Windows Forms Designers.