Introduction

Microsoft targets .NET as a platform extremely well suited for component development. Behind this assertion is a brand-new architecture that really turns it into much more than a marketing hype. Microsoft shows that it has learned a lot from its previous component-oriented architectures (COM/ActiveX/OLE), and offers the developer of professional components a comprehensive set of features (both at design-time, inside the VS.NET IDE, and at run-time) that make them much easier to develop and at the same time much more powerful and useful.

But what are “software components” after all? Through this article, we will unmask what components really are in the .NET and VS.NET world, and we will specifically discuss the advanced features made available to them through the IDE that allow us to create effective professional components that can greatly boost programmer productivity, increase the separation of concerns and enforce design patterns across the company.

We will take advantage of as many advanced features as possible and build a model-view-controller framework on top of them. In the meantime, we will see some minor drawbacks and hacks generally necessary when developing hi-end componentized architectures. This is a prototype application development model that can be completed and extended easily to build production-quality applications on top.

During the course of this article, we will discuss:

.NET and VS.NET vision of components: the building blocks and how they fit together.

As you can see, pretty much everything is a component in the .NET platform. The main consequence of a class being a component is that the IDE has features that are made available to it. The key property for the IDE to offer services to components is the IComponent.Site. A so-called sited component is one that has been placed in a Container. This containment is general and is not related to visual containment.

For example, an ASP.NET server control, when dropped in a Web Form, is said to be sited. Its Site property (part of the implementation of the IComponent interface), is set to the host where the component lives now, which inside VS.NET is an instance of the Microsoft.VisualStudio.Designer.Host.DesignSite class. Exactly the same object type is assigned as the Site property of a Windows Forms user control when dropped in the forms designer, and to a non-visual component at design-time. There are differences between the last one and the former, which we will discuss when we look at components in the strict sense (non-visual IComponent implementations).

The Site property, of type ISite, contains members that allow the component to communicate with other components, with its container (a logical container) and services provided by it. We will learn the benefits and how to take advantage of this as we move on. So the overall architecture is:

The container is an object that implements the System.ComponentModel.IContainer (IContainer alone from now on) interface. At design-time, the container is always an instance of the Microsoft.VisualStudio.Designer.Host.DesignerHost. This object is the core of VS.NET IDE features for components, so let’s look at it in more depth.

Hosted components

First of all, to look at the DesignerHost class, you will need a tool that uses reflection and that is capable of showing non-public members of an assembly. One such tool is Reflector, and I strongly suggest that if you’re not using it yet, you start familiarizing with it. It’s an invaluable (and free) tool to learn the internals of any .NET assembly. You can download it from here. The class we’re interested in is located in the Microsoft.VisualStudio.dll assembly in the Common7\IDE subfolder of your VS.NET installation (by default C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE). You will have to enable reflector to display non-public members, through its View – Customize menu.

This class implements (among many other interfaces) the IContainer interface we talked about and which is required to be able to contain components, and indirectly the System.ComponentModel.Design.IDesignerHost interface (by implementing the derived Microsoft.VisualStudio.Designer.IDesignerDocument interface). This interface provides the upper-level layer of services, such as creating and destroying components, accessing designers associated with components, etc. A component sited (and therefore contained) inside an object implementing this interface is called a hosted component and that will mean that it can use services provided by it. Currently only the DesignerHost implements it, but other IDEs may choose to do so also.

The host holds most of the services we can use in our components, which are accessible through the component Site property. Currently, the host holds the following services: IDesignerHost, IComponentChangeService, IExtenderProviderService, IContainer, ITypeDescriptorFilterService, IMenuEditorService, ISelectionService, IMenuCommandService, IOleCommandTarget, IHelpService, IReferenceService, IPropertyValueUIService and ManagedPropertiesService. As you can see, there’s a breed of services for our components to use. We will show uses of most of these through the article.

The entry point to get these services is the IServiceProvider interface. Both the DesignSite and the DesignerHost classes indirectly implement this interface:

The interface contains only one method: GetService which receives a Type indicating the service to retrieve. For example:

Currently, the DesignSite (which we access through the component Site property) provides two services itself: IDictionaryService and IExtenderListService. Requests for other services are passed up to the host.

These services provided by the VS.NET host are not only available directly through the component’s Site property but also from associated (through attributes) classes that offer design-time improvements to components. Those other classes complete the architecture.

Design-time architecture

Besides the component/site/container architecture, there’re a number of additional aspects that make up a feature-rich platform to offer improved design-time support for components. This is very important for RAD tools and to increase programmer productivity. Professional components should offer a rich design-time experience to developers if they are to be successful.

These additional features are added to a component through Attributes. Each type of attribute assigns a different design-time (and some of them also run-time) feature to the component. The IDE uses these attributes in two main areas: the design surface and its interaction with the code (behind) file and the property browser. Note that by design surface we mean not only the Windows or Web Forms design surfaces but also the component area below them, which is made visible whenever a non-visual component is dropped on the designer.

Most design-time features are contained in the System.Design.dll assembly. The following picture shows the kind of attributes and their usage at design-time.

We don’t show in this image the more basic attributes like DescriptionAttribute, CategoryAttribute and others, which provide limited features that are mainly used by the property browser. However, there are some other attributes that add more complex and useful characteristics to components that we don’t show either, as they are very specific. We will introduce them as we move to more advanced scenarios.

Each of the three attributes associates another related class with the component. DesignerAttribute associates a class directly or indirectly implementing System.ComponentModel.Design.IDesigner (such as System.ComponentModel.Design.ComponentDesigner, System.Web.UI.Design.HtmlControlDesigner or System.Windows.Forms.Design.ControlDesigner). TypeConverterAttribute does the same for a class derived from System.ComponentModel.TypeConverter, and EditorAttribute for a class derived from System.Drawing.Design.UITypeEditor (yes, it’s a weird namespace location for this one!).

Usually, and erroneously in the author’s opinion, these attributes are classified according to the level of design-time enhancement they provide, in the following categorization:

Basic: those attributes not covered in our image.

Intermediate: TypeConverter and Editor attributes.

Advanced: Designer attribute.

We don’t subscribe to this categorization because many attributes allow us to offer both simple and advanced design-time features. This will be clear by the end of the article.

The three attributes (and the associated classes) provide the following features:

Designer: interacts with the designer host to provide various design features. The common functionality offered by a designer can be found in the System.ComponentModel.Design.ComponentDesigner class, which is usually the base class for any designer. There are members to notify the host about changes in the component and filter the properties the host and the property browser will see, for example.

The more specific features depend on the type of designer, which in turn depends on the type of component the designer is attached to. So, the System.Windows.Forms.Design.ControlDesigner (the base class for all Windows Forms controls) handles hooks between a control and its children, drag and drop operations, mouse events, etc. On the other hand, the System.Web.UI.Design.ControlDesigner (the base class for all Web Forms controls) is responsible for emitting the HTML to use to display a control at design-time, and for performing persistence of the control state to the page source (.aspx file), showing errors, etc.

Usually, designers handle functionality that applies to the whole lifecycle of the component at design-time, and they are the more flexible part of the architecture.

Editor: provides custom user interfaces for editing a component’s properties, and optionally paints a property value in the property browser cell that displays it. It’s accessed through the property browser whenever a property with an associated editor is about to be changed. Examples of editor are the System.Drawing.Design.FontEditor that is displayed when you click the ellipsis next to a property of type System.Drawing.Font, or the System.Drawing.Design.ColorEditor that provides a dialog for color selection.

You may notice that these two are different kinds of editors: the FontEditor appears as a modal dialog (in a Windows Forms control property for example), and the ColorEditor (both in Windows and Web controls) is shown as a dropdown widget (like the AnchorEditor too).

TypeConverter: this is by far the most difficult piece to classify and describe, because several features belong to it. First of all, it provides an extensive set of methods, many of them virtual, to convert an object to and from other types. This conversion is mainly used by the property browser to convert to/from the string representation of the object, which is used to show a property value. But we will see that other conversions may apply, and can even affect code serialization for the component.

It can also provide a list of values for the property, which is displayed in dropdown mode. You may wonder what this has to do with the “Converter” word. I also do. Maybe this feature should have been placed in the Editor…

Finally, we can filter the list of properties that will appear in the property browser. This may be useful when you want to filter the editable properties based on a custom attribute you create, for example (instead of the default BrowsableAttribute). This is an advanced case, but you’ll notice that this feature is also available through the ComponentDesigner (the base class for almost all designers), that allows pre/post filtering of not only properties but also events and attributes. So I also wonder why this feature is here at all…

TypeConverter and Editor attributes can be applied to an individual property or directly to the type. In this last case, any object with a property of that type will automatically have the editor/converter attached.

Some features of the designer are also used by the property browser, such as the DesignerVerbs we will discuss shortly together with examples of each attribute and their usage.

Root Components and Designers

Some classes cause a design surface to be shown where we can drop other components. This is the case for a Web Form (Page class), a Windows Form, or any Component-inherited class (in general). You will notice that this is not the case for your custom classes or ASP.NET custom controls, for example. A combination of two attributes makes it possible for the IDE to offer a design surface for a class:

DesignerCategoryAttribute: the constructor of the attribute must specify the “Component” category.

[System.ComponentModel.DesignerCategory("Component")]

DesignerAttribute: the overload taking the base designer type must be passed, and the designer must implement IRootDesigner.

The IComponent interface implements the last attribute. So every class that implements directly or indirectly this interface will have the default design surface we see when we double click a component class, if it has the appropriate category. The Component class is an example. The case for ASP.NET custom controls is that the base System.Web.UI.Control class specifies a DesignCategory of “Code” and that’s why they’re not “designable” (for now I hope).

When an item is selected for edition in the Solution Explorer, if it has the appropriate category and root designer, the IDE will instantiate the root designer and show the design surface to the user. It will also create an instance of the component and make it the root component of the designer host. This is the relation between objects when a Web Forms page with some child components is opened in design view, for example:

The following pseudo code shows the Page class declaration and the attribute that causes this behavior:

The WebFormsDesigner class is located in the Microsoft.VSDesigner.dll assembly in the same folder as the Microsoft.VisualStudio.dll we mentioned earlier. This designer, among other things, inherits from System.ComponentModel.Design.ComponentDesigner, which is the base class of most designers and is the default implementation of the IDesigner interface.

As we said, the first attribute defines the class that will handle the display of the design surface when the component is the root component. The other designer specifies the behavior the component will offer when it’s placed inside another component, for example, a Web Form.

All the architecture we discussed so far has a primary goal of making a programmer more productive by enhancing the design-time experience. There are far too many features to explore, but they can only be fully realized in the context of a concrete and highly integrated application, instead of isolated examples. For that purpose, and to dig deep into the IDE, we will implement an MVC framework that allows Web and Windows applications to share a common code base and isolate visual programmers/designers from the complexities of their business objects. This framework will be mainly based on non-visual components, but most of the IDE integration features we will implement are equally pertinent for visual controls.

As we move on with the implementation, we will revisit many of the concepts of this first architecture overview, and will put them in context with concrete code. If the MVC design pattern is already familiar to you, feel free to skip the next section. It is not intended to be a comprehensive explanation of the pattern, but just an introduction to let you move forward to the implementation.

MVC: the Model-View-Controller design pattern

Under this pattern, there are three very cleanly separated concerns in an application:

Model: this is the part of the architecture that holds the data about your business entity and its behavior. This is the only one in charge of going to a database, for example, to perform some action.

View: this is the piece that displays (or outputs) the information in the model. Typically represented by a form and its controls.

Controller: all interaction between the view and the model is isolated by the controller. So when the view needs to perform an operation on the model, it asks for it to the controller. When it needs to show data about a model, it asks for the model to the controller.

This separation makes for a loosely-coupled architecture where components can evolve independently, and where changes to one of them don’t impact the others, and maintainability is greatly increased. What’s more, the same code in the model is reused by disparate views. And depending on the way the controller itself is programmed, it can also be reused.

The architecture we will implement will have the following interactions:

This pattern has been implemented and adapted so many times that some purists will surely object that this is not a “true” MVC. In the more traditional concept, the View is in charge of displaying the data in the model, in a pull-fashion. In our implementation, the Controller is actually pushing the data to the View. This will prove more effective in web scenarios, without hindering applicability to desktop applications.

In order for this model to be feasible for both Windows Forms and Web Forms Views, we implement another pattern, the Adapter, which will take care of updating the appropriate UI:

The benefit of this approach is that the same controller is reused across view technologies.

We will begin the implementation and our journey through the IDE features by implementing the core enabling piece of this puzzle: the view mappings between the Controller and the View.

AOP in the .NET era

There are many approaches to mapping two components, one of which is using an XML file with the data, another one may be to place those mappings in a database. However, both of them (and others too) have a couple important drawbacks:

The mapping file/storage becomes another point of maintenance.

The loading/parsing of the mappings becomes an issue with high-load.

There’s a significant departure from the usual drag & drop-control-set-properties-run development style.

One way to avoid these issues would be to extend the built-in controls and implement the mapping configuration in the controls themselves. Besides being a daunting task (just count the number of built-in Web and Windows Forms controls), any change in the mapping feature would require modifications to the control's code, which doesn’t seem like a very good idea. Besides that, we may not be able to inherit third-party acquired controls.

VS.NET supports the notion of an extender, which is a component that extends the feature set of existing components from the outside, without requiring either inheriting, nor containing, or even accessing any internals of the extended component. This extensibility mode is usually called Aspect Oriented Programming, because it allows us to easily add and remove aspects (in this case, the mappings) to existing components from outside. This technique has its own web site and there are may articles on the internet about it. An interesting article on the topic is available at MSDN, although its approach takes the path of custom attributes.

The component-way to AOP are the IExtenderProvider interface and the ProvidePropertyAttribute class in the System.ComponentModel namespace. The attribute must be applied to the component that will extend other components:

What this attribute is saying to VS.NET is that the component (BaseController) will provide a property WebViewMapping to all components of type System.Web.UI.Control in the root component (a Web Form). We can refine the controls for which we provide the extender property in the implementation of the interface unique method:

bool IExtenderProvider.CanExtend(object extendee)
{
returntrue;
}

Here we are saying that we always support Control-inherited objects (the ProvideProperty filter has already passed at this stage). Mappings don’t make sense, however, for the root component, that is, the Page object itself, which also inherits from Control. To verify this condition, we can make use of the IDesignerHost service, which can be accessed from our component directly by calling the GetService method, as we discussed at the beginning:

The Component class implementation of GetService simply forwards the request to the Component.Site property value object, which is, as we saw, the DesignSite, and which contains several other services we will take advantage of as we go.

It’s important to note that both the interface and the attribute have to be implemented in order for this to work. The final piece is a couple of methods with specific naming conventions, which must exist in the extender component:

///<spanclass="code-SummaryComment"><summary></span>/// Gets/Sets the view mapping that is used with the control.
///<spanclass="code-SummaryComment"></summary></span>[Category("ClariuS MVC")]
[Description("Gets/Sets the view mapping that is used with the control.")]
publicstring GetWebViewMapping(object target)
{
//Retrieve a mapping
}
///<spanclass="code-SummaryComment"><summary></span>/// Sets the mapping that applies to this control.
///<spanclass="code-SummaryComment"></summary></span>publicvoid SetWebViewMapping(object target, stringvalue)
{
//Store the mapping
}

The naming convention is: Get/Set + [property name used in ProvidePropertyAttribute]. The GetXX return value must match the value parameter of the SetXX method. If we were implementing only the code shown so far (with the addition of a private field to hold the value) and we dropped a BaseController component on a WebForm, we would see the following new property in the property browser, attached to any control on the page:

Note that the Category and Description attributes applied on the GetWebViewMapping method are used just as if they were applied to a regular property. The Get is always the one that counts for all the usual property-attributes.

As the state for the property is kept outside the control itself, inside our component, the target parameter that both methods receive allows us to determine the object for which the property is being accessed. We will usually keep a hashtable with the configured properties for each control, based on its unique identifier. Furthermore, a single value isn’t usually enough to keep our information, so we can create another class to keep the settings. In our case, it’s the ViewInfo class. This class contains the following properties: ControlID, ControlProperty, Model and ModelProperty. All of them are strings and will be used to synchronize the model values with the configured control. If the property is not a simple type, we can offer improved property browser integration by assigning a specific type converter with the class:

This converter is provided in the System.ComponentModel namespace, and causes the property browser to display the property as follows:

The property can be expanded and configured directly in the property browser. The custom string representation shown next to the property name is simply a matter of overriding the class ToString method:

where ConfiguredViews is a property of type Hashtable which keeps the mappings. Note that the extended property is just like another member for the IDE and its persistence in code. So right now, the IDE will not know how to persist the new WebViewMapping property of the control. It won’t know either how to persist the ConfiguredViews property of the component itself. In the next section, we will discuss how to emit custom code to persist these values. To tell VS.NET it should ignore these properties in the persistence (called serialization) process, we add the following attribute:

Well, this will not work, actually. Even if we can have both, the first extender property used “wins”. That is, if we open a Windows Forms with a controller and define some mappings, Web Forms controls will no longer see the extended property for the entire VS.NET session. We would have to close and reopen VS.NET in order to get the extended property again in Web Forms. But then, if Web Forms “wins”, Windows Forms lose. So we implement the Get/Set for each one:

What we achieved, effectively, is adding functionality to existing controls without actually touching them. Now we need a way to persist these configured values before we move on, because right now, we will lose all values as soon as we close the form.

Custom Code Serialization: the CodeDom power

The main object persistence mechanism in VS.NET is handled through direct code emission. You have already seen this in the InitializeComponent method that all Web and Windows Forms contain. It’s also present in Component-inherited classes. Two attributes determine this behavior: System.ComponentModel.Design.Serialization.RootDesignerSerializerAttribute and System.ComponentModel.Design.Serialization.DesignerSerializerAttribute. Just like the DesignerAttribute we talked about at the beginning, there’s a distinction between the root and the normal serializer. But unlike the DesignerAttribute, the normal (non-root) serializer is always used, and the root serializer is additionally used when the component is at the same time the root component being designed. It’s usual to customize only the non-root serializer. An indicator of this is that the IComponent interface already includes the root serializer:

Note that both have their unique serializer, because the way Windows Forms controls are persisted to code is very different than that of Web Forms controls. The former serializes all values and settings to the InitializeComponent method, while the latter only stores in the code-behind the event handlers' attachments, because control properties are persisted in the aspx page itself.

You have surely noticed that whether the control is used in a VB.NET project or a C# one (or any other .NET language, for that matter), the InitializeComponent always has code emitted in the proper language. This is possible because of a new feature in .NET called the CodeDom. CodeDom is a set of classes that allows us to write object hierarchies representing the more common language constructs, such as type, field and property declarations, event attachments, try..catch..finally blocks, etc. They allow us to build what is called an abstract syntax tree (AST) of the intended target code. It is abstract because it doesn’t represent VB.NET or C# code, but the constructs themselves.

What the serializers hand to the IDE are these ASTs containing the code they wish to persist. The IDE, in turn, creates a System.CodeDom.Compiler.CodeDomProvider-inherited class that matches the current project, such as the Microsoft.CSharp.CSharpCodeProvider or the Microsoft.VisualBasic.VBCodeProvider. This object is finally responsible for transforming the AST in the concrete language code that gets inserted inside the InitializeComponent method.

There’s nothing terribly complicated about CodeDom, but beware that it is extremely verbose, and can take some time to get used to. Let’s have a quick crash-course on CodeDom.

CodeDom syntax

CodeDom is best learned by example, so let’s have a look at some C# code and its equivalent CodeDom statements (we assume they all happen inside a class). The code download contains a project to test for CodeDom in the CodeDomTester folder. It’s a simple console application where you have two skeleton methods, GetMembers and GetStatements where you can put sample CodeDom code and see the results in the output. Let’s see some examples:

C#:

privatestring somefield;

CodeDom:

CodeMemberField field = new CodeMemberField(typeof(string), "somefield");

All class-level member representations, CodeMemberEvent, CodeMemberField, CodeMemberMethod and CodeMemberProperty, all of which inherit from CodeTypeMember, have the private and (where applicable) final attributes by default.

Here we are calling a hypothetical overload of the same method we called before. Note that we first create the method invoke expression. Then we assign its method reference to the one pointing to this and the method name. Next we append the two parameters, the second being a reference to the field as we did above for the assignment.

If you want (and you will, I guarantee that) to avoid endless (and useless) variable declarations, you can compose the statements without resorting to temporary variables. This makes the code less legible, but much more compact. A good technique for creating those (rather lengthy) statements is to think about the target code to generate from inside out. For example, in the code above:

this.GetValue("Someparameter", this.somefield);

We should start by the parameters, then think about the method reference, and once we have that in mind, write something like this:

It looks pretty bad, but again, analyze it from inside-out: the last thing we see is a this.somefield. Next, the primitive expression. That is passed as the initialization expression for the array of parameters to the method call. Then you have the this.GetValue and finally that makes the actual invoke.

Note that proper indenting can greatly help, but you have to do it mostly by yourself, especially with several nesting levels. The most important nesting in order to achieve some legibility is the array initialization, as shown above. It’s also recommended that you put the intended C# (or VB.NET) output code just above the big multi-line statement, so that anyone can know what you’re trying to emit (maybe even yourself after a week!).

There are classes to define all the cross-language features. But let’s look at the concrete persistence code we need for our extended property.

Emitting CodeDom

Like we said, we will need to associate a custom serializer with our BaseController in order to customize persistence and emit the code to preserve the view mappings and potentially any code we need.

The Serialize method is called whenever our object needs to be persisted, for example when a property changes. The return value must be an object of type CodeStatementCollection containing the code to persist. Likewise, the codeObject parameter in the Deserialize method contains the statements that have previously been emitted.

A little insight on this process and how the IDE works is helpful here. We said in the introductory section that VS.NET instantiates the root component in order to “design” it through the root designer. This process actually happens for all the components (and controls) in the root component. What’s actually happening is that the IDE executes most of the code in InitializeComponent in order to recreate the objects just as they will be at run-time. We say most and not all because only the statements modifying the component in question are called: i.e., property sets and method calls in them. We are given an opportunity to interact in this design-time recreation process by customizing the Deserialize method. Usually this is not necessary, so most of the time we will just be passing the ball to the original component serializer, the ComponentCodeDomSerializer, which basically “executes” the code. In order to get the serializer for a type, we use the GetSerializer method of the IDesignerSerializationManager parameter we receive. This object has other useful methods we will use later.

Retrieving the component original serializer is a common usage of the manager, and as the deserialization is usually (and for our implementation, always) the same, we will put that in a base class from which we will inherit our controller serializer:

We will add other common methods to this class later on. Now we have to go to the serialization process. We need to iterate through all the DictionaryEntry elements of the Hashtable and emit code like the following in order to persist our ConfiguredViews property:

Another common practice is to let the original component serializer to do its work and then add our custom statements. This way we avoid having to persist the common component properties ourselves. So our serializer starts by doing just that:

It’s important to note that our serializer will be called even when the root component is the BaseController itself. In this case, we don’t want to persist our custom code, as it applies basically when it’s used inside another component, such as a Page or a Form. To take this into account, we ask for the IDesignerHost service we used before and check against its RootComponent property. The IDesignerSerializationManager implements IServiceProvider, so we have the usual GetService method to do that:

The base CodeDomSerializer class has a number of useful methods to work with while serializing/deserializing code. When we access the ConfiguredViews property, we do so through a reference to the actual controller being processed (the value parameter). One helper method in the base class creates the appropriate CodeDom object to use this reference in our so-called CodeDom graph:

Note that we use other manager methods to format the error message, GetInstance and GetName, which allow us to retrieve object references and their names respectively. By using continue after an error is found, we are avoiding serialization of invalid settings, effectively removing them. The component user will see something like the following when there are invalid values:

Back to simplicity

Having discussed the power and flexibility of custom CodeDom persistence, it’s true it can be a daunting task to serialize several members using this technique. Luckily, VS.NET supports a simpler way of serializing custom types to the code-behind file.

The process simply requires that we implement a type converter for our class that can convert the object to an InstanceDescriptor type. This is done just like we did before for string conversions:

The InstanceDescriptor constructor receives as the first parameter, the MemberInfo of the method or constructor to call in order to create an instance of our custom class. We use reflection to retrieve a constructor that matches the signature of the parameter types we specify. The second parameter passes the actual arguments to the call, and finally, the third parameter specifies whether the object instantiation is complete with this method call. If we say it’s not, each property will be serialized in turn.

This mechanism works automatically if the property to be serialized is an array of ViewInfo objects, where we would get the following automatically emitted code:

In our case, this doesn’t meet our needs, but there are many occasions where this kind of serialization is enough.

Finally, another way (rather unadvisable in the author’s opinion) is implementing ISerializable in your class. This way, VS.NET will serialize the object directly to a resource file. This makes it much more difficult to see what’s going on behind the serialization process, which can be good or bad, depending on the point of view.

Now that we know the basics of property extenders and custom code serialization, let’s complete the MVC picture.

Completing the MVC Component Model

In our model, controllers contain models. Views access the models through the controller. The models are also components that are drag & dropped inside a controller designer. We know that the default designer allows any kind of component to be dropped in the surface. That’s now what we want. We want only models to be used in our controller, and we also want to avoid the models to be dropped on a container other than a controller.

The way to achieve this level of control is to use two related IDE features. One is attaching a custom RootDesigner to the base controller class from which all custom controllers will inherit:

The ToolboxItemFilterType.Require value tells the IDE to only enable the item in the ToolBox when both the current root designer and the component have a matching string (the first attribute parameter). There’s an interesting article with the different combinations on this attribute values and the effect on the toolbox items here.

The BaseModel class provides a virtual property that allows inheritors to assign a name to the model, which will be used to reference it by the view mappings. As a final step, we can prevent these two base classes to appear in the toolbox by adding the following attribute to them:

[ToolboxItem(false)]

Note that this attribute is inheritable, so derived classes must have a ToolboxItem(true) attribute if they want to be available on the toolbox. Usual OO programming advices to make base classes that are not intended to be used by developers abstract. We can’t do that with our components, though, because currently the IDE needs to be able to instantiate them in order to work.

We can now create a PublisherController and a PublisherModel inheriting from these two classes, compile everything and add them to the toolbox. With the PublisherController class in design mode, we can see the effect of the filter, which causes the PublisherController component to be disabled. The contrary happens in a form (Windows or Web), where it will be the only one enabled:

Before the model is complete, however, we must take into account some details regarding the way VS.NET builds the component hierarchies as we drag & drop components in designers, especially when components inherit other components.

.NET Component Model: a closer look and inheritance problems

When we add a new component to a project, we get some automatically generated code from the basic template. If we drop another custom component into it, the component code will look something like the following (comments aside):

The important thing to notice here is the privatecomponents variable. It is of type Container and it is passed to the Component2 constructor. We know that in turn, this constructor will do just what the Component1 constructor does: adds itself to the container:

This component, in turn, contains its own components field, which it will pass to any other component we drag into its design surface, which will in turn add itself to it and so on. The Container class implementation assigns the incoming component Site property (in the Add method) a newly created ISite that points to the container as well as the component. This Site, of type Container.Site, can later be used by the component to know who contains it. Recall that at design-time, this Site will be pointed to the DesignSite as explained in the introduction.

This mechanism works fine for containment, but when components inherit from other components, we end-up with a broken chain of containments, because the base class will have its own components field. So we will have a hierarchy similar to this:

This hierarchy is created this way because the model dropped in the PublisherControler is passed the “wrong” container. The consequence is that any code we place in the BaseController class will not be able to access the contained components. The solution would be to pass to the model constructor the base class components field. This, however, would involve making this field accessible to the inheritors (protected) which in some way violates the OO principle of encapsulation. A better solution is to implement IContainer in the base class itself, and pass a this reference to the model constructor. The interface implementation in the base class will provide proper access to the components field:

We can safely remove the base class constructor that takes the container, as the inheriting classes such as PublisherController will add themselves to it, which is the default behavior and doesn’t conflict with inheritance:

We can safely pass the CodeThisReferenceExpression because the base class of the containing component implements IContainer, as expected by the constructor overload.

Up to now, except for the ExpandableObjectConverter and the extended property, we didn’t offer any deep integration with the IDE. Let’s see how to enhance the design-time experience.

Deep IDE Integration

There are several aspects where our implementation so far is very weak:

Changes made to mapping values inside the expanded property in the property browser are not always persisted immediately (sometimes they’re not persisted at all).

When a visual component is renamed, we lose all view mappings, because they are stored in the ConfiguredViews table based on its name. Likewise, when it’s removed the mappings are not accordingly removed from that table.

Some properties shouldn’t appear in the property browser (even in Intellisense), such as the controller Components, or ConfiguredViews when the controller is the root component.

A controller may need to access a DB connection. It would be nice to support web.config-bindable properties (or app.config for EXEs).

Typing the control and model properties name, as well as the model name itself is error prone.

Setting view mappings control by control can be annoying if many controls are involved.

There’s no way to check all applied mappings at once.

The first issue is a direct consequence of the fact that the IDE has no way to know that the ViewInfo object belongs to the controller component, and that it should be serialized again as soon as there are changes to it. One way to do this would be to modify all property setters so that they raise some kind of event that can be trapped from the containing controller. Although this is the more evident solution, it requires writing code to notify changes to all property setters, and .NET provides us a more elegant approach.

Inside the IDE, all property changes to components are performed through so-called descriptors, which are instances of the System.ComponentModel.PropertyDescriptor class which describe a property. This is true especially for changes applied through the property browser. In fact, many controls and the Windows Forms binding mechanism also use this approach. You can check that by dumping the System.Windows.Forms.dll IL code assembly with the ILDasm tool and performing a search for PropertyDescriptor::SetValue.

This descriptor has a method, AddValueChanged, which allows us to add a handler that will be called whenever the property is changed. This is exactly what we need in order to trigger the serialization process. We will also add an IsHooked flag to the ViewInfo class, so that we know if we have already attached our handler:

We use the TypeDescriptor class to get the collection of PropertyDescriptor objects that apply to our object. This class has many static methods to handle components that are worth a look. Look at the MSDN help for a list of members and their usage.

The RaiseViewInfoChanged handler notifies the host that a change occurred to the mappings by using another service from the IDE, the IComponentChangeService:

By using this service, the IDE will take appropriate measures to persist our component again.

The second problem, tracking naming and removal changes of controls that have already been mapped, is achieved using the same service, but in this case, it’s a sort of global tracking, which can’t be done as we did above. The way to do this is to create a designer for our component to listen to changes. A designer allows us to add more design-time features to our component, as we will see shortly. This designer, just like the RootDesigner, is associated through an attribute:

Our ControllerDesigner class inherits from ComponentDesigner, which is the default designer associated with the IComponent interface. A designer has greater control and interaction with the IDE, and it exists for the whole time the root component (i.e. a Form or Page) is being designed, and is permanently attached to the component it’s “designing”, in our case, the concrete controller. The critical point in a designer, where we can hook into the IDE services, is the Initialize method, where we receive the component to be designed:

As stated in the code comment, the first thing we must do inside the Initialize method is call the base class implementation. This allows the designer to be properly hooked with the IDE. The designer contains a Component property which is set to the incoming IComponent parameter of this function. After processing, we request for the service using the usual GetService method, which now is implemented directly in the base ComponentDesigner class. This implementation just passes the call to the designer’s Component.Site property, being just a shortcut.

With the service at hand, we can attach to the two events we are interested in: ComponentRemoving and ComponentRename. Let’s look at the rename handler first:

We simply remove the mapping and add it again using the new component name as the key. Finally, we have to notify the environment to trigger the code generation again. This is achieved through a method of the base ComponentDesigner class, RaiseComponentChanging. The removing handler is slightly different:

Here we use another service, the IReferenceService. It allows us to retrieve component names, a component given its name (the opposite) and the set of components of a certain type that exists inside the host. We use it to know the name of the component being removed and we remove the mapping accordingly, notifying the environment as we did before.

The next problem, hiding members from the property browser, depends on the scenario. Hiding a public property usually involves setting an attribute:

<Browsable(false)>
public ComponentCollection Components

This will simply hide this BaseController member from the property browser. Sometimes we will want to hide a member not only from the property browser but also from Intellisense, such as for members that are made public in order to be accessible by the generated code, but which we want to hide from users. An example of this are the Get/Set methods that act as the property provider implementation. The class user shouldn’t see them at all, as they are relevant only for the IDE and its AOP model. We can do that using another attribute:

The case is not so easy to selectively hide a property depending on some external factor. We are talking about hiding the ConfiguredViews only when the controller is the root component. We can take advantage of the fact that we created a root designer for it, and take advantage of that to filter the list of properties:

There are many overridable members in the base ComponentDesigner, from which ComponentDocumentDesigner inherits. There is a set of Pre/Post filter methods for properties, attributes and events. We are pre-filtering the property and checking against the IDesignerHost.RootComponent to determine the property removal. It’s worth pointing again that the GetService implementation forwards the request to the current component Site property as usual.

The IDE provides us with a built-in mechanism for persisting property values in an application configuration file. This feature is available through the property browser, under the DynamicProperties category. The dialog that appears makes it possible to “bind” a component property to a key in the appSettings section of the application configuration file. We implemented an IConnectable interface with a single property, ConnectionString, which controllers as well as models can use to signal they need a connection of some kind.

It simply propagates the setting to any contained “connectable” component. Every public read/write property (if its type is a built-in one) in a component will appear in the dialog that opens when we click the ellipses next to the Advanced item under DynamicProperties. However, by applying the RecommendedAsConfigurable attribute, it will also appear directly below the Advanced item, as follows:

Note that the property has been bound to the config file as shown by the little green icon next to the ConnectionString property. If we first set the property value, and they define the key under DynamicProperties, the appropriate entry is added in the application configuration file:

To solve the last three issues (providing list of values for the mapping properties, creating a UI to set all mappings in a single place and verifying configured mappings), we will take advantage of the serviced architecture of the IDE and extend it with our own service.

Extending the Serviced Architecture

We have been using several services from the IDE. Most of them are provided by the designer host. If we need to access a feature from several places, it doesn’t make sense to duplicate the same code. Implementing them as static members of some class may work, but we can achieve the same effect by playing according to the VS.NET rules: create a custom design-time service.

The IDesignerHost interface provides us with a way to attach and remove our services, the AddService and RemoveService methods. The VS.NET way to attach services is to define an interface, and add the concrete instance through the host methods. We will need the following methods in our service implementation:

publicinterface IControllerService
{
///<spanclass="code-SummaryComment"><summary></span>/// Verifies that mappings for the controller are correct.
///<spanclass="code-SummaryComment"></summary></span>///<spanclass="code-SummaryComment"><paramname="controller">Controller to check.</param></span>void VerifyMappings(BaseController controller);
///<spanclass="code-SummaryComment"><summary></span>/// Lists all the properties of the control.
///<spanclass="code-SummaryComment"></summary></span>string[] GetControlProperties(object control);
///<spanclass="code-SummaryComment"><summary></span>/// Lists all the properties of the model.
///<spanclass="code-SummaryComment"></summary></span>string[] GetModelProperties(BaseModel model);
}

Attaching the service can be done in several places, but doing so as soon as the component becomes “designable” is usual. This happens when the Initialize method is called in a component designer, where we receive the component being designed. Therefore, we will add the following code to our ControllerDesigner:

We first check if the service has already been added by trying to retrieve it. Finally, we add the service passing the type and the service instance. We also have the possibility to pass a delegate that performs the actual object instantiation, in case we want to do that on-demand. This is useful if the service is costly to create. In our case, the service instance is created immediately and it receives a reference to the IDesignerHost, so that we can access other IDE services from within our implementation:

The behavior of the GetControlProperties method is exactly the same as the property browser itself. We use the BrowsableAttribute to filter the list of property names to return. We do the same for the model properties, but with the BindableAttribute, which is not strictly necessary, but allows us to provide a well-known approach to hiding properties. We could have used any attribute we wanted, but using the built-in ones makes for an easier understanding by our component users. For example, the BaseModel.ModelName property shouldn’t be available for the controller component user, so we can apply the attribute and it will stop appearing wherever we show the available properties:

Note that at the same time, we apply the BrowsableAttribute to hide them from the property browser, which is logical since these properties only make sense at run-time, and certainly not while we are designing the model component itself or the controller containing it.

Side-note: component properties serialization

We found that sometimes, using the DesignerSerializationVisibilityAttribute causes the IDE to behave strangely. Specifically, we found that the UndoManager, a type of IDesignerSerializationManager that controls the undo/redo feature, loses its ability to serialize the component for undo operations, throwing from time to time an “Unknown Undo/Redo exception”. When this happens, whenever we type code in the source we lose all syntax coloring and Intellisense features.

There’s another way to tell the IDE that a property shouldn’t be serialized, through a method (it can be private) with the name ShouldSerializeXX, which returns a boolean indicating the serialization support. The XX must be replaced with the property name. For the two properties above, we can add the following methods to achieve the same effect as applying the DesignerSerializationVisibilityAttribute:

Now we can get back to the controller service. To provide lists of values in the property browser, for the view mappings, we have to use a TypeConverter. A class of this type provides a way to interact with the property browser, in several ways. The converter determines which conversions are supported (to and from other types), as well as what values are valid, also called standard values.

In this implementation, we state that we support conversions to and from the string type. We can provide custom conversions, just like the Width property does for Web controls. You may have noticed that the property is of type Unit, so that a value is composed by a numeric value and a UnitType. But we can type directly in the property browser a value of “2px” for example, and it will be successfully converted to an object of type Unit, with the numeric part and the appropriate UnitType. This is done in its associated converter, which can split the string and create the unit object with the parts.

This base class, will be used to provide an exclusive list of values in the property browser (a dropdown). All the code so far is common to all converters providing lists of values, that’s why we use this base class. The methods that tell the property browser that we intend to provide lists of values and that they are exclusive are the following:

The ITypeDescriptorContext parameter provides important information about the context in which the conversion is taking place. It provides access to the instance being changed (the ViewInfo in this case), the current container (the DesignerHost) and the property being changed as a PropertyDescriptor. It also implements IServiceProvider, so we can also ask for services just like we do from inside the designer or the component.

The list of values is provided by the controller service we added earlier to the design host and we have to return a StandardValuesCollection object that can be constructed out of a string array with the values. Notice that for some (strange) reason, this class will not appear in Intellisense, even when it doesn’t have the EditorBrowsable attribute applied.

Now we only have to associate the converter with the corresponding property in the ViewInfo class and that’s it:

The effect of this association is immediately visible in the property browser:

But what if we want to check if the received ViewInfo.ControlID actually points to an object in the containing component (Windows or Web Form)? It’s only natural to think we can simply pull the IReferenceService out of the context (an IServiceProvider) and use its GetReference method. This won’t work, however, because edition is happening inside the property browser, and it’s the one answering for the GetService requests, and unfortunately, it will return null. There’s an easy workaround, though, which involves getting the IDesignerHost service first, and asking for the desired service to it instead:

The ModelPropertyConverter has another issue. We need a way to know the controller the current ViewInfo is linked to in order to get a reference to the model instance given its name. Without this reference, it’s impossible to reflect it and show its properties. To solve this, we added a Controllerinternal property to the ViewInfo, which is set to point to the containing controller just before leaving the GetXxxViewMapping extender property getter. We will get back to that in a moment, but let’s look at the code for the remaining converters first:

They are associated with their corresponding ViewInfo properties through the TypeConverter as we did before.

So far, taking the property enumeration code out of the converters themselves and putting them in the IControllerService doesn’t make much sense. After all, it’s the only place we use it so far. However, the next feature we will add, a central place to edit all mappings in the form, will make this design decision relevant.

Custom Editors

The property browser is usually enough for setting small amounts of property values. However, configuring a complex component can be really difficult through the simple property browser interface. Think about the way the Windows Forms font editor dialog simplifies selecting several font-related properties. Or the really useful Style Builder available for CSS files and HTML style attributes:

The good news is that we can have such an editor ourselves. It’s basically a custom Windows form we build just like any other form. Inside the form we can configure a component by setting several properties on behalf of the user, based on the form settings selected. We created the following form to edit all mappings applied to a controller:

It’s far from the style builder, but it’s what we need for our purposes. We just fill the listbox on the left with the ViewInfo instances we find in the controller being configured, and fill the comboboxes with the relevant values, positioning the selected item to match that of the selected ViewInfo. The question now is how to start editing with this form. One way is by attaching a custom type Editor to the property, in our case, the controller ConfiguredViews:

In our case, we’re going to show a full-blown modal Windows form. The other possibility we have is UITypeEditorEditStyle.DropDown, in which case we are able to show an in-place Windows Forms user control, just like the Windows Forms Anchor and Dock property editors do, and also the Web Forms editor for BackColor and ForeColor control properties.

With what we have so far, we will get the ellipsis drawn next to the controller ConfiguredViews, but nothing will happen yet when clicked:

The property knows it has to show the ellipses because of the value we returned in the GetEditStyle override. In order so show the form and begin editing, we must override the EditValue method:

Even though we can simply create the new form instance and call its ShowDialog method, the proper way to do this inside the property browser is through the IWindowsFormsEditorService, a service provided by the property browser itself.

if (srv != null && host != null)
{
BaseController controller = (BaseController) context.Instance;
//Get the designer so we can pass it to the form.
ViewMappingsEditorForm form = new ViewMappingsEditorForm(
host, (BaseController) context.Instance);

We will see the form code in a minute, but note that we pass a reference to the designer host to it. We do so because the form, unlike the type converters we used so far, has no knowledge of the environment, and if it needs to ask for some service from the IDE, it doesn’t have a context whatsoever to ask it for. So it will keep a reference to the host we pass to the constructor for that purpose.

Note that after showing the form, we use the PropertyDescriptor to set the new value on the controller. This notifies the IDE of the change and triggers the code generation process again. The form provides us with a property with the new settings the user chose through the form (ConfiguredMappings).

We won’t show all the form code, as it’s mostly Windows Forms (boring) UI interaction code. But there are some important points we will highlight.

The first is that whenever we need to work with an instance of an object where we only have its ID, we should go through the IReferenceService as we did before. Besides retrieving instances from the GetInstance method, this service also allows us to avoid some “inconsistencies” in the way Windows and Web Forms name their controls. The former uses the control’s Name property, while the latter uses the control’s ID. We can avoid hard coding this difference by using that service’s GetName method. We do that in the InitControls method where we load the available controls in the appropriate dropdown:

Another important issue is related to threading. If form initialization is costly, you may be tempted to span a new thread to perform it. We could use the following code to invoke the InitControls method in another thread:

ThreadPool.QueueUserWorkItem(new WaitCallback(InitControls));

However, if your initialization code has to ask for a service through the host, this will not work. An exception will be thrown. That’s because services are expected to be pulled from the main application (VS.NET) thread. If you don’t need to retrieve services, or if you did so from inside the Load event handler, for example, then there’s no problem.

Depending on the concrete application and service our components provide, we may use qualified type names that we use to instantiate the concrete objects. For example, your application may allow the user to define through a dropdown the type that will handle a certain request or application feature. You may even get the list from a database. The usual way to load a type and reflect it, to show its members in a form for example, is through Type.GetType:

This will only work at design-time for assemblies in the GAC. That’s because the IDE is not running from the place where the project is stored. So, there’s no way for the process running the IDE to locate the assemblies, even if they are referenced by the project. There is another service that provides us with the type loading feature, the ITypeResolutionService. This service provides a GetType method that will correctly locate an assembly by taking into account the assemblies referenced in the current project.

In this editor, we can take advantage of the IControllerService to fill the dropdown with values:

Here we can immediately appreciate the benefit of having globally available services to avoid code duplication mainly with type converters:

Custom and Global Commands

The pending issue is how to provide an easy way to check all configured mappings in a single step. The IControllerService has a method, VerifyMappings for that purpose, and its implementation is as follows:

Note how the code becomes extremely simple when the common features are moved to first-class IDE services. With the verb in place, we will see the modified context menu:

Another usual feature is to add to this context menu the most used editors for the component. For example, we could add an Edit mappings item to the menu. This may seem like a no-brainer, but there are subtleties too. The first step is to add another verb, that’s the easy part:

Note that we are asking for the service to the host directly (because of our implementation of ITypeDescriptorContext). Anyway, the srv variable will always be null. Why? The answer is that this service is not provided by the IDE itself, but by the property grid, more precisely, the internal class System.Windows.Forms.PropertyGridInternal.PropertyGridView, which responds to requests for this service by returning itself (because it implements the service interface). Looking at the IL code for the ShowDialog implementation in this class reveals some complexity in the process, which involves calculating the dialog position, setting focus and passing the call to another service, IUIService. That’s why we didn’t directly instantiate a form and called ShowDialog in the first place in the editor!

We won’t duplicate all this code, so we will instantiate the form directly instead, as there’s no restriction to doing so from within a designer verb:

The code is almost the same that is used in the editor itself, except for the call to the IWindowsFormsEditorService.

If the form developer needs to use several components at once, checking the settings one by one can be tedious. We can use another IDE service to add so-called global commands. It’s the IMenuCommandService. As checking the mappings is a task being performed in the controller service, it seems logical to put this new behavior inside it. Recall that our service is instantiated by the controller designer in the Initialize method call. In the service constructor, we can hook our global command:

We can only add designer verbs, which inherit from the MenuCommand class received by the AddCommand method. This new context menu item is shown always every time the user clicks anywhere in the components area of the form:

Note that the controller is not selected. We clicked next to it, in the components surface. The handler now iterates all components checking one by one in turn:

It simply passes the ball to the VerifyOne method for each controller found. The IDesignerHost.Container.Components property contains all the references that currently exist.

If only it were that easy… Unfortunately, if we now right-click on the controller, we will sadly see that the global command has replaced the first controller designer verb:

The Verify this controller … item has disappeared! This strange behavior happens because the IDE first asks for the component verbs, and then (rather inexplicably) puts all the global verbs on top. The only workaround I found is adding a sort of “placeholder” verb in the controller designer:

All the designer verbs are displayed at the bottom of the property browser too, allowing easy access to them. But unfortunately, the new empty verb we added appears there! Note the first colon at the beginning :(

For now, we either have to live with that, or drop global commands.

Now we have all the IDE integration plumbing in place, we get to the point where we have to actually reflect the model data in the mapped UI widgets. As we stated above when analyzing the framework architecture, the controller delegates the responsibility of setting/getting values in the UI to adapter classes which know how to handle Web and Windows forms.

Dealing with different View technologies

We know that we will support Windows and Web Forms. We will do so by defining the common members we will need from both technologies in a common base abstract class, and let each “adapter” concrete implementation to perform the necessary actions for the particular technology.

There are fundamental differences in the way Web and Windows Forms access and set values in their child controls. We already mentioned naming differences also. These details will be isolated in a new service we will provide, the IAdapterService. Its interface contains the basic method we need:

Two classes implement this interface, WebFormsAdapterService and WindowsFormsAdapterService. Both are pretty similar, so we will look at the former. Both adapters receive in the constructor the parameters they will use to resolve the other methods:

The Windows Forms version is similar, but casts the controlsContainer parameter to a Form. The first four methods are pretty simple. You can look at the downloaded code for their implementation. The interesting bits are the RefreshView and RefreshModels methods. The former causes an iteration on all the configured mappings in the received controller, where the appropriate control properties are set to the values found in the model. The opposite happens in the latter method.

We simply use reflection and load types and properties and set values. The reverse process (RefreshModels) is identical but instead of calling SetValue on the control property we do so in the model property.

We already know to hook a new service into the architecture, so let’s have a brief look at the added code in the ControllerDesigner.SetupServices method that performs the task:

You may have noticed that we pass false as the last parameter when we add any service to the host. That parameter specifies whether we want to promote the service to higher layers in the IDE architecture. It’s not advisable to do so, unless you’re absolutely sure that the service is unique and never needs to be changed dynamically. In our case, we are switching the service object depending on the root component type, so we need it to remain in the IDesignerHost and be easily replaced. By passing false to the third parameter, we indicate that the service only lasts for the life of the current root designer.

We can now take advantage of this service when we access the mappings in the controller extender properties (we removed attributes for better readability):

Note that these methods are really simple now because the adapter is taking care of the interaction with the view. This is slightly different than the model proposed by the original MVC pattern, where each view technology would have its own controller.

Before we move on with the view synchronization, however, we need to know how the view asks the controller to load a certain model.

Model Behavior, the MVC way

As the view is not allowed to execute actions directly on the model, isolation is preserved. As a consequence, the controller exposes methods that cause the actual model method execution. The model contains behavior as well as data, and it uses that data either to hold results from method executions or as input for some actions.

A simple PublisherModel component may expose, besides the properties that map to the publishers table fields in the sample Pubs database, the three basic operations on an entity: Load, Save and Delete:

Note that there is no “platform” specific code. Not a single line. But how and when does the controller update the UI? There are fundamental differences in this timing in the Web and Windows environments. In the former, the UI is loaded typically on the Load event, as post-backs cause new Load events to be fired. A Windows Form, however, needs to refresh the view at times other than the Load, because further actions don’t cause new Load events. To provide this connectivity between the containing form, its events and the controller actions (namely, RefreshView and RefreshModels), we use connectors.

Connecting the Views

The responsibility of the connector is to call the controller methods to synchronize the models with the view at the appropriate time. Basically, the UI container (a Web or a Windows Form) should instantiate the appropriate connector and call Connect passing the relevant parameter to perform the wiring. We provide a base class as usual from which both connectors will inherit:

In order to make the process automatic, we will emit the concrete connection code from inside the controller code generation, after detecting the hosting technology. This is the relevant code from the ControllerCodeDomSerializer method:

Note that the only difference is the concrete connector being instantiated. An important thing to notice here is that this method will never be called at design-time. The IDE only loads properties on components, but it will not call methods.

Note that this will hook the controller to the page events, causing them to be called at the appropriate times.

Let’s wrap up the interactions that will happen at the Load event:

The critical point here is the Site.GetService() method call, which must give the controller an instance of the appropriate adapter to use. We have already tested that the service is right there when attached at design time. But the designer is only called at design-time, so what happens at run-time?

Back at the beginning, when we introduced this .NET Component-oriented architecture, we said there were differences between UI controls and non-visual components. We are now in a position to explain what this difference is. At design-time, both kinds of components are sited in the DesignSite and contained in the DesignHost, as shown back then. But at run-time, components get passed the this.components field as the container and they become sited on this container, simply a class of type Container. The site, accordingly, is of type Container.Site. The problem is that the GetService method, implemented internally in the Container class itself, only returns services of type IContainer, which it itself provides. All other services are gone. Worse, visual controls don’t have a Site at all, and that’s the difference.

What we can do is perform some run-time initialization and provide a custom site that can answer requests for our IAdapterService. Remember that the service instance must always be kept somewhere, just as it’s kept by VS.NET at design-time. We will create a RuntimeSite class implementing ISite that we can use to “site” the components with our own infrastructure:

Our constructor, besides receiving the current container and component, receives a delegate. We said that the service instance must be kept somewhere during runtime. This callback is provided so that when a component asks for a service, the call is passed to this delegate, which can be implemented as it fits in the technology in use. For example, the web version can use the HttpContext to keep the object, something a Windows Forms version can’t do. The delegate gives both a chance to answer the GetService request any way they want. The delegate class and its arguments are:

Simply enough, the web version of the connector first stores the service in the HttpContext, and later lets the service itself answer to components asking for the service. Now the controller, in its RefreshModels method to be called at Page.Load time, will successfully retrieve the service:

The web version of the framework is now complete. When the page is loaded, the model is updated with whatever values existing on the form as posted (or initially loaded) by the user. Just before rendering to the client, and generally after all the event handlers for button clicks, textbox changes, etc. have been called, the controller refreshes the view with what has ended in the model. A Web Form showing publisher data and with the correct mappings, can provide load/save/delete features with the single line of code that we saw in each button handler:

Now, suppose we enter an ID in the respective field and click Load, this is the sequence of actions until the page comes back to us:

On Load, the adapter service is hooked and components are sited.

Model is refreshed, so the value in txtID is placed in the PublisherModel.ID property.

The event handler calls the controller method, LoadPublisher().

The model goes to the database and loads itself with the returned row.

Before rendering, control values are updated with the new values in the model (the complete publisher data).

The page is rendered with the values and sent to the browser.

And the page designer only had to configure the mappings using our IDE integration features, and call the appropriate controller method when it was appropriate!

What’s more, if later we decide to move (or at the same time) to a Windows Forms client, the same controller and mappings will do the work. And even the event handlers for the UI events will look the same!

As Windows Forms allows statefull applications, we can simply keep the relevant variables inside the connector itself. However, in the web, at initialization time, we know all controls have been created. This is not the case for Windows Forms, where controls are just class-level variables that must be initialized in the InitializeComponent method just like our controller. Therefore, there’s no guarantee that our connector will be called after the controls have been initialized.

And why do we need to access the controls at initialization time? Because unlike the web where we have a single point of model refreshes, the Load event, in Windows Forms, we have to refresh the models as soon as a control is modified, and we have to attach to that event and trigger the model refresh.

As you can see, each connector (one for each controller) will have the service and the corresponding connector. The OnActivated handler takes care of hooking the RefreshModels method to each control Leave event:

The other trick we did at the end is hook the ModelChanged event fired from the controller to the same controller RefreshView method. This way we achieve automatic UI updates without writing a single line of code.

Finally, the delegate that handles GetService requests (and which we passed to the new RuntimeSite for each component), simply returns the reference it keeps to the service:

Persisting at design-time through code certainly has an edge over run-time detection and emission: it’s compiled and will always be faster. For example, we may want to emit some attributes in the rendered Web Forms controls that we could use in client-side JavaScript to know the details of the mappings. Again, there’s a natural way that seems obvious: attach to PreRender and iterate configured mappings again, adding the relevant attributes to the control.

If the mappings and the controls are known at design-time, why do we have to waste precious run-time processing doing this iteration over and over on each page interaction? The answer lies in a much better way that we can take advantage of. It’s called an IDesignerSerializationProvider.

An object implementing this interface can be registered with the IDesignerSerializationManager (which we receive in our controller serializer).

We first check that the provider hasn’t been added already. At serialization time, the manager will call each configured provider and give it a chance to provide a custom serializer for a type. This is done in the GetSerializer method:

If we find a control we can attach attributes to (implementing IAttributeAccessor), we return our new serializer. From now on, every time the IDE needs to serialize a Web control (either HtmlControl or WebControl), our serializer will be called, receiving the control being serialized.

In order to avoid disturbing the normal serialization process, we will retrieve the regular serializer for the received control first, and get the code statements from it. We defined a helper method in the base serializer to get the appropriate type:

Here we see in action the ITypeResolutionService. After we retrieve the relevant attribute, all we have is the qualified type name, and we must load it through that service to guarantee the assembly it lives in is located and successfully loaded. After that, we simply use the Activator.CreateInstance method to create the serializer and return it.

Let’s go back to the WebControlSerializationProvider.Serialize method:

As a side node, we have to point that the ViewInfo object contains a Controller property that points to the controller that created it. That’s how we emit its name to the page. This is a specific implementation detail of ours, but what if that property didn’t exist? How would we retrieve the real property provider backing up a “fake” property on a control?

The answer is not easy, unfortunately. When we ask the TypeDescriptor for the property, we expect a PropertyDescriptor. However, when the property is extended, in a class derived from it, ExtendedPropertyDescriptor is returned instead. This object has a property, Provider that has a reference to the object that is providing this property. Great! What else could we ask for? Well, we could ask for the class not to be private!

So, now that we’re out of luck, we have to find a workaround, as usual. The trick comes from reflection. Beware that the technique we’re about to use is highly risky. If MS decides to change the class name or the property name, or even remove it, your now-working code will stop doing so. And they wouldn’t be doing anything wrong, as the interface was private. Now that you are aware, let’s get to it!

To achieve this, we have to first load the type. Through the Assembly.GetType method we can’t reach a private type. We have to use GetTypes and iterate all of them and find the matching one. We created a utility class, DesignUtils with a LoadPrivateType that does the trick. Its usage is simple:

Now that we have the private reflected type, we can get the property as usual with:

PropertyInfo provider = t.GetProperty("Provider");

Finally, we can get the actual provider instance by using the property info GetValue method:

object instance = provider.GetValue(prop, newobject[0]);

We have encapsulated this behavior in a static field in DesignUtils, so that this process happens only once. From inside the Web control custom serializer, we could get a reference to the controller with the following code:

This prototype implementation of an MVC-like framework is by no means complete or production-quality. However, we have explored almost every aspect of the IDE design-time architecture, and we have even extended the concept to the run-time, taking advantage of the intrinsic component-oriented features of the whole of the .NET framework. So, even when the Web Forms implementation is particularly weak (because we are causing a full view and model refresh on every change), it does prove the concept. More granularity, as well as a richer event model for the controller would greatly benefit the architecture.

Deep integration with VS.NET can dramatically increase developer productivity, and by using custom code generation techniques we can achieve quite complex component persistence, and even enforce certain architectural decisions, such as the ones we made for the model accesses.

For those researching other areas of component persistence, you should know that components can also be stored in resource files. Even more, we can attach our own serialization provider, as we did for Web Forms controls, to serialize to other mediums, such as XML, a database or whatever.

It’s also possible to create a custom root designer, with custom drawing, elements in the toolbox, etc. Just like the DataSet or XmlSchema designers. Look at the article here for a good example of that.

Tip 1: debugging these designers is hard. We have to either start a web client (to work from within it) or start a Windows one. Trying to debug both is very difficult, as the process seems to be loaded twice, so you effectively stop debugging.

Tip 2: beware that using an ExpandableObjectConverter may cause the child component (accessible in a converter through the ITypeDescriptorContext.Instance property) to cease being sited, and we may have trouble retrieving services.

Tip 3: if you use a DropDown type of editor, if you open new dialog forms, the property browser immediately loses control of the focus and the editor no longer controls the opened form.

Tip 4: it’s tempting to use a converter IsValid method to determine validity of a certain value when we provide lists of standard values. The validation is not automatic, however. We have to override this method and check the received value. But again, this method override may need a TypeDescriptorContext. As there is no need to use the IWindowsFormsEditorService, we can safely pass our custom implementation of the context here.

During this article we explored the most advanced features available in .NET and VS.NET for component-based development. We offered deep integration with the IDE, and even expanded the model to the run-time.

We have discussed the MVC design pattern, and created a concrete implementation that can make application development substantially faster. Not only that, we were able to create an implementation that can work with the same code base for both Windows and Web Forms.

Create a virtual folder in IIS pointing to the WebComponents folder, with the same name.

Open the solution, and run the Windows and Web apps.

The code assumes you have SQL installed locally, with the sample Pubs database. The files WebComponents\Web.config and WinComponents\App.config contain a connection string pointing to it. You must ensure the user and password in those files is valid for your installed database.

Another important issue is related to threading. If form initialization is costly, you may be tempted to span a new thread to perform it. We could use the following code to invoke the InitControls method in another thread:

ThreadPool.QueueUserWorkItem(new WaitCallback(InitControls));

However, if your initialization code has to ask for a service through the host, this will not work. An exception will be thrown. That’s because services are expected to be pulled from the main application (VS.NET) thread. If you don’t need to retrieve services, or if you did so from inside the Load event handler, for example, then there’s no problem.

Depending on the concrete application and service our components provide, we may use qualified type names that we use to instantiate the concrete objects. For example, your application may allow the user to define through a dropdown the type that will handle a certain request or application feature. You may even get the list from a database. The usual way to load a type and reflect it, to show its members in a form for example, is through Type.GetType:

This will only work at design-time for assemblies in the GAC. That’s because the IDE is not running from the place where the project is stored. So, there’s no way for the process running the IDE to locate the assemblies, even if they are referenced by the project. There is another service that provides us with the type loading feature, the ITypeResolutionService. This service provides a GetType method that will correctly locate an assembly by taking into account the assemblies referenced in the current project.

In this editor, we can take advantage of the IControllerService to fill the dropdown with values:

Here we can immediately appreciate the benefit of having globally available services to avoid code duplication mainly with type converters:

Custom and Global Commands

The pending issue is how to provide an easy way to check all configured mappings in a single step. The IControllerService has a method, VerifyMappings for that purpose, and its implementation is as follows:

Note how the code becomes extremely simple when the common features are moved to first-class IDE services. With the verb in place, we will see the modified context menu:

Another usual feature is to add to this context menu the most used editors for the component. For example, we could add an Edit mappings item to the menu. This may seem like a no-brainer, but there are subtleties too. The first step is to add another verb, that’s the easy part:

Note that we are asking for the service to the host directly (because of our implementation of ITypeDescriptorContext). Anyway, the srv variable will always be null. Why? The answer is that this service is not provided by the IDE itself, but by the property grid, more precisely, the internal class System.Windows.Forms.PropertyGridInternal.PropertyGridView, which responds to requests for this service by returning itself (because it implements the service interface). Looking at the IL code for the ShowDialog implementation in this class reveals some complexity in the process, which involves calculating the dialog position, setting focus and passing the call to another service, IUIService. That’s why we didn’t directly instantiate a form and called ShowDialog in the first place in the editor!

We won’t duplicate all this code, so we will instantiate the form directly instead, as there’s no restriction to doing so from within a designer verb:

The code is almost the same that is used in the editor itself, except for the call to the IWindowsFormsEditorService.

If the form developer needs to use several components at once, checking the settings one by one can be tedious. We can use another IDE service to add so-called global commands. It’s the IMenuCommandService. As checking the mappings is a task being performed in the controller service, it seems logical to put this new behavior inside it. Recall that our service is instantiated by the controller designer in the Initialize method call. In the service constructor, we can hook our global command:

We can only add designer verbs, which inherit from the MenuCommand class received by the AddCommand method. This new context menu item is shown always every time the user clicks anywhere in the components area of the form:

Note that the controller is not selected. We clicked next to it, in the components surface. The handler now iterates all components checking one by one in turn:

It simply passes the ball to the VerifyOne method for each controller found. The IDesignerHost.Container.Components property contains all the references that currently exist.

If only it were that easy… Unfortunately, if we now right-click on the controller, we will sadly see that the global command has replaced the first controller designer verb:

The Verify this controller … item has disappeared! This strange behavior happens because the IDE first asks for the component verbs, and then (rather inexplicably) puts all the global verbs on top. The only workaround I found is adding a sort of “placeholder” verb in the controller designer:

All the designer verbs are displayed at the bottom of the property browser too, allowing easy access to them. But unfortunately, the new empty verb we added appears there! Note the first colon at the beginning :(

For now, we either have to live with that, or drop global commands.

Now we have all the IDE integration plumbing in place, we get to the point where we have to actually reflect the model data in the mapped UI widgets. As we stated above when analyzing the framework architecture, the controller delegates the responsibility of setting/getting values in the UI to adapter classes which know how to handle Web and Windows forms.

Dealing with different View technologies

We know that we will support Windows and Web Forms. We will do so by defining the common members we will need from both technologies in a common base abstract class, and let each “adapter” concrete implementation to perform the necessary actions for the particular technology.

There are fundamental differences in the way Web and Windows Forms access and set values in their child controls. We already mentioned naming differences also. These details will be isolated in a new service we will provide, the IAdapterService. Its interface contains the basic method we need:

Two classes implement this interface, WebFormsAdapterService and WindowsFormsAdapterService. Both are pretty similar, so we will look at the former. Both adapters receive in the constructor the parameters they will use to resolve the other methods:

The Windows Forms version is similar, but casts the controlsContainer parameter to a Form. The first four methods are pretty simple. You can look at the downloaded code for their implementation. The interesting bits are the RefreshView and RefreshModels methods. The former causes an iteration on all the configured mappings in the received controller, where the appropriate control properties are set to the values found in the model. The opposite happens in the latter method.

We simply use reflection and load types and properties and set values. The reverse process (RefreshModels) is identical but instead of calling SetValue on the control property we do so in the model property.

We already know to hook a new service into the architecture, so let’s have a brief look at the added code in the ControllerDesigner.SetupServices method that performs the task:

You may have noticed that we pass false as the last parameter when we add any service to the host. That parameter specifies whether we want to promote the service to higher layers in the IDE architecture. It’s not advisable to do so, unless you’re absolutely sure that the service is unique and never needs to be changed dynamically. In our case, we are switching the service object depending on the root component type, so we need it to remain in the IDesignerHost and be easily replaced. By passing false to the third parameter, we indicate that the service only lasts for the life of the current root designer.

We can now take advantage of this service when we access the mappings in the controller extender properties (we removed attributes for better readability):

Note that these methods are really simple now because the adapter is taking care of the interaction with the view. This is slightly different than the model proposed by the original MVC pattern, where each view technology would have its own controller.

Before we move on with the view synchronization, however, we need to know how the view asks the controller to load a certain model.

Model Behavior, the MVC way

As the view is not allowed to execute actions directly on the model, isolation is preserved. As a consequence, the controller exposes methods that cause the actual model method execution. The model contains behavior as well as data, and it uses that data either to hold results from method executions or as input for some actions.

A simple PublisherModel component may expose, besides the properties that map to the publishers table fields in the sample Pubs database, the three basic operations on an entity: Load, Save and Delete:

Note that there is no “platform” specific code. Not a single line. But how and when does the controller update the UI? There are fundamental differences in this timing in the Web and Windows environments. In the former, the UI is loaded typically on the Load event, as post-backs cause new Load events to be fired. A Windows Form, however, needs to refresh the view at times other than the Load, because further actions don’t cause new Load events. To provide this connectivity between the containing form, its events and the controller actions (namely, RefreshView and RefreshModels), we use connectors.

Connecting the Views

The responsibility of the connector is to call the controller methods to synchronize the models with the view at the appropriate time. Basically, the UI container (a Web or a Windows Form) should instantiate the appropriate connector and call Connect passing the relevant parameter to perform the wiring. We provide a base class as usual from which both connectors will inherit:

In order to make the process automatic, we will emit the concrete connection code from inside the controller code generation, after detecting the hosting technology. This is the relevant code from the ControllerCodeDomSerializer method:

Note that the only difference is the concrete connector being instantiated. An important thing to notice here is that this method will never be called at design-time. The IDE only loads properties on components, but it will not call methods.

Note that this will hook the controller to the page events, causing them to be called at the appropriate times.

Let’s wrap up the interactions that will happen at the Load event:

The critical point here is the Site.GetService() method call, which must give the controller an instance of the appropriate adapter to use. We have already tested that the service is right there when attached at design time. But the designer is only called at design-time, so what happens at run-time?

Back at the beginning, when we introduced this .NET Component-oriented architecture, we said there were differences between UI controls and non-visual components. We are now in a position to explain what this difference is. At design-time, both kinds of components are sited in the DesignSite and contained in the DesignHost, as shown back then. But at run-time, components get passed the this.components field as the container and they become sited on this container, simply a class of type Container. The site, accordingly, is of type Container.Site. The problem is that the GetService method, implemented internally in the Container class itself, only returns services of type IContainer, which it itself provides. All other services are gone. Worse, visual controls don’t have a Site at all, and that’s the difference.

What we can do is perform some run-time initialization and provide a custom site that can answer requests for our IAdapterService. Remember that the service instance must always be kept somewhere, just as it’s kept by VS.NET at design-time. We will create a RuntimeSite class implementing ISite that we can use to “site” the components with our own infrastructure:

Our constructor, besides receiving the current container and component, receives a delegate. We said that the service instance must be kept somewhere during runtime. This callback is provided so that when a component asks for a service, the call is passed to this delegate, which can be implemented as it fits in the technology in use. For example, the web version can use the HttpContext to keep the object, something a Windows Forms version can’t do. The delegate gives both a chance to answer the GetService request any way they want. The delegate class and its arguments are:

Simply enough, the web version of the connector first stores the service in the HttpContext, and later lets the service itself answer to components asking for the service. Now the controller, in its RefreshModels method to be called at Page.Load time, will successfully retrieve the service:

The web version of the framework is now complete. When the page is loaded, the model is updated with whatever values existing on the form as posted (or initially loaded) by the user. Just before rendering to the client, and generally after all the event handlers for button clicks, textbox changes, etc. have been called, the controller refreshes the view with what has ended in the model. A Web Form showing publisher data and with the correct mappings, can provide load/save/delete features with the single line of code that we saw in each button handler:

Now, suppose we enter an ID in the respective field and click Load, this is the sequence of actions until the page comes back to us:

On Load, the adapter service is hooked and components are sited.

Model is refreshed, so the value in txtID is placed in the PublisherModel.ID property.

The event handler calls the controller method, LoadPublisher().

The model goes to the database and loads itself with the returned row.

Before rendering, control values are updated with the new values in the model (the complete publisher data).

The page is rendered with the values and sent to the browser.

And the page designer only had to configure the mappings using our IDE integration features, and call the appropriate controller method when it was appropriate!

What’s more, if later we decide to move (or at the same time) to a Windows Forms client, the same controller and mappings will do the work. And even the event handlers for the UI events will look the same!

As Windows Forms allows statefull applications, we can simply keep the relevant variables inside the connector itself. However, in the web, at initialization time, we know all controls have been created. This is not the case for Windows Forms, where controls are just class-level variables that must be initialized in the InitializeComponent method just like our controller. Therefore, there’s no guarantee that our connector will be called after the controls have been initialized.

And why do we need to access the controls at initialization time? Because unlike the web where we have a single point of model refreshes, the Load event, in Windows Forms, we have to refresh the models as soon as a control is modified, and we have to attach to that event and trigger the model refresh.

As you can see, each connector (one for each controller) will have the service and the corresponding connector. The OnActivated handler takes care of hooking the RefreshModels method to each control Leave event:

The other trick we did at the end is hook the ModelChanged event fired from the controller to the same controller RefreshView method. This way we achieve automatic UI updates without writing a single line of code.

Finally, the delegate that handles GetService requests (and which we passed to the new RuntimeSite for each component), simply returns the reference it keeps to the service:

Persisting at design-time through code certainly has an edge over run-time detection and emission: it’s compiled and will always be faster. For example, we may want to emit some attributes in the rendered Web Forms controls that we could use in client-side JavaScript to know the details of the mappings. Again, there’s a natural way that seems obvious: attach to PreRender and iterate configured mappings again, adding the relevant attributes to the control.

If the mappings and the controls are known at design-time, why do we have to waste precious run-time processing doing this iteration over and over on each page interaction? The answer lies in a much better way that we can take advantage of. It’s called an IDesignerSerializationProvider.

An object implementing this interface can be registered with the IDesignerSerializationManager (which we receive in our controller serializer).

We first check that the provider hasn’t been added already. At serialization time, the manager will call each configured provider and give it a chance to provide a custom serializer for a type. This is done in the GetSerializer method:

If we find a control we can attach attributes to (implementing IAttributeAccessor), we return our new serializer. From now on, every time the IDE needs to serialize a Web control (either HtmlControl or WebControl), our serializer will be called, receiving the control being serialized.

In order to avoid disturbing the normal serialization process, we will retrieve the regular serializer for the received control first, and get the code statements from it. We defined a helper method in the base serializer to get the appropriate type:

Here we see in action the ITypeResolutionService. After we retrieve the relevant attribute, all we have is the qualified type name, and we must load it through that service to guarantee the assembly it lives in is located and successfully loaded. After that, we simply use the Activator.CreateInstance method to create the serializer and return it.

Let’s go back to the WebControlSerializationProvider.Serialize method:

As a side node, we have to point that the ViewInfo object contains a Controller property that points to the controller that created it. That’s how we emit its name to the page. This is a specific implementation detail of ours, but what if that property didn’t exist? How would we retrieve the real property provider backing up a “fake” property on a control?

The answer is not easy, unfortunately. When we ask the TypeDescriptor for the property, we expect a PropertyDescriptor. However, when the property is extended, in a class derived from it, ExtendedPropertyDescriptor is returned instead. This object has a property, Provider that has a reference to the object that is providing this property. Great! What else could we ask for? Well, we could ask for the class not to be private!

So, now that we’re out of luck, we have to find a workaround, as usual. The trick comes from reflection. Beware that the technique we’re about to use is highly risky. If MS decides to change the class name or the property name, or even remove it, your now-working code will stop doing so. And they wouldn’t be doing anything wrong, as the interface was private. Now that you are aware, let’s get to it!

To achieve this, we have to first load the type. Through the Assembly.GetType method we can’t reach a private type. We have to use GetTypes and iterate all of them and find the matching one. We created a utility class, DesignUtils with a LoadPrivateType that does the trick. Its usage is simple:

Now that we have the private reflected type, we can get the property as usual with:

PropertyInfo provider = t.GetProperty("Provider");

Finally, we can get the actual provider instance by using the property info GetValue method:

object instance = provider.GetValue(prop, newobject[0]);

We have encapsulated this behavior in a static field in DesignUtils, so that this process happens only once. From inside the Web control custom serializer, we could get a reference to the controller with the following code:

This prototype implementation of an MVC-like framework is by no means complete or production-quality. However, we have explored almost every aspect of the IDE design-time architecture, and we have even extended the concept to the run-time, taking advantage of the intrinsic component-oriented features of the whole of the .NET framework. So, even when the Web Forms implementation is particularly weak (because we are causing a full view and model refresh on every change), it does prove the concept. More granularity, as well as a richer event model for the controller would greatly benefit the architecture.

Deep integration with VS.NET can dramatically increase developer productivity, and by using custom code generation techniques we can achieve quite complex component persistence, and even enforce certain architectural decisions, such as the ones we made for the model accesses.

For those researching other areas of component persistence, you should know that components can also be stored in resource files. Even more, we can attach our own serialization provider, as we did for Web Forms controls, to serialize to other mediums, such as XML, a database or whatever.

It’s also possible to create a custom root designer, with custom drawing, elements in the toolbox, etc. Just like the DataSet or XmlSchema designers. Look at the article here for a good example of that.

Tip 1: debugging these designers is hard. We have to either start a web client (to work from within it) or start a Windows one. Trying to debug both is very difficult, as the process seems to be loaded twice, so you effectively stop debugging.

Tip 2: beware that using an ExpandableObjectConverter may cause the child component (accessible in a converter through the ITypeDescriptorContext.Instance property) to cease being sited, and we may have trouble retrieving services.

Tip 3: if you use a DropDown type of editor, if you open new dialog forms, the property browser immediately loses control of the focus and the editor no longer controls the opened form.

Tip 4: it’s tempting to use a converter IsValid method to determine validity of a certain value when we provide lists of standard values. The validation is not automatic, however. We have to override this method and check the received value. But again, this method override may need a TypeDescriptorContext. As there is no need to use the IWindowsFormsEditorService, we can safely pass our custom implementation of the context here.

During this article we explored the most advanced features available in .NET and VS.NET for component-based development. We offered deep integration with the IDE, and even expanded the model to the run-time.

We have discussed the MVC design pattern, and created a concrete implementation that can make application development substantially faster. Not only that, we were able to create an implementation that can work with the same code base for both Windows and Web Forms.

Create a virtual folder in IIS pointing to the WebComponents folder, with the same name.

Open the solution, and run the Windows and Web apps.

The code assumes you have SQL installed locally, with the sample Pubs database. The files WebComponents\Web.config and WinComponents\App.config contain a connection string pointing to it. You must ensure the user and password in those files is valid for your installed database.

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.

Comments and Discussions

Although years has passed , the thoughts still worth us learning . I am a newbi in .Net . Last year , i tried to bind data with control by control's tag property by assigning the property string to tag and recursion , iteration and reflection . Later , i looked back and thought that would caused trouble when tag property used for other info and hope there was a data-proerty and control-property mapping manager between ui and entity .Then I also tried use component which can get all child controls on a (window) form , but my ABC was not well enough to implement this ... In this article , i've seen all work i was not able to do , exited and affected , you know the feeling ...

I'm relatively new to .NET and have been struggling with the following issue.

I'd like to attach the View mapping of your code to a DateTimePicker object, specifically the Value property. However, it seems that the System.Reflection CanConvert is unable to convet a DateTime.Value property. Is there some elegant way around this?

I've looked into the TypeConverterAttribute suggestion that you recommended in an earlier post re: boolean types, but I cannot seem to get that to work. A code fragment or concrete example would be much appreciated.

Hi! Can anyone help me? For examlple i'm provide new property for some controls. Can I develop custom property editor for that extended property? If so, that the value i will recieve in overrided edit method?

This is because my extender component has internal collection of items. This Item must be assigned (or not) to some of controls. By the way, I need a drop down (or another) editor to manually select and assign item to extended control.

You have a bug in your instance converter class' ConvertTo method. Specifically you are returning an InstanceDescriptor when the destination type is string instead of when it is typeof(InstanceDescriptor).

Let me first say that I was very impressed with your article. I found an error in your implementation. When you multi-select multiple controls and try setting the extending property, Visual Studio encounters a problem and has to close. Do you mind telling me what may be causing this?

FYI, I have found a work-around. If you markup the ControlProperty property of ViewInfo with the attribute of [MergableProperty(false)], then it works. I would like to be able to multi-select controls that are similar but it forces the IDE to dump. If you know what is going on here please let me know but in the meanwhile, I suggest that you add this attribute so that you don't have issues when trying to mult-select the ControlProperty.

Hi,In most of the article I see that the IExtenderProvider is implemented by the Components, components used in the runtime. But this interface is (most of the time) a Designer related interface, so my question is how and where implementation of this interface should be put so that the implementation isinvisible at runtime. I tried to put the implementation together with the ControlDesigner (extending it) but then I am getting the extended attribute in the properties windows multiple times ( number of components using this designer ).

I simple wish to separate the designer time classes, helpers, interfaces from the runtime code (The code that is supposed to do the actual business)

I don't know what you mean by "don't work with file". There's always a file backing a designer!!!Are you creating those components manually and adding them through your designer? You should always be doing that through the designer host instead. Can you show the code you're using to manipulate the components and then where it's "failing"?

"You should always be doing that through the designer host instead." - I wrote about it, new component actually adding through the designer host.To show all the code I using to manipulate the components and to explain a problematic place a little complicated, because it's a part of big program and a chain of used files is pretty long. But thank you for proposing.

oop promised code reuse but as of yet its promises have only been partially realized. we all know that there is going to be minor tweaks and modifications of existing classes when we import them into new projects. so much for reuse. but wait, now with .net components, code reuse can be fully realized. yes it can, however without the ability to visually modify the components, we're stuck having to go back to the source to make changes and that sux. now after reading this article i can have the best of both worlds. true code reuse and visual design for rad. wow, is it a dream come true? read this article and read the other one mentioned in here, .net shape library. that one goes over a lot of the points left out in this one. for example, why adding declarative code is better vs xml files, which is ironic becuase the lead designer of castle aop knocks the spring.net aop library for using too much xml declaratives. and he's right, outside declarative statements become too difficult to maintain and change quickly.

Some of the built-in converters are not properly implemented, unfortunately. You can always attach a custom converter to your properties by using the TypeConverterAttribute. I suggest you try to create a minimalist repro of the issue using a simple console app and creating the converter manually, and posting that as a bug in ProductFeedback (http://lab.msdn.microsoft.com/productfeedback/)

Hi Daniel,first of all *thanks* for your great article(!), it has been very nice of you to publish it.

I'm having a hard time with a component i am developing, hope you have the time to answer to me.

It's just a simple button, which getting pressed show a ContextMenuStrip.Everything works fine, both at design time and run time, but what is driving me crazy is the paste operation.I create a ContextMenuStrip from the DesignerHost, and i associate it to my button through the AssociatedComponents property.When i copy or cut my control, the ContextMenuStrip get copied or removed as well, but when i paste the control, i get this error:

"Top-level control cannot be added to a control."

It seems that when the designer add the ContextMenuStrip to the design surface, it adds it to the Controls collection of some component. Why does this happen?

I have a component which declares a serializer in its DesignerSerializer attribute. When the component is added to a container component, for example a Windows form or a simple component (root component), the serializer is supposed to generate a nested type within the root component. The end result should look as follows:

The serializer generates code for SomeClass and I can display the code generated by the serializer and it looks ok. However when I switch to code view, Visual Studio removes the generated code. Do you know why that is happening? Do I need to add the declaration to the Context stack of the Serialization manager?

I'm afraid you have hit another limitation of the support for CodeDom available to your components. You see, internally VS uses a CodeProvider that maps the CodeDom to its DTE CodeModel crap, and this translation is not full-fidelity. For example, if you emit a method override, the "override" keyword is lost in translation and never written to the file.

My suggestion is to directly write out another partial class (all components supporting designers are already partial classes) with your nested class if needed, and only emit from your designer what you know VS will translate appropriately. You can ask for the ProjectItem as a service to get the file name you need to generate, add it as a dependent file, and so on.

Daniel, thanks for quick reply.If I could add a class to the project that would be even easier, I wouldn't have to deal with a nested type. I just couldn't find a way to automatically add a class to the project when a component is added to design surface, so I turned to generation of a nested type.Since VS 2005 uses MSBuild is there a way to get a reference from within a designer or serializer to Microsoft.Build.Engine.BuildItem or Microsoft.Build.Engine.Project?

You're mixing the stuff. MSBuild is for *compiling* stuff. You don't want MSBuild objects to add stuff to VS. You need to talk to the DTE.From your serializer, just do a GetService(typeof(ProjectItem)) in the received IDesignerSerializationManager, and from there you can get the entire VS to do whatever you want. You will want to generate a temporary file (using the CodeProvider that VS gives you, too, which you can get issuing a GetService(typeof(CodeProvider))) and then using the ProjectItem.ProjectItems.AddFromXXX methods (don't remember which one).

Daniel,Thanks for your advice this is definitely the way to go. BTW I wasn't mixing things. I have a tool that is used by the end user to build simple apps. The tool uses MSBuild projects to compile the code it also makes use of the DesignSurface object. I hoped to be able to write the same code for both VS and MSBuild. After looking deeper into the object model I know VSNet only outputs an XML file that's read by MSBuild but internally does not support the Microsoft.Build.BuildEngine.Project object model.

I will have to write code for two different cases: VSNET and MSBuild. The only difference will be in adding a project item to two types of projects, everything else is the same.

At first I've followed your advices about initialising component after setting up it's properties (codedom generated a this.Open() call in my case).

After searching for w while I've found out that there were no need in doing so - it is possible to implement an interface called ISupportInitialize and code generator will happily call my function BeginInit() before setting properties' values and EndInit() after that.

Note: There is a small difference, which may be good or bad - BeginInit() and EndInit() are called in design mode as well.

It's awesome that much can be done... and it's undocumented by MS.I wonder if other .NET parts has that many hidden possibilities out there.

Using hints from your article I have managed to enhance my component to use capabilities offered by visual designer and this component is now much easier to use!

Well, anyway. Let's ask a question

Some properties of my component are enums.Designer hapily allows users to select one of available options from properties editor.But unfortunatelly designed doesn't want to put those enum-type properties on app.config

I think it's strange as the only difference beetween code that loads strings from app.config and code that loads myenumtype is that to load myenumtype the code has to cast using "(myenumtype)" instead of "(string)". Looks like a trivial change.

Oh well, even if designer really doesn't know that, I'd be willing to write a code that would use CodeDom to teach it but I can't find where to plug my code into the process...

Any hints?

By the way: I've tried to use strings instead of enums and doing all of the conversions myself, but there is a bad limitation in this workaround: If user wants to change property's value at run-time, intellisense would not hint about available values. Second limitation is that errors in assigned values are not caught at compile time.

Interesting problem... I would have thought that RecommendedAsConfigurable would solve it :S

Maybe you can try adding properties dinamically through the associated IDesigner (it's PreFilterProperties or sometihng like that), where you can probably fake VS into showing a string property with the dynamic ones, and then internally you generate the code for setting the real property through CodeDom and casting the enum. That may work...

1. Implement a property as enum.2. Write a code that would make the designer think it works with a string property, not an enum.3. Write designer helper class that would make string property look like an enum in the designer.4. Write a custom serialiser that would fix code generated by the designer - change casting to string into casting to myenumtype (in fact it would need to work with two cases - variable stored in the code and variable stored in app.config).

Well... I thought it would be a little bit easier but I've got no better ideas so I'll try doing that.

OK. I have disassembled TypeConverter, StringConverter and EnumConverter to find out if StringConverter implements something undocumented, that could be used be Visual Studio. I could implement the same on a class derived from EnumConverter and everything should have been fine.

Thanks for a great article. I have searched for months trying to understand the relationship between ISite and IComponent. This question will ultimately replace the age old chicken and egg debate, I am sure.

After months of having a development project on hold over a siting issue, I finally resorted to paying Microsoft (out of my own pocket - I am but a poor underpaid C# and .Net consultant) to help resolve the issue. After weeks of effort from many teams, they finally came up with Microsoft.VisualStudio.Designer.Host.DesignSite in their solution. That is what finally led me to your article. Theinteresting thing is that your article is the only place I can find where DesignSite is mentioned. It is an undocumented feature that is absolutely necessary for good designer development.

I think that, after browsing your article, my fee to Microsoft for help was money well spent. It will take more weeks or months to assimilate all the information in your article. I am sure, though, that when I do, I will have a much better understanding of all this undocumented code I just paid Microsoft for.

Great article and you seem to know a lot about this IComponent/IServiceContainer/etc stuff. Can you perhaps shed a light how the following is supposed to work (if it's possible):

Suppose you have a System.Web.UI.Page control. Is there a way to automatically add it to an IContainer (preferably this container is constructed in global.asax) when the page is being requested? Is this also possible for webservices?

Daniel,I have a quick question about TypeCodeDomSerializerI'm overriding both Serialize and Deseserialize methods. The designer serializes with no problems.However, when I try to deserialize it the CodeDomSerializerException "Could not find type 'CES.Components.RootComponent'...." I pass it the instance of System.ComponentModel.Design.Serialization.IDesignerSerializationManager and System.CodeDom.CodeTypeDeclaration

This is the best .NET article I've ever read!You explain and show with concrete examples the poorly (not at all?) documented design-time extensibility. I found it extremely interesting and useful.Thank you very much to have published this material for free.

About the CodeDom section, as you said it's very verbose. Thus, this free tool may help you a lot: Refly.

I've already tried it. And it didn't help much, other than adding a few new constructors that are missing in CodeDom... It's basically the same approach as CodeDom: a huge object model (AST) that I don't care about anyway... I wish there was a kind of XmlWriter-like API (I've even started TDD'ing one, but didn't have enough time...). Under the covers, it would use CodeDom, but on the surface, it would just be plain writing, as if you were typing the code.... sweeet...

I think you should have broken this up into 3 or 5 articles. Also, I would like to see clearer examples of how one can apply some of these techniques to other problems besides the simple 'form' style application.

As I explained in the history of this article, this was a book chapter, finished about 2 years ago for a Pro .NET book from Wrox, which was never printed. Therefore, I decided to make it available for free, even if at the time I was doing it for money (although I never got a peny as they went bankrupt).

The purpose of the article is to show the features available in the ComponentModel, and how to exploit deep integration with the IDE. The MVC model (an implementation of which is in the article) is the most widely used approach to handling separation of concerns in what you call simple "form style application". I don't know what you're looking for, but hte MVC pattern is just one particular use case that I picked to show the former, not the core point of it.

Splitting the article is not even an option as I cannot spend the time it would take to make a logical separation of what was all the time a single book chapter. Sorry. I did argue with the CodeProject submission people that this was too long, and I wanted to basically post a summary of contents and some abstracts so you could decide whether you wanted to read the entire article somewhere else, but they don't do that, they said.

There should be more articles that are so comprehensive and complete. I don't like articles that give you a short intro and some source and now you need to sit there and figure out looking at the code what the writer is trying to get at.

Well, I for one agree with you. I started reading this, but after the introduction my eyes started glazing over. I don't think it's the author's fault; he's trying to explain what seems to be a very complicated subject. I read his reply stating that this was a book chapter, so that explains why it's so long. (I stopped reading long before I got to the History section where he mentions that this is a book chapter; I wish he had said so in his introduction.)

I'm somewhat interested in this subject, but only in a casual way. So from my point of view this would have been much better as a series of shorter articles.

Wow... Maybe Microsoft really did have to make the IDE framework and .NET components and Reflection this complicated, but the one thing that came to mind as I tried to read this is Einstein's famous quote about trying to keep things simple.

Why Microsoft cannot have at least one comprehensive example like yours? I've been looking for documentation on how the designer can find reference to the serializer and vice versa. Now it's all clear. I think the link is obscured by the fact that these references are hidden in the GetService method.

How can we be sure that the base set of services will be available in the future, since none of the interfaces enforce it?

Also, you might want to point out that RootDesignerSerializerAttribute is now obsolete and instead should be specified by DesignerSerializer with base type of TypeCodeDomSerializer.

Very good questions. GetService basically allows them to expose what they need, without having to commit to any services/interfaces, as they are usually all undocumented, and in the best case, they are so poorly documented that you don't even know how to use them. In fact, it's almost never mentioned in which context you can access a given service to use its features.

This obviously makes relying on these services a risky business. This is one price to pay to have closer integration with VS. As long as we let MS know that people IS using those undocumented/"unsupported" features, as I've tried to do with the terrible mistake of retiring support for extender providers and components in ASP.NET v2, they should keep them. One reason to publish this article and keep doing the same on "obscure" features in the future, is to engage people in using them, and force MS to keep supporting them, even if they were not officially documented. As long as there's enough publicity and people using them, MS will probably keep them.

Good point on the designer attribute. Honestly, I did the entire article and the code when Whidbey wasn't even in the horizon...