-
Notifications
You must be signed in to change notification settings - Fork 263
Simple DOM Editor WPF Programming Discussion
The ATF Simple DOM Editor WPF Sample is very similar to ATF Simple DOM Editor Sample, but it is implemented using ATF’s WPF framework. It shows defining a data model in an XML Schema, editing data, and saving the edited data in an XML file. Application data is displayed in two WPF ListView
controls, and data properties can be examined in a property editor.
This application's data consist of sequences of events, which can contain resources. The sequence of events is displayed in the main tab, and the resources of the selected event are listed in the Resources control.
Note that "event" in this context means application data, not an event that is processed by an event handler. Both meanings of the term are used in this discussion, and the meaning of "event" should be clear from the context.
For information about WPF support in ATF, see WPF Support.
The files EventSequenceView.xaml
and ResourceListView.xaml
define the EventSequenceView
and ResourceListView
controls. The contain the XAML and code behind for WPF controls that display the lists of events and resources associated with an event sequence. These also define behaviors and data bindings between the controls and application data.
SimpleDOMEditorWPF uses an XML Schema to define its data model of sequences of events with resources. Its schema loader also defines DOM adapters, palette items, and property descriptors that determine which properties appear in the property editors. The data model is identical to SimpleDOMEditor.
The application's PaletteClient
, along with the WPF version of ATF's PaletteService
, creates a palette of events and resource items.
SimpleDOMEditorWPF provides the DOM adapters Event
, EventSequence
, and Resource
for their corresponding types to provide properties that access information in a DomNode
of that type. These DOM adapters are nearly identical to ones in SimpleDOMEditor.
SimpleDOMEditorWPF relies heavily on contexts. ResourceListContext
handles resources belonging to the selected event and manages the Resources ResourceListView
editing. This context implements interfaces to handle displaying items, selection, and editing. EventSequenceContext
does a similar job for a sequence of events in the main window's ListView
.
EventSequenceDocument
extends DomDocument
, which implements IDocument
. The Editor
component is its document client, handling closing and saving documents.
The ResourceListEditor
component registers a "slave" ListView
control to display and edit resources that belong to the most recently selected event.
A lot of types have both WinForms and WPF versions. The WinForms versions are in-scope even for WPF applications, because their namespaces aren't segregated from the non-WinForms core GUI code. (That is, Atf.Gui.WinForms.dll
's namespaces are the same as those in Atf.Gui.dll
.) If you add a type to the MEF catalog and it doesn’t seem to be recognized, or you're seeing odd behavior at runtime such as typecasts that should succeed failing, make sure you're explicitly specifying the WPF version of the type.
For instance, the SchemaLoader
class has the following statement to explicitly specify the WPF version:
using NodeTypePaletteItem = Sce.Atf.Wpf.Models.NodeTypePaletteItem;
SimpleDOMEditorWPF's initialization is typical for a WPF application, as described in WPF Application in the ATF Application Basics and Services section. In particular, SimpleDOMEditorWPF includes App.xaml
and App.xaml.cs
files. For more details, see App XAML. From the basic application described there, this sample builds up its functionality by overriding two methods in AtfApp
: GetCatalog()
and OnCompositionBeginning()
.
GetCatalog()
builds on the simple example in App XAML by adding additional MEF components. Its TypeCatalog
contains basic components in the application shell framework, such as SettingsService
, FileDialogService
, CommandService
, and ControlHostService
. The WPF MainWindow
component handles the application's main window. SimpleDOMEditorWPF uses components to handle documents: DocumentRegistry
, RecentDocumentCommands
, StandardFileCommands
, StandardFileExitCommand
, and MainWindowTitleService
. Editing and context management are handled by ContextRegistry
, StandardEditCommands
, and StandardEditHistoryCommands
. How these common components function is discussed elsewhere in this Guide, such as Documents in ATF and ATF Contexts. Such components as SettingsService
, RecentDocumentCommands
, MainWindowTitleService
can simply be added to the TypeCatalog
, requiring no other modifications: they should "just work".
The PropertyEditor
component handles property editing. This is well integrated with the ATF DOM, so the application only needs to include this component to be able to edit properties of the application's data. For details on property editing, see Property Editing in ATF.
Note that some of these components are the WPF versions rather than WinForms versions, such as ControlHostService
, SettingsService
, CommandService
, FileDialogService
, StandardEditCommands
, PropertyEditor
, and HelpCommands
.
SimpleDOMEditorWPF adds a few custom components of its own:
-
PaletteClient
: populate the palette with the basic DOM types, with help from the WPF ATF componentPaletteService
. For details, see Using a Palette. -
Editor
: create and save event sequence documents. See Document Handling. -
SchemaLoader
: load the XML Schema for the data model as well as define property descriptors and palette items. See Application Data Model. -
EventSequenceContext
: track event sequence contexts and controls that display event sequences. See Working With Contexts. -
ResourceListContext
: track resource contexts and controls that display resource lists. See Working With Contexts. -
ResourceListEditor
: display resources that belong to the most recently selected event. See ResourceListEditor Component.
OnCompositionBeginning()
method primarily adds Help capability:
protected override void OnCompositionBeginning()
{
base.OnCompositionBeginning();
var helpCommands = Container.GetExportedValueOrDefault<HelpCommands>();
if (helpCommands != null)
{
// We don't have any context sensitive help, so disable these options.
helpCommands.ShowContextHelp = false;
helpCommands.EnableContextHelpUserSetting = false;
// Set the URL for the sample app documentation.
helpCommands.HelpFilePath = @"https://github.com/SonyWWS/ATF/wiki/ATF-Simple-DOM-Editor-WPF-Sample";
}
}
HelpCommands
is the ATF component for WPF that provides main application help command and context specific context menu help commands. Note that the HelpFilePath
property is set to a URL. The result is that this URL is displayed in a Web browser when the user selects the menu item Help > Contents.
SimpleDOMEditorWPF defines two controls in XAML for lists of events and resources, EventSequenceView
and ResourceListView
, in the files EventSequenceView.xaml
and ResourceListView.xaml
. Both controls and their definitions are very similar.
The EventSequenceView
control displays a sequence of events in the file being edited. EventSequenceView
is a UserControl
containing a Grid
which contains a ListView
, as seen in these lines from EventSequenceView.xaml
that set up the control's layout:
<UserControl x:Class="SimpleDomEditorWpfSample.EventSequenceView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
...>
...
<Grid>
<ListView ...>
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn Header="Name">
...
<GridViewColumn Header="Duration">
...
Two columns, "Name" and "Duration", are defined.
Because EventSequenceView
is a UserControl
, it ultimately derives from
FrameworkElement
. FrameworkElement
has the DataContext
dependency property that defines the control's Source
, which references the Binding's data source.
EventSequenceContext.OnNodeSet()
sets DataContext
for the EventSequenceView
. Here, m_view
holds the EventSequenceView
instance:
m_view.DataContext = this;
The XAML also defines data binding for the ListView
:
<ListView x:Name="m_listView" ItemsSource="{Binding Events}"
SelectedItem="{Binding BindableSelection, Converter={StaticResource SelectionConverter}}">
We just saw that the Source
for the EventSequenceView
's ListView
is EventSequenceContext
. The XAML binding above sets the Path
for the ListView
's data binding to indicate which property on the source object to get and set the bound data value. It sets Path
to the Events
property of EventSequenceView
. As a result, the EventSequenceView
's ListView
displays EventSequenceContext.Events
.
EventSequenceContext
defines a BindableSelection
property:
/// <summary>
/// Exposes the Selection for two way data binding. Needed because the ISelectionContext.Selection
/// property is read-only.</summary>
public IEnumerable<object> BindableSelection
{
get { return this.As<ISelectionContext>().Selection; }
set { this.As<ISelectionContext>().SetRange(value); }
}
The second part of the XAML binding refers to this property:
SelectedItem="{Binding BindableSelection, Converter={StaticResource SelectionConverter}}">
Setting the binding this way means that when the user selects an event in the list, BindableSelection
's set()
method is called, and so we set the ISelectionContext
's selection so all the appropriate ATF selection changed events are raised, and so the other views get updated. Thus the appropriate list shows up in the Resources window's ListView
and the PropertyGrid
in the Property window shows the properties of the selected event.
ISelectionContext.Selection
itself is read-only, so the BindableSelection
wrapper property was added to call SetRange()
instead.
SelectionConverter
is an IValueConverter
that just handles the data type conversion between the displayed ListView.SelectedItem
and the ISelectionContext.Selection
. It is defined in SelectionConverter.cs.
and has very basic implementations of Convert()
and ConvertBack()
.
Finally, look at the bindings for the cell in the GridView
cell that displays the data for a column:
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
As previously seen, the EventSequenceView
's ListView
displays EventSequenceContext.Events
— which is a list of Event
objects. Each row in the ListView
displays a single Event
. And the binding above is to the Name
property of the DOM adapter class Event
, so the value of the Event
's Name
appears in the cell.
There is a similar binding for the Event.Duration
property.
Behaviors are also defined for the ListView
. Note that the namespace i
is defined xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
at the beginning of the XAML file:
<ListView x:Name="m_listView" ItemsSource="{Binding Events}"
SelectedItem="{Binding BindableSelection, Converter={StaticResource SelectionConverter}}">
<i:Interaction.Behaviors>
<behaviors:InstancingDropTargetBehavior/>
<behaviors:ContextMenuBehavior/>
</i:Interaction.Behaviors>
Interaction.Behaviors
is an attached property holding a list of attached behaviors: InstancingDropTargetBehavior
and ContextMenuBehavior
, which are defined in ATF to handle drag and drop and context menu behaviors. For instance, InstancingDropTargetBehavior
implements IDataObject
to handle setting data after a drag and drop. InstancingDropTargetBehavior
derives from the ATF class DropTargetBehavior<T>
, which defines basic drag and drop functions and itself derives from Behavior<T>
. SimpleDOMEditorWPF differs from SimpleDOMEditor by relying on WPF to handle drag and drop behavior, instead of implementing drag and drop directly.
This section follows through how ContextMenuBehavior
operates for this sample.
After the sample application starts, OnAttached()
is called. This method overrides the method in System.Windows.Interactivity.Behavior
and is called when InitializeComponent()
is called for the WPF controls:
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseRightButtonUp += Element_MouseRightButtonUp;
}
AssociatedObject
here is a System.Windows.Controls.ListView
, so Element_MouseRightButtonUp()
is called on MouseRightButtonUp
events in the ListView
.
OnAttached()
is called multiple times: first for the ListView
in the ResourceListView
, and then for the ListView
in the EventSequenceView
control each time this control is created to display an event sequence document.
A user right-clicking in the ListView
triggers the Element_MouseRightButtonUp()
call:
private void Element_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
object context = GetCommandContext(sender, e);
object clickedData = GetCommandTarget(sender, e);
if (context != null && clickedData != null)
{
...
}
}
This method first gets a context for the event with GetCommandContext()
:
protected virtual object GetCommandContext(object sender, MouseButtonEventArgs e)
{
var senderFwe = (FrameworkElement)sender;
object context = Context;
if (context == null)
context = senderFwe.DataContext;
return context;
}
The variable sender
holds the ListView
clicked on in the EventSequenceView
control. Context
is the DependencyProperty
for ContextMenuBehavior
and is null
in this case. The returned context is the DataContext
for EventSequenceView
, which is EventSequenceContext
, as noted in Data Binding.
Next, the event handler calls GetCommandTarget()
to obtain the target item that was right-clicked:
protected virtual object GetCommandTarget(object sender, MouseButtonEventArgs e)
{
// Default behavior to find command target is to just take the data context
// of the clicked element
object clickedData = null;
var originalSource = e.OriginalSource as DependencyObject;
if (originalSource != null)
{
var fwe = originalSource.FindAncestor<FrameworkElement>();
if (fwe != null)
clickedData = fwe.DataContext;
}
return clickedData;
}
OriginalSource
is a System.Windows.Controls.TextBlock
here and this is also returned as the ancestor by FindAncestor<T>()
. Its DataContext
is SimpleDomEditorWpfSample.Event
, the DOM adapter for event objects.
Because both context
and clickedData
are non-null
, the next block of Element_MouseRightButtonUp()
is executed:
if (context != null && clickedData != null)
{
var service = Composer.Current.Container.GetExportedValueOrDefault<IContextMenuService>();
if (service != null)
{
var providers = Composer.Current.Container.GetExportedValues<Atf.Applications.IContextMenuCommandProvider>();
IEnumerable<object> commands =
Atf.Applications.ContextMenuCommandProvider.GetCommands(
providers,
context,
clickedData);
service.RunContextMenu(commands, (FrameworkElement)sender, e.GetPosition((FrameworkElement)sender));
e.Handled = true;
}
}
GetExportedValueOrDefault<T>()
sets service
to an Sce.Atf.Wpf.Interop.ContextMenuService
object. Then GetExportedValues<>()
obtains a list of context menu providers: Sce.Atf.Wpf.Applications.StandardEditCommands
and Sce.Atf.Wpf.Applications.HelpCommands
.
Atf.Applications.ContextMenuCommandProvider.GetCommands()
uses the list of providers to get an enumeration of commands for the context menu. Note that it takes the target's context
and target item (clickedData
) as parameters. In this context, HelpCommands
doesn't apply, so only commands from StandardEditCommands
are added to the enumeration.
Finally, the event handler uses the ContextMenuService
to call RunContextMenu()
, which displays a context menu listing menu items from the command enumeration.
The ResourceListView
control displays a list of resources associated with an event. ResourceListView
is almost identical to EventSequenceView
. The XAML files differ only in the names of the control and the column names.
This application creates and edits event sequences. An event has attributes, such as duration, and can contain one or more resources, such as animations. The sample defines its data model in the XML Schema Definition (XSD) language in the type definition file eventSequence.xsd
. This figure shows the Visual Studio XML Schema Explorer view of the sample's data definition:
This tree view shows that an "Event", eventType
, contains a "Resource", resourceType
, and has "name", "time", and "duration" attributes. The XML Schema for this type definition shows a different view of the same thing:
<!--Event, with name, start time, duration and a list of resources-->
<xs:complexType name ="eventType">
<xs:sequence>
<xs:element name="resource" type="resourceType" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string"/>
<xs:attribute name="time" type="xs:integer"/>
<xs:attribute name="duration" type="xs:integer"/>
</xs:complexType>
The data model has a "Resource" type, and the types "Animation" and "Geometry" are based on "Resource". "Resource" types have the attributes "name", "size", and "compressed".
An event sequence is simply a sequence of any number of events, as this type definition shows:
<!--Event sequence, a sequence of events-->
<xs:complexType name ="eventSequenceType">
<xs:sequence>
<xs:element name="event" type="eventType" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
Note this definition for the root element:
<!--Declare the root element of the document-->
<xs:element name="eventSequence" type="eventSequenceType"/>
The root element is of "EventSequence" type, because a document contains one event sequence. This is very important, because this type has several DOM adapters defined for it, which can apply to the entire document.
The ATF DomGen utility generated a Schema
class file from the type definition file. The file GenSchemaDef.bat
contains commands for DomGen.
Schema
contains subclasses for all the data types and fields for the attributes that are of the appropriate ATF DOM metadata classes.
For example, the "Resource" type has three AttributeInfo
fields for its "name", "size", and "compressed" attributes:
public static class resourceType
{
public static DomNodeType Type;
public static AttributeInfo nameAttribute;
public static AttributeInfo sizeAttribute;
public static AttributeInfo compressedAttribute;
}
The "Event" type also has AttributeInfo
fields for its attributes, plus a ChildInfo
field for any resources contained in the object:
public static class eventType
{
public static DomNodeType Type;
public static AttributeInfo nameAttribute;
public static AttributeInfo timeAttribute;
public static AttributeInfo durationAttribute;
public static ChildInfo resourceChild;
}
Even though an event may contain any number of resources, it needs only one ChildInfo
object, because in the ATF DOM, a node can have either a single child or a single list of children in the DOM node tree.
The SchemaLoader
component loads the XML schema and does any other initialization required for the data model. It derives from XmlSchemaTypeLoader
, which performs a substantial part of the schema loading process.
Its constructor, which is automatically called during MEF initialization when the component object is created, resolves the type definition file address and loads the file:
public SchemaLoader()
{
// set resolver to locate embedded .xsd file
SchemaResolver = new ResourceStreamResolver(Assembly.GetExecutingAssembly(), "SimpleDomEditorWpfSample/schemas");
Load("eventSequence.xsd");
}
SchemaLoader
constructors nearly always take this form. Other standard items are the NameSpace
and TypeCollection
properties:
public string NameSpace
{
get { return m_namespace; }
}
private string m_namespace;
...
public XmlSchemaTypeCollection TypeCollection
{
get { return m_typeCollection; }
}
private XmlSchemaTypeCollection m_typeCollection;
The other work to do is to define the method OnSchemaSetLoaded()
, which is called after the schema set has been loaded and the DomNodeType
objects have been created. This method accomplishes several things, described in the following sections.
The schema loader calls Schema.Initialize()
to initialize the Schema
class. This method was also created by DomGen when it generated the rest of the metadata classes.
The loader defines DOM extensions for data types, so that methods in the various DOM adapters are called appropriately:
Schema.eventSequenceType.Type.Define(new ExtensionInfo<EventSequenceDocument>());
Schema.eventSequenceType.Type.Define(new ExtensionInfo<EventSequenceContext>());
Schema.eventSequenceType.Type.Define(new ExtensionInfo<MultipleHistoryContext>());
Schema.eventSequenceType.Type.Define(new ExtensionInfo<EventSequence>());
Schema.eventSequenceType.Type.Define(new ExtensionInfo<ReferenceValidator>());
Schema.eventSequenceType.Type.Define(new ExtensionInfo<UniqueIdValidator>());
Schema.eventSequenceType.Type.Define(new ExtensionInfo<DomNodeQueryable>());
Schema.eventType.Type.Define(new ExtensionInfo<Event>());
Schema.eventType.Type.Define(new ExtensionInfo<ResourceListContext>());
Schema.resourceType.Type.Define(new ExtensionInfo<Resource>());
Note that the types used here are the ones defined in the Schema
class.
The first group's definitions all apply to Schema.eventSequenceType
, which is the type of the document's root. These DOM adapters thus apply to the entire document and accomplish a number of purposes. A document describes a single sequence of events, so its DomNode
tree has only one node of type "EventSequence", the tree root DomNode
. This root node can be adapted to all of the adapters listed above. For instance, defining both EventSequenceDocument
and EventSequenceContext
as extensions of eventSequenceType
makes it possible to convert between these types using As<>
. For more details, see Working With Contexts.
The Schema.eventType
and Schema.resourceType
types also have DOM adapters. For more information on these adapters, see DOM Adapters.
The schema loader enables metadata driven property editing for events and resources by creating an AdapterCreator
:
var creator = new AdapterCreator<CustomTypeDescriptorNodeAdapter>();
Schema.eventType.Type.AddAdapterCreator(creator);
Schema.resourceType.Type.AddAdapterCreator(creator);
If this were not done, the property editors would not show properties from the property descriptors, defined later on.
This step adds tag information to the type that is used later to add these types to the palette: "Event", "Animation", and "Geometry". The palette item information is encapsulated in a NodeTypePaletteItem
object, a container class:
Schema.eventType.Type.SetTag(
new NodeTypePaletteItem(
Schema.eventType.Type,
"Event".Localize(),
"Event in a sequence".Localize(),
"Events".Localize(),
Resources.EventImage));
The NodeTypePaletteItem
contains all the information needed to display and use the palette item: its type, name, descriptive text, category, and an icon image to appear on the palette and in the ListView
controls.
For further details on how this sets up the palette, see Using a Palette.
Property descriptors for types specify the data that shows up in property editors for the types. For details on how this works, see Property Editing in ATF and Property Descriptors in particular.
The NamedMetadata.SetTag()
method used below creates a PropertyDescriptorCollection
containing an AttributePropertyDescriptor
for each attribute of an "Event" type. This information is required for each attribute to appear in a property editor when an event is selected in the main ListView
. The AdapterCreator
must also be set up, as described previously in Metadata Driven Property Editing.
Schema.eventType.Type.SetTag(
new PropertyDescriptorCollection(
new PropertyDescriptor[] {
new AttributePropertyDescriptor(
"Name".Localize(),
Schema.eventType.nameAttribute,
null,
"Event name".Localize(),
false),
new AttributePropertyDescriptor(
"Time".Localize(),
Schema.eventType.timeAttribute,
null,
"Event starting time".Localize(),
false),
new AttributePropertyDescriptor(
"Duration".Localize(),
Schema.eventType.durationAttribute,
null,
"Event duration".Localize(),
false),
}));
SimpleDOMEditorWPF uses the WPF version of the ATF PaletteService
component, which manages a palette of objects that can be dragged on to other controls. SimpleDOMEditorWPF allows dragging palette items onto the two ListView
controls: the main ListView
for events and the Resources ListView
for resources of the selected event. PaletteService
's constructor creates a PaletteContent
view model for the palette and registers it with the ControlHostService
component, placing the control on the left side of the main window.
SimpleDOMEditorWPF adds its own PaletteClient
component that imports IPaletteService
, which is satisfied by the PaletteService
component. The IInitializable.Initialize()
method for the PaletteClient
component adds items to the palette:
void IInitializable.Initialize()
{
NodeTypePaletteItem eventTag = Schema.eventType.Type.GetTag<NodeTypePaletteItem>();
if (eventTag != null)
m_paletteService.AddItem(eventTag, eventTag.Category, this);
foreach (DomNodeType resourceType in m_schemaLoader.GetNodeTypes(Schema.resourceType.Type))
{
NodeTypePaletteItem resourceTag = resourceType.GetTag<NodeTypePaletteItem>();
if (resourceTag != null)
m_paletteService.AddItem(resourceTag, resourceTag.Category, this);
}
}
This method adds items to the palette with the IPaletteService.AddItem()
method, defined as:
void AddItem(object item, string categoryName, IPaletteClient client);
In the first call to AddItem()
, the type for an event is added, Schema.eventType.Type
.
The foreach
loop iterates through all the types for the "Resource" type, Schema.resourceType.Type
. The types "Animation" and "Geometry" are both based on the "Resource" type. For each of these "Resource" types, the loop checks that it can get a NodeTypePaletteItem
for the type:
NodeTypePaletteItem resourceTag = resourceType.GetTag<NodeTypePaletteItem>();
if (resourceTag != null)
m_paletteService.AddItem(resourceTag, resourceTag.Category, this);
Recall that in the SchemaLoader
class, this kind of initialization occurs to add information for each "Resource" type for the palette:
string resourcesCategory = "Resources".Localize();
Schema.animationResourceType.Type.SetTag(
new NodeTypePaletteItem(
Schema.animationResourceType.Type,
"Animation".Localize(),
"Animation resource".Localize(),
resourcesCategory,
Resources.AnimationImage));
The call to GetTag()
above simply retrieves the above NodeTypePaletteItem
information, if present. After it verifies that the type has an associated NodeTypePaletteItem
tag, Initialize()
adds the type to the palette.
Looping in this way guarantees that all resource types are added to the palette and makes adding new resource types to the palette later easier.
The IPaletteClient.GetInfo()
method gets display information for the item. It retrieves the data from the NodeTypePaletteItem
that was set in SchemaLoader
:
void IPaletteClient.GetInfo(object item, ItemInfo info)
{
NodeTypePaletteItem paletteItem = item.As<NodeTypePaletteItem>();
if (paletteItem != null)
{
info.Label = paletteItem.Name;
info.Description = paletteItem.Description;
}
}
Convert()
takes a palette item and returns an object that can be inserted into an IInstancingContext
— a DomNode
in this case:
object IPaletteClient.Convert(object item)
{
DomNode node = null;
NodeTypePaletteItem paletteItem = item.As<NodeTypePaletteItem>();
if (paletteItem != null)
{
DomNodeType nodeType = paletteItem.NodeType;
node = new DomNode(nodeType);
if (nodeType.IdAttribute != null)
node.SetAttribute(nodeType.IdAttribute, paletteItem.Name); // unique id, for referencing
if (nodeType == Schema.eventType.Type)
node.SetAttribute(Schema.eventType.nameAttribute, paletteItem.Name);
else if (Schema.resourceType.Type.IsAssignableFrom(nodeType))
node.SetAttribute(Schema.resourceType.nameAttribute, paletteItem.Name);
}
return node;
}
The item
parameter in Convert()
is a selected item in the palette. This method first verifies that it can adapt the given item
to a NodeTypePaletteItem
. If so, it obtains the DomNodeType
of the type associated with the palette item and creates a new DomNode
of that DomNodeType
.
Next, the IdAttribute
of the DomNode
is set, if it wasn't already, with the DomNode.SetAttribute()
method:
public void SetAttribute(AttributeInfo attributeInfo, object value);
Finally, it sets the name attribute of the DomNode
. The type of item is checked, so that the proper nameAttribute
is set.
DOM adapters allow a DomNode
to be dynamically cast to another interface. SimpleDOMEditorWPF provides the DOM adapters Event
, EventSequence
, and Resource
for their corresponding types. All these adapters do is provide properties that access information in a DomNode
. For instance, Event
has a Name
property:
/// Gets or sets name associated with event, such as a label
public string Name
{
get { return GetAttribute<string>(Schema.eventType.nameAttribute); }
set { SetAttribute(Schema.eventType.nameAttribute, value); }
}
The DomNodeAdapter.GetAttribute()
method gets the value of a specified attribute, Schema.eventType.nameAttribute
, defined in the Schema
class as
public static AttributeInfo nameAttribute;
Most of the DOM adapter properties simply access attribute values of the type, but the EventSequence
DOM adapter's Events
property gets all the events in an event sequence:
/// Gets list of Events in sequence
public IList<Event> Events
{
get { return GetChildList<Event>(Schema.eventSequenceType.eventChild); }
}
Note that the returned property value is an IList<Event>
, that is, a list of the DOM adapter Event
objects associated with an event sequence DOM adapter EventSequence
.
The DOM adapters in this application provide a simple way to get properties for the event, event sequence, and resource objects, all embodied in DomNode
objects.
SimpleDOMEditorWPF relies heavily on contexts. A context provides services for operations in certain situations, hence the name context. ATF provides quite a few interfaces and classes for different types of contexts, and this sample uses several. For information about contexts in general, see ATF Contexts.
This section illustrates how contexts control and facilitate editing operations in the main and Resources ListView
controls.
ResourceListContext
provides the editing context for the ResourceListView
in the Resources window, manages DOM events, and handles resources belonging to the selected event. This class adapts the application data to a list and uses contexts to enable data editing as well as undoing and redoing data changes. In this respect, it is similar to the EventContext
class in the SimpleDOMEditor sample.
ResourceListContext
has this derivation ancestry: EditingContext
, HistoryContext
, TransactionContext
, and finally DomNodeAdapter
, so all these context classes are DOM adapters. EditingContext
is a history context with a selection, providing a basic self-contained editing context. Several samples, such as ATF Fsm Editor Sample and ATF State Chart Editor Sample have context classes that extend EditingContext
. For more information about this context, see General Purpose EditingContext Class.
As its declaration shows, ResourceListContext
also implements several interfaces:
public class ResourceListContext : EditingContext,
IObservableContext,
IInstancingContext,
IEnumerableContext,
INotifyPropertyChanged
The ancestor classes of EditingContext
also implement some useful interfaces, such as IHistoryContext
and ITransactionContext
, which allow changes in the context, such as deleting resources in an event, to be undone and redone.
ResourceListContext
's constructor creates a new ResourceListView
object, in which resources are listed for the selected event.
Several useful properties are defined:
-
View
: Get and set theResourceListView
object. -
Items
: Get a list of the event'sResource
objects. -
Resources
: GetIEnumerable<Resource>
for the event's resources. -
BindableSelection
: Gets and sets the selection. It exposes the selection for two way data binding and is needed because theISelectionContext.Selection
property is read-only. This property is bound to theSelectedItem
inResourceListView.xaml
.
As a DOM adapter, ResourceListContext
's OnNodeSet()
method subscribes to several events for a DomNode
, such as ChildInserted
:
protected override void OnNodeSet()
{
...
DomNode.ChildInserted += DomNode_ChildInserted;
...
}
...
private void DomNode_ChildInserted(object sender, ChildEventArgs e)
{
Resource resource = e.Child.As<Resource>();
if (resource != null)
{
ItemInserted.Raise(this, new ItemInsertedEventArgs<object>(e.Index, resource));
}
OnPropertyChanged();
}
...
private void OnPropertyChanged()
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Resources"));
}
}
If this handler can adapt the child DomNode
e.Child
to the Resource
DOM adapter, it raises the ItemInserted
event, which is defined in the IObservableContext
interface.
The other event handlers defined deal with Resource
objects, too, in a similar way.
This WPF interface notifies clients that a property value has changed for the WPF property system. It contains the PropertyChanged
event, which should be raised any time a property changes. Numerous functions in ResourceListContext
raise this event.
IInstancingContext
handles instancing in the Resources ListView
, that is, working with resource instances in this control, which can be edited using menu items and by drag and drop. You insert resources in the Resources ListView
for an event by dragging and dropping a resource from the palette onto the Resources ListView
. Drag and drop behavior is governed by the InstancingDropTargetBehavior
WPF behavior class.
The IInstancingContext
interface provides methods to check whether items can be copied, inserted (pasted), or deleted, and performs these actions, too.
Here are the copy related methods:
public bool CanCopy()
{
return Selection.Count > 0;
}
...
public object Copy()
{
IEnumerable<DomNode> resources = Selection.AsIEnumerable<DomNode>();
List<object> copies = new List<object>(DomNode.Copy(resources));
return new DataObject(copies.ToArray());
}
Selection
is defined in the EditingContext
class and is an AdaptableSelection
representing a collection of selected objects; this class handles basic selection mechanics. CanCopy()
uses Selection
to simply verify that there is at least one selected item. Copy()
adapts the selection to a collection of DomNode
s, copies them as a list of object
s, and then constructs a System.Windows.Forms.DataObject
from the copy, which can be placed on the Windows® clipboard. The StandardEditCommands
component actually calls Copy()
and puts the copied data onto the clipboard.
CanInsert()
and Insert()
do the following:
public bool CanInsert(object insertingObject)
{
IDataObject dataObject = (IDataObject)insertingObject;
object[] items = dataObject.GetData(typeof(object[])) as object[];
if (items == null)
return false;
foreach (object item in items)
if (!Adapters.Is<Resource>(item))
return false;
return true;
}
...
public void Insert(object insertingObject)
{
IDataObject dataObject = (IDataObject)insertingObject;
object[] items = dataObject.GetData(typeof(object[])) as object[];
if (items == null)
return;
DomNode[] itemCopies = DomNode.Copy(Adapters.AsIEnumerable<DomNode>(items));
IList<Resource> resources = this.Cast<Event>().Resources;
foreach (Resource resource in Adapters.AsIEnumerable<Resource>(itemCopies))
resources.Add(resource);
Selection.SetRange(itemCopies);
}
The first part of both methods is identical, simply verifying that there are items to insert.
StandardEditCommands
calls CanInsert()
to determine whether the Edit > Insert menu item is enabled or not. CanInsert()
casts the inserted data as a IDataObject
and then casts it to an array of selected object
s, each of which is a DomNode
of type "Resource". The second part of CanInsert()
checks whether all the selected items can be adapted as Resource
objects. If so, the method returns true, and false otherwise.
For the insert operation, StandardEditCommands
gets the clipboard data and calls Insert()
to insert this data. Insert()
casts the inserted data as a IDataObject
and then casts it to an array of selected object
s, just as for CanInsert()
. These object
s are adapted to DomNode
s and then copied.
In this method, this
represents a ResourceListContext
object, which is an adapted DomNode
of type "Event" representing a selected event in the main ListView
. Both ResourceListContext
and Event
are DOM adapters for the "Event" type, as shown in these lines from SchemaLoader
where DOM adapters are defined:
Schema.eventType.Type.Define(new ExtensionInfo<Event>());
Schema.eventType.Type.Define(new ExtensionInfo<ResourceListContext>());
Because both of the DOM adapters are defined for the "Event" type, DomNode
s of these types can be adapted to each other. So this DomNode
can be adapted to an Event
object, and its list of resources can be obtained from the Resources
property of the Event
DOM adapter:
IList<Resource> resources = this.Cast<Event>().Resources;
Finally, each inserted DomNode
is adapted to a Resource
object and added to the list of resources for the selected Event
in the main ListView
.
The deletion operations are simpler:
public bool CanDelete()
{
return Selection.Count > 0;
}
...
public void Delete()
{
List<DomNode> nodesToRemove = new List<DomNode>();
foreach (DomNode node in Selection.AsIEnumerable<DomNode>())
nodesToRemove.Add(node);
foreach (DomNode node in nodesToRemove)
node.RemoveFromParent();
Selection.Clear();
OnPropertyChanged();
}
CanDelete()
is identical to CanCopy()
, simply verifying that there's something selected to delete.
Delete()
iterates all selected items' underlying DomNode
s and adds them to a temporary list. It then removes each one from the parent with DomNode.RemoveFromParent()
, effectively removing them from the application data. It also clears the selection using the Selection.Clear()
method. Finally it calls OnPropertyChanged()
to raise the PropertyChanged
event so displays are updated.
EventSequenceContext
handles the logic of document related events and passes them on to the EventSequenceView
, which lists a sequence of events.
EventSequenceContext
has many similarities to ResourceListContext
, including its derivation ancestry from EditingContext
. One of the differences is that EventSequenceContext
's constructor creates an EventSequenceView
instance used to display events, while ResourceListContext
uses the ResourceListView
defined in ResourceListEditor
. This is because there can be multiple EventSequenceDocument
s open, each requiring its own EventSequenceView
, but there is only ever one ResourceListEditor
open, so it can maintain a single ResourceListView
.
EventSequenceContext
implements all the interfaces that ResourceListContext
does. Their implementations are very similar, but Resource
objects are replaced by Event
objects in EventSequenceContext
. The IInstancingContext
implementations look identical except for that substitution, for example.
One other important difference from ResourceListContext
is that EventSequenceContext
's OnNodeSet()
method does this:
m_view.DataContext = this;
The field m_view
contains the EventSequenceView
. Setting its DataContext
dependency property to this
, i.e., the EventSequenceContext
, means that the Source
dependency property for the EventSequenceView
is the EventSequenceContext
object.
Several useful properties are defined:
-
ControlInfo
: Gets and sets basic info about the hosted control such as its name and dock location. -
Document
: Gets and sets the DOM document being displayed. -
View
: Get theEventSequenceView
object. -
Items
: Get a list of theEvent
objects. -
Events
: GetIEnumerable<Event>
for the events. -
BindableSelection
: Gets and sets the selection. It exposes the selection for two way data binding and is needed because theISelectionContext.Selection
property is read-only. This property is bound to theSelectedItem
inEventSequenceView.xaml
.
Document handling applications typically implement both IDocument
for a document object and IDocumentClient
for the document client.
EventSequenceDocument
represents document data and extends DomDocument
, which implements IDocument
.
The DomDocument
class provides the basics of a document: IsReadOnly
and Dirty
properties, a DirtyChanged
event, and the OnDirtyChanged()
and OnReloaded
event handlers. DomDocument
extends DomResource
and implements IResource
to provide the resource properties Type
and Uri
to describe a document's type and location.
EventSequenceDocument
itself implements overrides for the property Type
and the methods OnUriChanged()
and OnDirtyChanged()
for application-specific behavior. These latter event handlers simply execute the base methods, as well as update ControlInfo
for the ListView
displaying the new document. ControlInfo
holds information about controls hosted by ControlHostService
.
The Editor
component tracks documents with the DocumentRegistry
, registers the editor view control EventSequenceView
with the ControlHostService
, and tracks the active context for the ContextRegistry
. Editor
is the client for event sequence documents.
Editor
uses the DocumentClientInfo
class to hold document editor information. The Info
property gets the DocumentClientInfo
from a static
variable:
public DocumentClientInfo Info
{
get { return DocumentClientInfo; }
}
/// <summary>
/// Information about the document client</summary>
public static DocumentClientInfo DocumentClientInfo = new DocumentClientInfo(
"Event Sequence".Localize(),
new string[] { ".xml", ".esq" },
Sce.Atf.Resources.DocumentImage,
Sce.Atf.Resources.FolderImage,
true);
CanOpen()
simply checks that the filename extension is suitable, using the DocumentClientInfo
:
public bool CanOpen(Uri uri)
{
return DocumentClientInfo.IsCompatibleUri(uri);
}
The Open()
method reads the document file's data and converts it into a tree of DomNode
objects. After that, it sets up the context and other information needed for the new document. The initial phase of opening creates a DomNode
for the tree root and gets the file name:
public IDocument Open(Uri uri)
{
DomNode node = null;
string filePath = uri.LocalPath;
string fileName = Path.GetFileName(filePath);
For an existing document, a FileStream
is created and read. Because the document is stored as an XML document, you can use an DomXmlReader
to read data and convert it to a tree of DomNode
s. DomXmlReader.Read()
returns the root DomNode
of the tree it creates. For a new document, a DomNode
with the DomNodeType
of the event sequence root element — from the Schema
class — is created.
if (File.Exists(filePath))
{
// read existing document using standard XML reader
using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
DomXmlReader reader = new DomXmlReader(m_schemaLoader);
node = reader.Read(stream, uri);
}
}
else
{
// create new document by creating a Dom node of the root type defined by the schema
node = new DomNode(Schema.eventSequenceType.Type, Schema.eventSequenceRootElement);
}
Next, Open()
performs a series of ATF DOM set up operations to complete the open process:
EventSequenceDocument document = null;
if (node != null)
{
// Initialize Dom extensions now that the data is complete
node.InitializeExtensions();
EventSequenceContext context = node.As<EventSequenceContext>();
ControlInfo controlInfo = new ControlInfo(Path.Combine(filePath, fileName),
StandardControlGroup.Center,
new DockContent(null, null), this);
context.ControlInfo = controlInfo;
// set document URI
document = node.As<EventSequenceDocument>();
document.Uri = uri;
context.Document = document;
// show the document editor
// This line requires references to System.Drawing and System.Windows.Forms. Would really like to remove those dependencies!
m_controlHostService.RegisterControl(context.View,
fileName,
"Event sequence document",
StandardControlGroup.Center,
Path.Combine(filePath, fileName),
this);
}
return document;
First, a null
EventSequenceDocument
object is created.
InitializeExtensions()
initializes all the DOM adapters defined in the schema loader, shown in Define DOM Extensions.
An EventSequenceContext
is created by adapting the root DomNode
to EventSequenceContext
, which is a DOM adapter for the "EventSequence" type. Recall that the "EventSequence" type is the type of the document's root.
The event sequence document's data is going to be displayed in a EventSequenceView
. A ControlInfo
is set up for this control and is placed in the EventSequenceContext
ControlInfo
property.
EventSequenceDocument
is also a DOM adapter for the "EventSequence" type, so the tree root DomNode
can be adapted to EventSequenceDocument
.
The document
is saved in the context's Document
property for future reference:
context.Document = document;
Finally, the EventSequenceView
control is registered with the ControlHostService
component.
Save()
uses the capabilities of DomXmlWriter
to write the DomNode
tree to an XML file. DomXmlWriter
gets data model information from the schema so it knows how to write the DomNode
tree to XML with its Write()
method. The document is cast back to an EventSequenceDocument
to get its root DomNode
for Write()
.
public void Save(IDocument document, Uri uri)
{
string filePath = uri.LocalPath;
FileMode fileMode = File.Exists(filePath) ? FileMode.Truncate : FileMode.OpenOrCreate;
using (FileStream stream = new FileStream(filePath, fileMode))
{
DomXmlWriter writer = new DomXmlWriter(m_schemaLoader.TypeCollection);
EventSequenceDocument eventSequenceDocument = (EventSequenceDocument)document;
writer.Write(eventSequenceDocument.DomNode, stream, uri);
}
}
Closing the document entails cleaning up the contexts that were used:
public void Close(IDocument document)
{
EventSequenceContext context = Adapters.As<EventSequenceContext>(document);
m_controlHostService.UnregisterContent(context.View);
context.ControlInfo = null;
// close all active EditingContexts in the document
foreach (DomNode node in context.DomNode.Subtree)
foreach (EditingContext editingContext in node.AsAll<EditingContext>())
m_contextRegistry.RemoveContext(editingContext);
// close the document
m_documentRegistry.Remove(document);
}
The EventSequenceView
control is unregistered.
Finally, any EditingContext
s that were created are removed from the ContextRegistry
, and the document is removed from the DocumentRegistry
.
Contexts are discussed in Working With Contexts.
Editor
also implements IControlHostClient
for the EventSequenceView
. This requires it to handle the control's activation, deactivation, and close events.
The Activate()
method updates the document and context registries:
void IControlHostClient.Activate(Control control)
{
var view = control as EventSequenceView;
if (view != null)
{
var context = view.DataContext as EventSequenceContext;
if (context != null)
{
EventSequenceDocument document = context.Document;
if (document != null)
{
m_documentRegistry.ActiveDocument = document;
m_contextRegistry.ActiveContext = context;
}
}
}
}
It casts the control to EventSequenceView
, which is a Control
. Assuming this cast succeeds, it retrieves the EventSequenceContext
from the DataContext
property of the EventSequenceView
in which it was stored. Finally, it obtains the EventSequenceDocument
from the EventSequenceContext
's Document
property and sets it as the active document in the DocumentRegistry
component. It sets the EventSequenceContext
as the active context with the ContextRegistry
.
Close()
performs a similar operation to Activate()
, but with the aim of closing things out. It uses the IDocumentService
in the variable m_documentService
, provided by the StandardFileCommands
component in this sample, to close the document. If it succeeds, it then removes the context associated with the document from the ContextRegistry
.
bool IControlHostClient.Close(Control control)
{
bool closed = true;
var view = control as EventSequenceView;
if (view != null)
{
var context = view.DataContext as EventSequenceContext;
if (context != null)
{
EventSequenceDocument document = context.Document;
if (document != null)
{
closed = m_documentService.Close(document);
if (closed)
m_contextRegistry.RemoveContext(document);
}
}
}
return closed;
}
Close()
obtains the EventSequenceDocument
the same way that Activate()
does.
The ResourceListEditor
component creates the ResourceListView
and registers it with the ControlHostService
, and handles IControlHostClient
events and context changed events. It handles displaying resources in the ResourceListView
. Resources are edited by either dragging and dropping them from the palette onto a ListView
or deleting them from the ResourceListView
. You can also edit resources with context menus in the ResourceListView
. Resource attributes can also be edited, but that falls under property editing, which is all performed by the PropertyEditor
component.
Drag and drop behavior is governed by the InstancingDropTargetBehavior
WPF behavior class, and context menus by the ContextMenuBehavior
WPF behavior class. These functions are not handled in ResourceListEditor
.
Because the ResourceListEditor
shows the resources for the currently selected event, this component must track context changes. The ResourceListEditor
constructor sets up event handling for context changes, that is, tracking which ResourceListContext
is active:
m_contextRegistry.ActiveContextChanged += new EventHandler(contextRegistry_ActiveContextChanged);
The ActiveContextChanged
event handler, contextRegistry_ActiveContextChanged
, gets the current EventSequenceContext
and, if it is different from the previous EventSequenceContext
and non-null
, unsubscribes the previous context from the SelectionChanged
event and subscribes the new context to the event:
private void contextRegistry_ActiveContextChanged(object sender, EventArgs e)
{
// make sure we're always tracking the most recently active EventSequenceContext
EventSequenceContext context = m_contextRegistry.GetMostRecentContext<EventSequenceContext>();
if (m_eventSequenceContext != context)
{
if (m_eventSequenceContext != null)
{
m_eventSequenceContext.SelectionChanged -= new EventHandler(eventSequenceContext_SelectionChanged);
}
m_eventSequenceContext = context;
if (m_eventSequenceContext != null)
{
// track the most recently active EventSequenceContext's selection to get the most recently
// selected event.
m_eventSequenceContext.SelectionChanged += new EventHandler(eventSequenceContext_SelectionChanged);
}
UpdateEvent();
}
}
The UpdateEvent()
call updates the ResourceListContext
in the Context Registry. It also updates the ResourceListView
's DataContext
property (so the data source is updated) and sets the eventContext's View
property to the registered m_resourceListView
:
private void UpdateEvent()
{
Event nextEvent = null;
if (m_eventSequenceContext != null)
nextEvent = m_eventSequenceContext.Selection.GetLastSelected<Event>();
if (m_event != nextEvent)
{
// remove last event's editing context in case it was activated
if (m_event != null)
m_contextRegistry.RemoveContext(m_event.Cast<ResourceListContext>());
m_event = nextEvent;
// get next event's editing context and bind to resources list view
ResourceListContext eventContext = null;
if (nextEvent != null)
{
eventContext = nextEvent.Cast<ResourceListContext>();
eventContext.View = m_resourceListView;
}
m_resourceListView.DataContext = eventContext;
}
}
In this method, Event
refers to event data that is edited by this sample, not a subscribable event.
Because application data is in an ATF DOM, an event is represented by a DomNode
. Such a DomNode
can be adapted to both Event
and ResourceListContext
DOM node adapters, as indicated by this definition in the SchemaLoader
for the "Event" type:
Schema.eventType.Type.Define(new ExtensionInfo<Event>());
Schema.eventType.Type.Define(new ExtensionInfo<ResourceListContext>());
Both these adaptations are used in the UpdateEvent()
method. This method gets the last selected event item in the current EventSequenceContext
's ListView
as an Event
in this line:
nextEvent = m_eventSequenceContext.Selection.GetLastSelected<Event>();
If nextEvent
is different from the previous Event
, m_event
, then m_event
is adapted to ResourceListContext
and removed from the ContextRegistry
:
m_contextRegistry.RemoveContext(m_event.Cast<ResourceListContext>());
Finally, the last selected event, nextEvent
, is adapted to ResourceListContext
and used to update the ResourceListEditor
's ListView
:
if (nextEvent != null)
{
eventContext = nextEvent.Cast<ResourceListContext>();
eventContext.View = m_resourceListView;
}
m_resourceListView.DataContext = eventContext;
The last line sets the DataContext
property in the ResourceListView
. This makes the ResourceListContext
object the Source
dependency property for the ResourceListView
.
The IInitializable.Initialize()
function creates a new ResourceListView
and registers this control.
void IInitializable.Initialize()
{
m_resourceListView = new ResourceListView();
m_resourcesControlDef = new ControlDef()
{
Name = "Resources".Localize(),
Description = "Resources for selected Event".Localize(),
Group = StandardControlGroup.Bottom,
Id = s_resourceListEditorId.ToString()
};
m_controlHostService.RegisterControl(m_resourcesControlDef, m_resourceListView, this);
}
This interface mainly updates the active ResourceListContext
when the ResourceListView
is activated in Activate()
.
void IControlHostClient.Activate(object control)
{
// if our ListView has become active, make the last selected event
// the current context.
if (control == m_resourceListView)
{
if (m_event != null)
m_contextRegistry.ActiveContext = m_event.Cast<ResourceListContext>();
}
}
- Circuit Editor Programming Discussion: Learn how ATF handles graphs, and provides editors for kinds of graphs, such as circuits.
- Code Editor Programming Discussion: Shows how to interface third party software to an ATF application: the ActiproSoftware SyntaxEditor.
- Diagram Editor Programming Discussion: Very simply combines components from the CircuitEditor, FsmEditor, and StateChartEditor samples into one application, with the abilities of all three, showing the power of components.
-
DOM Property Editor Programming Discussion: Shows how to use the ATF DOM with an XML Schema to define application data with a large variety of attribute types, whose values can be viewed and edited using the ATF
PropertyEditor
component, using various value editors to view and edit attributes. - DOM Tree Editor Programming Discussion: Shows how to edit DOM data using a tree control and display properties in a variety of value editors.
- File Explorer Programming Discussion: Discusses the ATF File Explorer Sample using list and tree controls with adapters.
- FSM Editor Programming Discussion: Tells you about how the ATF FSM Editor Sample edits simple graphs for state machines, using DOM adapters for contexts and validation.
-
Model Viewer Programming Discussion: Shows how the ATF Model Viewer Sample is written, discussing how ATGI and Collada model data is handled, using rendering components, and using a
DesignControl
as a canvas for rendering. -
Simple DOM Editor Programming Discussion: Programming the ATF Simple DOM Editor Sample, creating a palette, using DOM adapters and contexts, editing application data, and searching
DomNode
s. - Simple DOM Editor WPF Programming Discussion: Programming the ATF Simple DOM Editor WPF Sample, which is similar to ATF Simple DOM Editor Sample, but implemented using ATF's WPF framework.
- Simple DOM No XML Editor Programming Discussion: Programming the ATF Simple DOM No XML Editor Sample, which is very similar to ATF Simple DOM Editor Sample, except that it doesn't use XML for either its data model or persisting application data.
- State Chart Editor Programming Discussion: Shows using ATF graph and other classes to create a statechart editor, using DOM adapters, documents, contexts, and validators.
- Target Manager Programming Discussion: Description of how a target manager is implemented using ATF components to manage target devices, such as PlayStation®Vita or PS3™ consoles. A target manager is used in other tools, such as the StateMachine tool.
- Timeline Editor Programming Discussion: Discusses how to create a fairly full-featured timeline editor using the ATF timeline facilities, such as the timeline renderer and the timeline control and its manipulators.
-
Tree List Control Programming Discussion: Demonstrates using the
TreeListControl
andTreeListItemRenderer
classes to display and edit hierarchical data in a tree view with details in columns. -
Tree List Editor Programming Discussion: Demonstrates how to use the ATF tree controls
TreeListView
and its enhancement,TreeListViewEditor
.TreeListView
usesTreeListViewAdapter
, which adaptsTreeListView
to display data in a tree. - Using Dom Programming Discussion: Shows how to use the various parts of the ATF DOM: an XML Schema, a schema metadata class file generated by DomGen, DOM adapters for the data types, a schema loader, and saving application data to an XML file.
- Home
- Getting Started
- Features & Benefits
- Requirements & Dependencies
- Gallery
- Technology & Samples
- Adoption
- News
- Release Notes
- ATF Community
- Searching Documentation
- Using Documentation
- Videos
- Tutorials
- How To
- Programmer's Guide
- Reference
- Code Samples
- Documentation Files
© 2014-2015, Sony Computer Entertainment America LLC