Skip to content

Modify Your Application's Existing Software

Gary edited this page Aug 27, 2014 · 1 revision

Table of Contents

This section discusses how to modify the sample's existing code to work for the new application.

What's Different?

Examining the differences between SimpleDomEditor and LandscapeGuide helps you see what changes you need to make to get to the goal application. In particular, look at the data model differences, because these two applications are very similar otherwise.

SimpleDomEditor has a main type "eventType" that describes events, and the main list view shows a sequence of events. LandscapeGuide does something similar, but has two types of items in the list view: plants and ornaments. On the other hand, SimpleDomEditor has two resource types, "animationResourceType" and "geometryResourceType", which are both subtypes of "resourceType", and are both listed in the Resources pane. Following this pattern, "plantItemType" and "ornamentationItemType" are subtypes of "itemType" in LandscapeGuide. In SimpleDomEditor, the two resource types only appeared in the Resources pane's list view; in LandscapeGuide we want these two types to appear in the application's main list view, a list box, instead.

Changing Types

You can start modifying source by mapping old type names to new ones. In general, "event" transforms to "item" and "eventSequence" to "itemList". This means making global text changes in the project files like these:

  • "eventType" to "itemType"
  • "eventSequenceType" to "itemListType"
  • "eventList" to "itemList"
Keep an eye on the use of these types from SimpleDomEditor:
  • "resourceType"
  • "animationResourceType"
  • "geometryResourceType"
Although these types aren't used in LandscapeGuide, their use suggests ways to use types in your new application, such as code that differentiates between the animation and geometry resource subtypes.

DOM Adapter Modifications

Two of the DOM adapters in SimpleDomEditor carry over pretty well to LandscapeGuide:

  • DomNodeAdapters/Event.cs: change its name to Item.cs and use it for "itemType".
  • DomNodeAdapters/EventSequence.cs: change its name to ItemList.cs and use it for "itemListType".
The original files can be easily modified to work for the new DOM adapters. SimpleDomEditor's adapter DomNodeAdapters/Resource.cs has already been removed.

Item.cs provides the Item class, a simple DOM adapter with properties for the item type's attributes:

/// <summary>
/// DomNode adapter for item data</summary>
public class Item : DomNodeAdapter
{
    /// <summary>
    /// Gets or sets name associated with item, such as a label</summary>
    public string Name
    {
        get { return GetAttribute<string>(Schema.itemType.nameAttribute); }
        set { SetAttribute(Schema.itemType.nameAttribute, value); }
    }

    /// <summary>
    /// Gets or sets notes associated with item</summary>
    public string Notes
    {
        get { return GetAttribute<string>(Schema.itemType.notesAttribute); }
        set { SetAttribute(Schema.itemType.notesAttribute, value); }
    }

    /// <summary>
    /// Gets or sets rating associated with item</summary>
    public int Rating
    {
        get { return GetAttribute<int>(Schema.itemType.ratingAttribute); }
        set { SetAttribute(Schema.itemType.ratingAttribute, value); }
    }
}

Be careful to specify the correct type for properties. If you edit an existing file, it's easy to forget this and produce a property like this:

/// <summary>
/// Gets or sets notes associated with item</summary>
public int Notes
{
    get { return GetAttribute<int>(Schema.itemType.notesAttribute); }
    set { SetAttribute(Schema.itemType.notesAttribute, value); }
}

This causes an exception, warning that a value could not be cast, because the Notes attribute is a string, not an int.

Here is ItemList.cs, whose Items property returns a list of the items:

/// <summary>
/// DomNode adapter for item list data</summary>
public class ItemList : DomNodeAdapter
{
    /// <summary>
    /// Gets list of items in item list</summary>
    public IList<Item> Items
    {
        get { return GetChildList<Item>(Schema.itemListType.itemChild); }
    }
}

Note that these adapters use the class and field names defined in the Schema.cs file's stub class Schema. Again, this is similar to SimpleDomEditor, which uses names from its Schema class for its DOM adapters.

SchemaLoader Changes

The schema loader class, in SchemaLoader.cs, loads the items.xsd file to get information from the schema, such as restrictions.

The SchemaLoader.cs file from SimpleDomEditor can be used with some straightforward changes. Many of these have already been made with the global string substitutions for type names described above.

The constructor SchemaLoader() has already been changed, so the remaining changes are in OnSchemaSetLoaded(). These changes are to define extensions, set up property editing and descriptors, and set up palette items. The code to do this is already in the sample's OnSchemaSetLoaded() method; it simply needs to be edited for the LandscapeGuide types.

Define Extensions

Extensions are classes that allow you to cast data in DOM nodes to other, more convenient types, and perform a variety of useful behaviors, such as listening to events on DOM nodes. Extensions are defined for data types, and this is usually done in the schema loader.

This extension definition from SimpleDomEditor goes away, because the editing context EventContext no longer exists:

Schema.eventType.Type.Define(new ExtensionInfo<EventContext>());

This extension definition from SimpleDomEditor goes away, because the Resource DOM adapter no longer exists:

Schema.resourceType.Type.Define(new ExtensionInfo<Resource>());

The new DOM adapters, Item and ItemList discussed above, have these definitions for their associated types:

Schema.itemListType.Type.Define(new ExtensionInfo<ItemList>());
...
Schema.itemType.Type.Define(new ExtensionInfo<Item>());

These extension definitions in SimpleDomEditor:

Schema.eventSequenceType.Type.Define(new ExtensionInfo<EventSequenceDocument>());
Schema.eventSequenceType.Type.Define(new ExtensionInfo<EventSequenceContext>());

change in LandscapeGuide by simply changing type names as indicated above:

Schema.itemListType.Type.Define(new ExtensionInfo<ItemListDocument>());
Schema.itemListType.Type.Define(new ExtensionInfo<ItemListContext>());

This definition is new for LandscapeGuide:

Schema.itemListType.Type.Define(new ExtensionInfo<DataValidator>());  //for restriction facets

The ratingAttribute attribute is used for ratings and is restricted to an integer between 1 and 10, as specified in "ratingType" in the XML Schema. Rules are automatically added to validate this attribute when the schema is loaded if you are using the default ATF schema loader. However, for the rules to operate, the DataValidator validator must be defined for the root type, as in this statement above.

Property Editing and Descriptors

Property descriptors are metadata for class properties that controls such as property editors can use. You can set up property descriptors for each type, which determines which properties — that is attributes — can be edited in property editing controls. The following block (very similar to the descriptors set up in SimpleDomEditor) specifies the editable properties for a plant item in a property descriptor:

// Register property descriptors for plant type
Schema.plantItemType.Type.SetTag(
    new PropertyDescriptorCollection(
        new PropertyDescriptor[] {
            new AttributePropertyDescriptor(
                Localizer.Localize("Name"),
                Schema.plantItemType.nameAttribute,
                null,
                Localizer.Localize("Plant name"),
                false),
            new AttributePropertyDescriptor(
                Localizer.Localize("Botanical name"),
                Schema.plantItemType.botanicalNameAttribute,
                null,
                Localizer.Localize("Plant botanical name"),
                false),
            new AttributePropertyDescriptor(
                Localizer.Localize("Notes"),
                Schema.plantItemType.notesAttribute,
                null,
                Localizer.Localize("Notes on plant"),
                false),
            new AttributePropertyDescriptor(
                Localizer.Localize("Rating"),
                Schema.plantItemType.ratingAttribute,
                null,
                Localizer.Localize("Rating reflecting interest"),
                false),
    }));

The AttributePropertyDescriptor constructor's parameters for each attribute/property specify the localized name, attribute, category, description, and whether the property is read only.

All the plant item's attributes are specified here, including the ones provided in "itemType", not just the "botanicalNameAttribute" attribute unique to "plantItemType". If any attributes are omitted, they don't appear in any property editor for a plant item. There is one AttributePropertyDescriptor constructor for each attribute that can be edited.

A similar property descriptor should also be set up for the ornamentation item type.

The class CustomTypeDescriptorNodeAdapter enables metadata-driven property editing in an application. This means that properties that appear in property editors are determined by the metadata provided about the properties in property descriptors, as in the above example. The following statement ensures that when items, either plants or ornaments, are edited in property editor controls, the attributes that are editable come from the property descriptors provided for each item type:

// Enable metadata driven property editing for events and resources
AdapterCreator<CustomTypeDescriptorNodeAdapter> creator =
    new AdapterCreator<CustomTypeDescriptorNodeAdapter>();
Schema.itemType.Type.AddAdapterCreator(creator);

Palettes

The NodeTypePaletteItem class indicates how a type should appear in a palette of items that a user can drag and drop into a document. The following statements set the properties for the palette objects:

// Annotate types with display information for palette
Schema.plantItemType.Type.SetTag(
    new NodeTypePaletteItem(
        Schema.plantItemType.Type,
        Localizer.Localize("Plant"),
        Localizer.Localize("Plant item"),
        Resources.PlantImage));

Schema.ornamentationItemType.Type.SetTag(
    new NodeTypePaletteItem(
        Schema.ornamentationItemType.Type,
        Localizer.Localize("Ornamentation"),
        Localizer.Localize("Ornamentation item"),
        Resources.OrnamentationImage));

Among other things, the NodeTypePaletteItem sets the icon that appears in the palette, specified as an image resource like Resources.PlantImage here.

Palette Changes

The palette for LandscapeGuide contains two items: plants and ornamentation items.The schema loader has already changed to accommodate the new palette.

To create the new palette, make a few changes to PaletteClient.cs. The first change, in Initialize(), determines the items in the palette:

string category = "Landscaping items";
//m_paletteService.AddItem(Schema.plantItemType.Type, category, this);
//m_paletteService.AddItem(Schema.ornamentationItemType.Type, category, this);
foreach (DomNodeType itemType in m_schemaLoader.GetNodeTypes(Schema.itemType.Type))
{
    NodeTypePaletteItem paletteItem = itemType.GetTag<NodeTypePaletteItem>();
    if (paletteItem != null)
        m_paletteService.AddItem(itemType, category, this);
}

This loop finds all "itemType" subtypes and gets information about the NodeTypePaletteItem object for that type, which was set in OnSchemaSetLoaded(), as described above in Palettes. In particular, it gets the icon for the item's image that appears in the palette. The commented out lines also do the job, but the loop is preferable, because it automatically accommodates adding new item types to the schema.

The next change is to a code segment in IPaletteClient.Convert(). It provides information on each palette client item, in this case the item to be used for an object when it is dragged onto a control, such as the list view in this application:

if (nodeType == Schema.plantItemType.Type)
    node.SetAttribute(Schema.plantItemType.nameAttribute, paletteItem.Name);
else
    if (nodeType == Schema.ornamentationItemType.Type)
        node.SetAttribute(Schema.ornamentationItemType.nameAttribute, paletteItem.Name);

These statements discriminate between the two item types, plant and ornament, and set the attribute accordingly.

Context Changes

Only one context file needs to change. SimpleDomEditor's EventContext.cs is not needed in LandscapeGuide and has already been removed. EventSequenceContext.cs on the other hand, is useful, and its name is changed to ItemListContext.cs in LandscapeGuide, as well as changing names in the file.

Most of the modifications are trivial text changes:

  • "eventSequenceType" to "itemListType".
  • "Event" to "Item". (Be sure to do this as a "Match whole word" replacement, or terrible things will happen to your event handlers.)
  • "_event" to "_item".
The following ColumnNames property in the IListView interface specifies what the column names are in the list view control, and lists strings for the attributes common to all items:
/// <summary>
/// Gets names for table columns</summary>
public string[] ColumnNames
{
    get { return new string[] { Localizer.Localize("Name"), Localizer.Localize("Notes"),
        Localizer.Localize("Rating") }; }
}

The change for the GetInfo() method is a little more involved. This code is actually copied from the SimpleDomEditor EventContext.cs's GetInfo() method and then slightly modified to determine information for an item type:

/// <summary>
/// Fills in or modifies the given display info for the item</summary>
/// <param name="item">Item</param>
/// <param name="info">Display info to update</param>
public void GetInfo(object item, ItemInfo info)
{
    Item _item = Adapters.As<Item>(item);
    info.Label = _item.Name;

    string type = null;
    if (_item.DomNode.Type == Schema.plantItemType.Type)
        type = Resources.PlantImage;
    else if (_item.DomNode.Type == Schema.ornamentationItemType.Type)
        type = Resources.OrnamentationImage;

    info.ImageIndex = info.GetImageList().Images.IndexOfKey(type);

    info.Properties = new object[] { _item.Notes, _item.Rating };
}

Similarly to previous code changes, this code discriminates between the plant and ornament item types to set the information correctly: the image used for the item. The field info.Properties is set to an array of the attributes, other than "Name", associated with an item: "Notes" and "Rating". These changes result in the correct attribute information being displayed in the application's main list view for items.

The GenericSelectionContext.cs file is indeed generic and doesn't need to change at all other than the namespace already changed.

Document Modifications

The eventSequence and itemList types function very similarly in both SimpleDomEditor and LandscapeGuide. So much of the code from SimpleDomEditor carries over to LandscapeGuide with only type name changes.

The change from EventSequenceDocument.cs to ItemListDocument.cs is to change the file name and to simply make a few text changes from "EventSequence" to "ItemList". For example, "EventSequenceDocument" becomes "ItemListDocument".

Editor Changes

Again, most of these changes are simple text substitutions.

In going from EventListEditor.cs to ItemListEditor.cs, change the file name and make these text substitutions:

  • "EventList" to "ItemList"
  • "eventSequence" to "itemList"
  • "EventSequence" to "ItemList"
  • "_event" to "_item"
Editor.cs changes involve the same text replacements. In addition, a few other changes are needed.

The namespace must change here:

m_scriptingService.ImportAllTypes("SimpleDomEditorSample");

becomes

m_scriptingService.ImportAllTypes("LandscapeGuide");

A few user interface changes are needed for the name of the list and for file extensions. Change

Localizer.Localize("Event Sequence"),
new string[] { ".xml", ".esq" },

to

Localizer.Localize("Item list"),
new string[] { ".xml", ".pil" },

Build Application

Building the application at this point may reveal some other changes you need to make, such as changing type names that were missed and adding resources, which is described in the next section.

Topics in this section

Clone this wiki locally