Hosting Windows Forms Designers

Implementing ISite and more

Implementing ISite

Our ISite implementation, called DesignSite, is going to be a poweful little beast that also implements IDictionaryService, the interface that for some reason some components use instead of using their own internal dictionaries. There isn't really much point going in to any of the stuff in this class in detail because it's mostly just template code except in the setter for the Name property.

The setter needs to check there isn't already a component with the name passed in the container. If there isn't, it can then proceed. To play friendly with the rest of the design environment, it must cause the IComponentChangeService implementation to raise the OnComponentChanging, OnComponentRename and OnComponentChanged events in that order while the change is made.

Implementing IComponentChangeService

This is an easy one. All this interface has is seven events and two methods which raise two of the events. All we do is add code for those two methods, and add another, internal method which is needed for our DesignSite class to raise the ComponentRename event. Although implementing this interface is easy, what's hard is working out where else in our implemented code to raise the events on it.

public void OnComponentChanged(object component, System.ComponentModel.MemberDescriptor
member, object oldValue, object newValue)
{
    if (ComponentChanged != null)
        ComponentChanged(this, new ComponentChangedEventArgs(component,
        member, oldValue, newValue));
}
public void OnComponentChanging(object component,
System.ComponentModel.MemberDescriptor member)
{
    if (ComponentChanging != null)
        ComponentChanging(this, new ComponentChangingEventArgs(component, member));
}
internal void OnComponentRename(object component, string oldName, string newName)
{
    if (ComponentRename != null)
        ComponentRename(this, new ComponentRenameEventArgs(component,
        oldName, newName));
}
public event System.ComponentModel.Design.ComponentEventHandler ComponentAdded;
public event System.ComponentModel.Design.ComponentEventHandler ComponentAdding;
public event System.ComponentModel.Design.ComponentChangedEventHandler ComponentChanged;
public event System.ComponentModel.Design.ComponentChangingEventHandler ComponentChanging;
public event System.ComponentModel.Design.ComponentEventHandler ComponentRemoved;
public event System.ComponentModel.Design.ComponentEventHandler ComponentRemoving;
public event System.ComponentModel.Design.ComponentRenameEventHandler ComponentRename;

Implementing IDesignerHost

This should really have been the class we started with, but IDesignerHost, IContainer, ISite and IComponentChangeService all depend on each other so much it makes sense to write about them in the order it's necessary to code them in. I'll cover the implementations of the members on IDesignerHost as I write them as best I can.

  • Activate - This method is called to activate the root component. Our implementation will grab ISelectionService and set the root component as the primary selection.
  • CreateComponent - This method is used by designers to have the host environment create an instance of a class that is to be situated on the design surface. Calling it is functionally identical to creating instance themselves then adding it to the container.
  • DestroyComponent - This is called to remove a component from the design surface and dispose of it properly. This method is also responsible for using IComponentChangeService to let others know what is going on as the component is being removed.
  • CreateTransaction - Creates an instance of our template DesignerTransaction class (I'll come on to that in a minute) and adds it to the stack of transactions.
  • GetDesigner - We just use our designers hashtable to return the designer associated with the passed component.
  • GetType - We check if there is an ITypeResolutionService available, and if so use it. If not, return Type.GetType() instead.
  • Container - Just return the instance of the class since it implements IContainer itself.
  • InTransaction - Return true if the number of transactions in our stack is greater than zero.
  • Loading - For this example we are not concerned with loading hosts, so just return false.
  • RootComponent - Return rootComponent, the first component added to our container.
  • RootComponentClassName - Return the name of the class the first component added to our container was created from.
  • TransactionDescription - Return the name of the transaction at the top of our stack, if there is one.

Designer Transactions

Every small change to a component should go through the IComponentChangeService, but when lots of small changes need to be wrapped up logically, that's where designer transactions come in. If we were writing a full-scale implementation of a design environment, we would keep track of these for undo/redo support.

There is no interface to be implemented here, we just need to inherit the abstract class DesignerTransaction with our own. All ours will do is make sure the appropriate events are raised by the host when the transaction is committed or cancelled.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“The generation of random numbers is too important to be left to chance.” - Robert R. Coveyou