Editing Model Architecture

[This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]

Your design-time implementation interacts with run-time controls though a programming interface called the editing model. The objects being designed are called editable objects. This topic describes the architecture and usage of the editing model in the WPF Designer for Visual Studio.

Your controls are defined in Extensible Application Markup Language (XAML). You update the XAML for your controls programmatically by using the editing model.

The editing model consists of three functional subunits: a model, a public wrapper that abstracts the model, and a view that represents the user interface (UI) of the model. The model and the view are separate, but the wrapper and model are closely related. The following illustration shows the relationship among the three subunits.

Model, ModelItem, and View relationships

The design environment uses the ModelItem type to communicate with the underlying model. All changes are made to the ModelItem wrappers, which affect the underlying model. This allows the model to be simple. The ModelItem wrappers handle complex designer features, such as transaction support, undo tracking, and change notifications.

Any designer feature which requires creating new objects on the design surface uses the ModelFactory class. Each object in the designer is wrapped with a ModelItem instance. Model items are created by a ModelFactory.

The following code shows a typical ModelFactory call.

ModelItem newButton = ModelFactory.CreateItem(_context, typeof(Button));

The CreateItem method always returns a data type of ModelItem. This is the base type for all items in the WPF Designer editing model, and it represents a wrapped instance of the type passed to the CreateItem method. The CreateItem method also requires an instance of a WPF Designer editing context (_context in the previous code sample), which is used to locate other services and dependencies in the designer.

Explicit item creation through the factory is important for objects to be placed on the design surface that have default initializers. This procedure is not necessary when you are simply setting properties to values.

Always use the CreateItem method to create new objects on the design surface. This is because many raw instances cannot be parsed into model items. Once a value is set in the model, you should only interact with it through the model. The instance backing the model can be rebuilt by the framework at any time, which invalidates any references you have cached.

Creation Options

You will sometimes need to customize the creation behavior of your objects. For example, a database connection component may choose not to query the database at design time. You may want to have control over instance creation The first time a component is created.

In this case, a component is dragged from the Toolbox or pasted from the clipboard. You may want to pre-configure the component with usable defaults. These defaults, if unchanged, are serialized into the XAML.

You can pass an optional set of flags to the CreateItem method by using the CreateOptions enumeration.

The InitializeDefaults flag is used by tools such as the creation tool to initialize a set of preconfigured property values. For example, a ContentControl may provide some default content. This does not take the place of correctly specifying default values of properties in the runtime control code.

Values set with this flag persist into the XAML.

This flag should not be used by parsing code, as the defaults may have been removed by the user during editing of the object in the designer.

The CreateItem method routes the call to CreateItem. This method performs several steps, outlined in the following flowchart.

Explicit instance creation using the creation API

In addition to creating new items, another common designer task is to change the parent of one item to another. This is handled through a static class named ModelParent, which provides features common to most parenting requirements.

  • Locating a valid parent object, given a coordinate offset or starting item in the hierarchy to search.

  • Determining if a given object can be a parent of a particular type.

  • Changing the parent of one object to another. This change also removes the old parent from the object. This removal allows the old parent the opportunity to clean up any data, for example, attached properties, which may reside on the item .

The ModelParent class works by locating a ParentAdapter class for current and proposed parent objects. If no ParentAdapter class exists, an object cannot be assigned to a parent. The ParentAdapter class defines several overrides for common cases. For example, many overrides accept a GestureData object as a parameter. This data type is available from the WPF Designer command mechanism when code is handling a user command. It provides common contextual information.

The ParentAdapter allows containers to perform smart parent removal. For example, if an object with a Canvas control parent is being changed to a Grid control parent, the Canvas control's attached properties on the object can be removed automatically.