Hosting a Designer Surface in Windows Forms Part Zero
Note that my 1st article in this series begins with Zero because I am partial to c-family languages. Yes, I'm hilarious. My apologies to those with smaller screen sizes, but the screenshots need to be readable.
The first step we take is to create UserControl that will contain the entire UI representation of the designer surface. You could put all of this on a Form but in my case It'd be handy to have several of these open at once using a tab page interface. On the UserControl I place a Panel with AutoScroll=true and a PropertyGrid. The PropertyGrid should look familiar as its essentially what you've come to know and love from VS2005. I also add another UserControl I’ve created called DefaultToolboxService. This control has a Checkbox and a ListBox control on it. So far I’ve got something like this:
As you can tell my initial goal is simple: I want to select an item in my ListBox and have it be automagically created when I click on my designer area. The next step will be to start tying in some of the plumbing to allow this to happen. This is a good place to introduce the notion of Designer Services.
From the class library documentation:
“Services are a foundation of the .NET Framework design-time architecture. Services provide design-time objects access to specific features and methods implemented by a service object that provides a service or services.” Let’s look at a few lines of code from the constructor of the TemplateDesignerControl:
_serviceContainer = new ServiceContainer();
_serviceContainer.AddService(typeof(INameCreationService), new DefaultNameCreationService());
_serviceContainer.AddService(typeof(IUIService), new DefaultUIService());
_designerHost = new DefaultDesignerHost(_serviceContainer);
_toolboxSvc.DesignPanel = _viewHostPnl;
ServiceContainer is a framework class that’s already implemented for me. Obviously this instance is going to hold all the Services I need for my designer. INameCreationService and IUIService we’ll come back to in the next article so for now we’ll look at DefaultToolBoxService. DefaultToolBoxService was instantiated in InitializeComponent() because it is also a UserControl as mentioned above. Now might be a good time to start modeling these relationships:
We’ll continue to flesh this diagram out as we move along.
Obviously the declaration for DefaultToolboxService, then, is...
public class DefaultToolBoxService : UserControl, IToolboxService
...because it is both a visual Control and an implementation of an important Service class. In my case, these do not need to vary independently but the implementations could be separate. If you wish, you can review (http://msdn2.microsoft.com/en-us/library/system.drawing.design.itoolboxservice(vs.80).aspx) the IToolBoxService interface before moving on. The implementation is straightforward: Add the types of items you’d like to create to the ListBox and implement the IToolboxService accordingly. The design-time environment, which in our case is also the run-time environment, can query what item is selected and attempt to create an instance of the appropriate class. Adding items is accomplished like so:
protected void LoadToolboxItems()
ToolboxItem labelItem = new ToolboxItem(typeof(CarSpot.TemplateDesigner.Controls.CustomLabel));
labelItem.DisplayName = "Text";
Running the code so far you would see a Toolbox, a Panel with nothing in it, and a PropertyGrid with no selected object. In the next article we’ll start to look at our IDesignerHost implementation and the sequence of events that occur when I want to see a designer view of my “designed document” with a CarSpot.TemplateDesigner.Controls.CustomLabel on it.