-
Notifications
You must be signed in to change notification settings - Fork 263
Tree List Editor Programming Discussion
The ATF Tree List Editor Sample demonstrates how to use the ATF tree controls TreeListView
and its enhancement, TreeListViewEditor
. TreeListView
uses TreeListViewAdapter
, which adapts TreeListView
to display data. TreeListEditor shows all styles of a TreeListView
as well as an unadorned TreeListView
, implementing them all as components.
TreeListEditor also demonstrates how to create data and display its properties in a PropertyEditor
component.
The CommonEditor
component is the base class for most of the tree editors in TreeListEditor. It shows how to use the TreeListViewEditor
class, which uses a TreeListView
with a TreeListViewAdapter
. CommonEditor
creates a UserControl
to hold the TreeListViewEditor
and related tool buttons and provides the control host client for the UserControl
. The tool buttons generate tree data and perform other functions that are handled by CommonEditor
's methods.
The RawTreeListView
component displays a folder's file hierarchy and operates somewhat like the CommonEditor
, but uses a TreeListView
.
TreeListEditor creates a data item class DataItem
derived from CustomTypeDescriptor
to display data in the trees. This also allows displaying data in a PropertyEditor
component. The DataContainer
class can contain a collection of DataItem
and implements ITreeListView
and other interfaces to allow viewing data in a tree. DataContainer
's methods also generate tree data.
The TreeListViewAdapter
wraps a TreeListView
and allows supplying data to the TreeListView
through its View
property, which implements the ITreeListView
interface. This View
property contains a DataContainer
object, which supplies what the TreeListViewAdapter
needs to display data.
TreeListEditor follows the standard WinForms initialization pattern discussed in WinForms Application. In addition to standard ATF components, it adds the following components of its own to the MEF TypeCatalog
:
-
List
: Add the standard list view editor, based on theCommonEditor
component. For details on this and the other editor components (exceptRawTreeListView
), see CommonEditor Component. -
CheckedList
: Add the checked list view editor, based on theCommonEditor
component. -
VirtualList
: Add the virtual list view editor, based on theCommonEditor
component. -
TreeList
: Add the tree list view editor, based on theCommonEditor
component. -
CheckedTreeList
: Add the checked tree list view editor, based on theCommonEditor
component. -
RawTreeListView
: Add a tree list editor for displaying a hierarchical file system. For details, see RawTreeListView Component.
The List
, CheckedList
, VirtualList
, TreeList
, and CheckedTreeList
components all derive from the CommonEditor
component, and these components all reside in the file Editor.cs
.
CommonEditor
is the common editor used for all the tree view editors. The other components' implementation is trivial, because all needed capability is provided in CommonEditor
.
CommonEditor
derives from TreeListViewEditor
:
class CommonEditor : TreeListViewEditor, IInitializable, IControlHostClient
TreeListViewEditor
adds extra functions to a TreeListView
, including a TreeListViewAdapter
and a right-click context edit menu. The TreeListView
class provides a tree with a list view. TreeListViewAdapter
wraps a TreeListView
and allows a user to supply data to it through the ITreeListView
interface. For more information on the operation of TreeListViewAdapter
with this sample, see TreeListViewAdapter Class.
CommonEditor
's constructor specifies a display name, tree list style, and flags indicating which buttons to display:
public CommonEditor(
string name,
TreeListView.Style style,
Buttons flags,
IContextRegistry contextRegistry,
ISettingsService settingsService,
IControlHostService controlHostService)
The constructor creates a UserControl
control to hold the TreeListViewEditor
plus any buttons desired.
The name
parameter specifies text to display in the user interface for the tree, which is on the window for the tree.
The TreeListView.Style
style
parameter is an enumeration specifying the tree style:
-
List
: A list. -
CheckedList
: A list with check boxes next to items. -
VirtualList
: A list supporting thousands of items, instantiated as needed. -
TreeList
: A tree with columns (default). -
CheckedTreeList
: A tree list with check-boxes next to the items.
The flags
parameter tells CommonEditor
which buttons to add to the tree control. The parameter is a flag bit mask, so it can specify any combination of buttons. Each button is created with the CreateButton()
method, which creates a new Button
with the desired text, sizing it to fit the text, and positioning it on the UserControl
. All the buttons have the same Click
event handler BtnClick()
, but have their Tag
property set to the Buttons
enumeration value for that button. The constructor invokes RegisterControl()
with the Control Host Registry for the UserControl
.
The BtnClick()
event handler uses Button.Tag
to determine what function to perform. Some of these functions generate data and place it in the tree. For a discussion of data generation, see Tree Data Generation.
CommonEditor
implements IControlHostClient
very simply for the UserControl
. Its Activate()
method just sets the active context to the TreeListViewEditor.View
property, which holds the ITreeListView
associated with data displayed in the control. For more information on data handling in the tree, see Editors Derived from CommonEditor and Tree Data Generation.
All the other tree editors derive from CommonEditor
. These other components do very little. Here's the entire CheckedList
definition, for example:
[ImportingConstructor]
public CheckedList(
IContextRegistry contextRegistry,
ISettingsService settingsService,
IControlHostService controlHostService)
: base(
"Checked List",
TreeListView.Style.CheckedList,
Buttons.AddFlat,
contextRegistry,
settingsService,
controlHostService)
{
TreeListView.NodeSorter =
new DataComparer(TreeListView);
View = new DataContainer();
TreeListView.NodeChecked += TreeListViewNodeChecked;
}
The constructor calls the base constructor to specify the editor name, to indicate it's a CheckedList
, and to specify that only the "Add Flat" button appears with this editor.
The TreeListView.NodeSorter
property specifies the way nodes are sorted in the TreeListView
. The TreeListView
property is in the TreeListViewEditor
and contains its TreeListView
. For information on the sorting class, see DataComparer Class.
The View
property is also in the TreeListViewEditor
and is a ITreeListView
indicating the data view for the TreeListView
. The DataContainer
class implements ITreeListView
and represents a set of data generated and placed in the tree. For details, see Tree Data Generation.
This constructor also specifies the handler for the TreeListView.NodeChecked
event:
private void TreeListViewNodeChecked(object sender, TreeListView.NodeCheckedEventArgs e)
{
Outputs.WriteLine(
OutputMessageType.Info,
"{0} {1}",
e.Node.Label,
e.Node.CheckState == CheckState.Checked
? "checked"
: "unchecked");
}
This method writes a message to the Output window in TreeListEditor. This window is created and handled by the Outputs
component, which provides static methods to access its Output window. TreeListEditor imports Outputs
.
RawTreeListView
creates the Raw TreeListView Usage window in TreeListEditor to display a folder's contents in a tree. In this respect, it has similarities to the ATF File Explorer Sample. That sample's FolderViewer
component displays the entire file hierarchy of the "C:" drive's contents as a tree. For details, see FolderViewer Component.
RawTreeListView
also operates somewhat like the CommonEditor
component. It creates a UserControl
to host a tree control and buttons. Its CreateButton()
method is identical to CommonEditor
's. It also implements IControlHostClient
to serve as the UserControl
control host client, which functions similarly to CommonEditor
's client. However, RawTreeListView
uses a TreeListView
for the tree control rather than a TreeListViewEditor
.
The constructor creates all the buttons needed, adds them to the UserControl
, and subscribes to events. The event handlers are all in RawTreeListView
. The TreeListView
is also created and added to the UserControl
. Finally, the constructor invokes RegisterControl()
with the Control Host Registry to register UserControl
.
RawTreeListView
also implements IInitializable.Initialize()
, which does nothing. However, implementing IInitializable
forces this component to be instantiated, which would not happen otherwise, because nothing imports it. For more details on MEF initialization, see Initializing Components.
MySorter
is the sorter that RawTreeListView
provides for the tree, as seen in this code from RawTreeListView
's constructor.
m_control = new TreeListView { Name = NameText, AllowDrop = true };
m_control.NodeSorter = new MySorter(m_control);
MySorter
is somewhat similar to the DataComparer
class used by CommonEditor
, but sorts folder and file names rather than generated data. For information on how DataComparer
works, see DataComparer Class.
Most of the methods and code in RawTreeListView
either handle events or enumerate the files in a folder to display them. The folder handling uses standard .NET classes, such as DirectoryInfo
and Directory
.
The file DataGenerator.cs
contains several classes used in generating display data for the trees, in particular DataItem
and DataContainer
.
DataContainer
in particular works with the TreeListViewAdapter
class to display generated data. To learn how this works, see TreeListViewAdapter Class.
DataItem
represents a set of generated data displayed in a tree; the PropertyEditor
component can also display certain DataItem
properties. DataItem
derives from System.ComponentModel.CustomTypeDescriptor
, which is a simple default implementation of the System.ComponentModel.ICustomTypeDescriptor
interface:
class DataItem : CustomTypeDescriptor
ICustomTypeDescriptor
supplies dynamic custom type information for an object and is useful for describing data items in an application.
DataItem
has a set of properties, such as Parent
and Children
, that allow it to represent hierarchical data. Consider these two properties:
/// <summary>
/// Gets whether item has children</summary>
[PropertyEditingAttribute]
public bool HasChildren
{
get { return m_children.Count > 0; }
}
/// <summary>
/// Gets item's children</summary>
public ICollection<DataItem> Children
{
get { return m_children; }
}
The Children
property gets an ICollection<DataItem>
of all the child data.
Note that the property HasChildren
is marked with the attribute [PropertyEditingAttribute]
, but Children
isn't.
[PropertyEditingAttribute]
is a custom attribute that TreeListEditor uses to mark the properties to be displayed in the Property Editor. It's defined this way:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class PropertyEditingAttribute : Attribute
{
}
The PropertyEditingAttribute
class itself is marked with the attribute [AttributeUsageAttribute()]
, which tells how the defined attribute can be used. In this case, the AttributeTargets.Property
enumeration value means the attribute applies to C# properties. AllowMultiple = true
means that the attribute can be used more than once in a class. The attribute [PropertyEditingAttribute]
has no parameters, so the class is very simple, requiring nothing but its declaration. The properties here are marked with the full attribute name [PropertyEditingAttribute]
, although [PropertyEditing]
also works, by convention.
DataItem
defines a property descriptor class PropertyPropertyDescriptor
for C# properties. Note that the word "property" is used in two senses here: for a C# property, as well as for a property represented by a property descriptor.
public class PropertyPropertyDescriptor : PropertyDescriptor
PropertyPropertyDescriptor
derives from System.ComponentModel.PropertyDescriptor
.
When a selection of tree items occurs, through a chain of events, a collection of property descriptors is requested by calling the GetProperties()
method for each selected item that implements ICustomTypeDescriptor
, which is DataItem
in TreeListEditor. This eventually results in the properties for the last selected item being displayed in the Property Editor window by the PropertyEditor
component.
The class deriving from CustomTypeDescriptor
, DataItem
, implements the GetProperties()
method:
public override PropertyDescriptorCollection GetProperties()
{
var props = new PropertyDescriptorCollection(null);
foreach (var property in GetType().GetProperties())
{
var propertyDesc =
new PropertyPropertyDescriptor(property, GetType());
propertyDesc.ItemChanged += PropertyDescItemChanged;
foreach (Attribute attr in propertyDesc.Attributes)
{
if (attr.GetType().Equals(typeof(PropertyEditingAttribute)))
props.Add(propertyDesc);
}
}
return props;
}
This method iterates through all the C# properties defined in the DataItem
class, obtained from GetProperties()
, and creates a PropertyPropertyDescriptor
for each property. It then checks all the Attribute
s for the data in the property descriptor. If any of the attributes is PropertyEditingAttribute
, it adds the property descriptor to the PropertyDescriptorCollection
object props
. It does not add the property descriptor more than once, because the only attribute marking properties in DataItem
is [PropertyEditingAttribute]
.
This GetProperties()
method includes properties that have the checked for attribute. This guarantees that only [PropertyEditingAttribute]
marked properties are listed in the Property Editor.
This class provides the ability to display data in tree controls, and it also generates the data as DataItem
objects. It contains data in its m_data
field, which is of type List<DataItem>
.
DataContainer
implements these interfaces, each of which are discussed below:
class DataContainer : ITreeListView, IItemView, IObservableContext, ISelectionContext, IValidationContext
ITreeListView
defines a view on hierarchical data and consists of the following:
-
IEnumerable<object> Roots
: Get the root level objects of the tree view. -
IEnumerable<object> GetChildren(object parent)
: Get an enumeration of the children of the given parent. -
string\[[\]] ColumnNames
: Get the list column's names.
ITreeView
interface, except that ITreeListView
allows more than one root object and also adds ColumnNames
. The implementation here uses the m_data
field along with DataItem
's properties HasChildren
and Children
.
This interface's GetInfo()
method gets a given object's ItemInfo
. The ItemInfo
class contains numerous properties to access information about an item displayed in a tree node (or list). The implementation of GetInfo()
in DataContainer
just fills out a few properties:
public void GetInfo(object obj, ItemInfo info)
{
var data = obj.As<DataItem>();
if (data == null)
return;
info.Label = data.Name;
info.Properties =
new object[]
{
data.Type.ToString(),
data.Value
};
info.IsLeaf = !data.HasChildren;
info.ImageIndex =
data.HasChildren
? s_folderImageIndex
: s_dataImageIndex;
}
First, this method adapts the item to a DataItem
. After copying the Name
, it creates an array of object
s for the Properties
property, containing a string of the item's type and its value. It sets IsLeaf
, and then sets up ImageIndex
to display a folder image for an item with children.
IObservableContext
contains events for items being changed, added, or removed. This is required so the tree display can be refreshed after items change. These events are raised when data is modified.
When the TreeListViewAdapter.View
property is set, TreeListViewAdapter
adapts the value of View
to IObservableContext
and subscribes the adapted value to these events. These events are thus handled by methods in TreeListViewAdapter
. Also recall that View
contains a DataContainer
object. For details, see TreeListViewAdapter View Property and IObservableContext Events in particular. TreeListViewAdapter
does the same thing for the IValidationContext
interface.
ISelectionContext
is the interface for selection contexts, which have selectable items. Here, the selectable objects are DataItem
objects, as seen in this section of the ModifySelected()
method, which is called when the "Modify Selected" button is clicked:
private void ModifySelected(IEnumerable<object> selection)
{
Beginning.Raise(this, EventArgs.Empty);
foreach (var obj in selection)
{
var data = obj.As<DataItem>();
...
The DataContainer
class creates a Selection
object, which is a collection representing a selection. It's easy to implement ISelectionContext
using the methods available on a Selection
object. The Selection
property gets and sets the current selection, and is implemented by simply getting and setting the m_selection
field, which contains the Selection
object:
public IEnumerable<object> Selection
{
get { return m_selection; }
set { m_selection.SetRange(value); }
}
The other properties and methods in this interface get information about the selection, such as the count of selected objects and the last selected object. These methods are also implemented using m_selection
, as in the LastSelected
property:
public object LastSelected
{
get { return m_selection.LastSelected; }
}
Another important thing in this interface are selection related events:
/// <summary>
/// Event that is raised before the selection changes</summary>
public event EventHandler SelectionChanging;
/// <summary>
/// Event that is raised after the selection changes</summary>
public event EventHandler SelectionChanged;
In its constructor, DataContainer
subscribes to the class's Selection<T>
selection change events:
m_selection.Changing += TheSelectionChanging;
m_selection.Changed += TheSelectionChanged;
TheSelectionChanging()
simply raises an event:
private void TheSelectionChanging(object sender, EventArgs e)
{
SelectionChanging.Raise(this, EventArgs.Empty);
}
Raising such selection events hooks into the event handling system that ultimately generates lists of property descriptors that property editors can use to display the common properties of selected items.
This interface provides events that validators can subscribe to and perform their validations at appropriate times:
-
Beginning
: Raised before validation begins. -
Cancelled
: Raised after validation is cancelled. -
Ending
: Raised before validation ends. -
Ended
: Raised after validation ends.
When the TreeListViewAdapter.View
property is set, TreeListViewAdapter
adapts the value of View
to IValidationContext
and subscribes the adapted value to these events. These events are thus handled by methods in TreeListViewAdapter
. For details, see TreeListViewAdapter View Property and IValidationContext Events in particular. TreeListViewAdapter
does the same thing for the IObservableContext
interface.
The event handler method BtnClick()
handles clicking buttons on the tree windows, as described in BtnClick Method. BtnClick()
calls DataContainer
's data generation methods for some of the buttons, as seen in this section of the method:
private void BtnClick(object sender, EventArgs e)
{
var btn = (Button)sender;
var flags = (Buttons)btn.Tag;
switch (flags)
{
case Buttons.AddFlat:
DataContainer.GenerateFlat(
View,
(TreeListView.TheStyle != TreeListView.Style.TreeList) &&
(TreeListView.TheStyle != TreeListView.Style.CheckedTreeList)
? null
: TreeListViewAdapter.LastHit);
break;
case Buttons.AddHierarchical:
DataContainer.GenerateHierarchical(
View,
TreeListViewAdapter.LastHit);
break;
...
The GenerateFlat()
method, for instance, has a first parameter of View
, which is a property of TreeListViewEditor
, from which the CommonEditor
class derives. However, the View
property in TreeListViewEditor
simply gets and sets the View
property in TreeListViewAdapter
. The TreeListViewAdapter
's View
property holds an ITreeListView
, that is, an object implementing ITreeListView
. For TreeListEditor, this is a DataContainer
object, because each of the tree editors makes an assignment like this:
View = new DataContainer();
For details, see Editors Derived from CommonEditor. For details on the View
property, see TreeListViewAdapter View Property.
The second parameter of AddHierarchical()
is either null
(depending on the tree style) or the last data item selected.
Here's the GenerateFlat()
method:
public static void GenerateFlat(ITreeListView view, object lastHit)
{
var container = view.As<DataContainer>();
if (container == null)
return;
container.GenerateFlat(lastHit.As<DataItem>());
}
The view
parameter is adapted to DataContainer
, which works because view
is a DataContainer
object. The private method GenerateFlat()
is called, with a parameter of the last selected item, if any, which serves as the parent to which data objects are added:
private void GenerateFlat(DataItem parent)
{
Beginning.Raise(this, EventArgs.Empty);
var items = s_random.Next(5, 16);
for (var i = 0; i < items; i++)
{
var data = CreateItem(parent);
data.ItemChanged += DataItemChanged;
if (parent != null)
parent.Children.Add(data);
else
m_data.Add(data);
ItemInserted.Raise(this, new ItemInsertedEventArgs<object>(-1, data, parent));
}
if (parent != null)
{
ItemChanged.Raise(this, new ItemChangedEventArgs<object>(parent));
}
Ending.Raise(this, EventArgs.Empty);
Ended.Raise(this, EventArgs.Empty);
}
Notice that GenerateFlat()
raises the IValidationContext
events at the appropriate times: Beginning
at the beginning, and then Ending
and Ended
at the end.
A System.Random
object is used repeatedly in data generation to randomize the number of objects generated and the lengths of strings. For instance, the loop generates between 5 and 16 items, using the CreateItem()
method to create DataItem
objects.
The ItemChanged
event is subscribed to for the new DataItem
with the handler DataItemChanged
. This handler raises the ItemChanged
event of the IObservableContext
interface. This is key to updating the tree with this new data. For details, see ItemChanged and ItemRemoved Events.
CreateItem()
creates all the DataItem
objects:
private static DataItem CreateItem(DataItem parent)
{
var enumLength = Enum.GetNames(typeof(DataType)).Length;
var name = CreateString(s_random.Next(2, 11));
var type = (DataType)s_random.Next(0, enumLength);
object value;
switch (type)
{
case DataType.Integer:
value = s_random.Next(0, 51);
break;
case DataType.String:
value = CreateString(s_random.Next(5, 16));
break;
default:
value = type.ToString();
break;
}
var data =
new DataItem(
parent,
name,
type,
value);
return data;
}
This method generates a random object name, randomly chooses the type of the generated object, and then generates a random integer or string for the object's value. It constructs a new DataItem
from this data and returns it.
DataComparer
provides the functions to sort items in the tree lists. It is defined for a TreeListView.Node
:
class DataComparer : IComparer<TreeListView.Node>
Its main function, Compare()
, shows that TreeListView.Node.Tag
is adapted to DataItem
:
public int Compare(TreeListView.Node x, TreeListView.Node y)
{
if ((x == null) && (y == null))
return 0;
if (x == null)
return 1;
if (y == null)
return -1;
if (ReferenceEquals(x, y))
return 0;
var lhs = x.Tag.As<DataItem>();
var rhs = y.Tag.As<DataItem>();
...
The TreeListView.Node.Tag
property gets or sets the user data attached to the node, which is a DataItem
in this case. For details on how TreeListView.Node.Tag
is set to a DataItem
, see ItemChanged and ItemRemoved Events in the section TreeListViewAdapter Class.
TreeListViewAdapter
wraps a TreeListView
and allows supplying data to the TreeListView
through its View
property, which implements the ITreeListView
interface. The TreeListView.Node.Tag
contains the data attached to the node, as already noted. This section shows how the TreeListViewAdapter
makes that happen and how the View
property is the key. TreeListViewAdapter
also triggers updating the tree's display after data changes. Raising the appropriate events makes the updates occur.
This section also shows that by implementing ITreeListView
, DataContainer
allows displaying data in the tree with TreeListViewAdapter
.
As previously seen in Editors Derived from CommonEditor, the tree editors deriving from CommonEditor
set the View
property of TreeListViewEditor
in their constructors:
View = new DataContainer();
Here's the definition of TreeListViewEditor.View
public ITreeListView View
{
get { return m_treeListViewAdapter.View; }
set { m_treeListViewAdapter.View = value; }
}
This shows that the TreeListViewEditor.View
property actually comes from TreeListViewAdapter.View
, because m_treeListViewAdapter
holds the TreeListViewAdapter
associated with the TreeListViewEditor
. Therefore, the DataContainer
object is the value that TreeListViewAdapter
sees in View
. Note that DataContainer
implements ITreeListView
, as required.
TreeListViewAdapter.View
contains the actual DataContainer
object, as just observed. Here is part of the implementation of TreeListViewAdapter.View
:
public ITreeListView View
{
get { return m_view; }
set
{
if (m_view != value)
{
if (m_view != null)
{
...
m_view = value;
if (m_view != null)
{
m_itemView = m_view.As<IItemView>();
m_selectionContext = m_view.As<ISelectionContext>();
if (m_selectionContext != null)
{
m_selectionContext.SelectionChanging += SelectionContextSelectionChanging;
m_selectionContext.SelectionChanged += SelectionContextSelectionChanged;
}
else
{
m_treeListView.NodeSelected += TreeListViewNodeSelected;
}
m_observableContext = m_view.As<IObservableContext>();
if (m_observableContext != null)
{
m_observableContext.ItemInserted += ObservableContextItemInserted;
m_observableContext.ItemChanged += ObservableContextItemChanged;
m_observableContext.ItemRemoved += ObservableContextItemRemoved;
m_observableContext.Reloaded += ObservableContextReloaded;
}
m_validationContext = m_view.As<IValidationContext>();
if (m_validationContext != null)
{
m_validationContext.Beginning += ValidationContextBeginning;
m_validationContext.Ending += ValidationContextEnding;
m_validationContext.Ended += ValidationContextEnded;
m_validationContext.Cancelled += ValidationContextCancelled;
}
}
}
Load();
}
}
The property's setter attempts to adapt the new value of the View
property to several context interfaces:
ISelectionContext
IObservableContext
IValidationContext
DataContainer
value to a set of events in the interface. Because DataContainer
implements all these interfaces, DataContainer
objects subscribe to all these events. However, all these event handlers are in TreeListViewAdapter
. These handlers allow displaying data in the tree.
These events relate to items in the context changing. They are raised by the data generation methods in DataContainer
to update the display, as shown in DataContainer Data Generation Methods.
Here's the handler for ItemInserted
:
private void ObservableContextItemInserted(object sender, ItemInsertedEventArgs<object> e)
{
if (m_treeListView.TheStyle != TreeListView.Style.VirtualList)
{
if (m_inTransaction)
m_queueInserts.Add(e);
else
AddItem(e.Item, e.Parent);
}
else
{
if (e.Item is object[])
VirtualListSize += ((object[])e.Item).Length;
else
VirtualListSize += 1;
}
}
For non-virtual tree lists, the new item in e.Item
is added to the queue m_queueInserts
for later addition to the tree — or added immediately with the AddItem()
method. The field m_inTransaction
indicates whether a transaction is occurring or not, and is set in the IValidationContext
events. For more details, see IValidationContext Events.
AddItem()
creates a TreeListView.Node
for the item with the CreateNodeForObject()
method, which also calls UpdateNodeFromItemInfo()
, discussed further in ItemChanged and ItemRemoved Events. AddItem()
also adds nodes for children of the item, because this data can be hierarchical: a DataItem
can have child DataItem
s.
The event handlers for ItemChanged
and ItemRemoved
are similar to each other. Both handle virtual lists separately, and queue items that are to be changed or removed during a transaction — or perform the update immediately when there is no transaction. Here is ObservableContextItemChanged()
, for instance:
private void ObservableContextItemChanged(object sender, ItemChangedEventArgs<object> e)
{
if (m_treeListView.TheStyle != TreeListView.Style.VirtualList)
{
if (m_inTransaction)
m_queueUpdates.Add(e);
else
UpdateItem(e.Item);
}
else
{
UpdateItem(e.Item);
}
}
During a transaction, the item is added to m_queueUpdate
; otherwise the tree is immediately updated with UpdateItem
, which does the following:
private void UpdateItem(object item)
{
TreeListView.Node node;
if (!m_dictNodes.TryGetValue(item, out node))
return;
ItemInfo info =
GetItemInfo(
item,
m_itemView,
m_treeListView.ImageList,
m_treeListView.StateImageList);
UpdateNodeFromItemInfo(node, item, info);
m_treeListView.Invalidate(node);
}
After getting item information from GetItemInfo()
, UpdateItem()
calls UpdateNodeFromItemInfo()
with that information and then invalidates the tree view to force redrawing the tree.
Among other things, UpdateNodeFromItemInfo()
makes the data stored in the tree easily accessible:
private static void UpdateNodeFromItemInfo(TreeListView.Node node, object item, ItemInfo info)
{
node.Label = info.Label;
node.Properties = info.Properties;
node.ImageIndex = info.ImageIndex;
node.StateImageIndex = info.StateImageIndex;
node.CheckState = info.GetCheckState();
node.Tag = item;
node.IsLeaf = info.IsLeaf;
node.Expanded = info.IsExpandedInView;
node.FontStyle = info.FontStyle;
node.HoverText = info.HoverText;
}
Note that node.Tag = item
sets the TreeListView.Node.Tag
property to the data item. For TreeListEditor, this is a DataItem
object. The Compare()
method takes advantage of this to access this data, as noted in DataComparer Class. In addition, the LastHit
property gets the data for the last selected node from Tag
:
public object LastHit
{
get
{
object lastHit = m_treeListView.LastHit;
return lastHit.Is<TreeListView.Node>() ? lastHit.As<TreeListView.Node>().Tag : this;
}
}
This finally explains how the Tag
property gets set to the data object, which is a DataItem
instance.
The Reloaded
event occurs when the data for a tree has been reloaded, and its event handler merely calls the Load()
method:
private void Load()
{
Unload();
if (m_view == null)
return;
if (m_treeListView.TheStyle == TreeListView.Style.VirtualList)
m_treeListView.VirtualListSize = 0;
// Add columns & data
foreach (string columnName in m_view.ColumnNames)
m_treeListView.Columns.Add(new TreeListView.Column(columnName));
try
{
m_treeListView.BeginUpdate();
if (m_treeListView.TheStyle != TreeListView.Style.VirtualList)
{
foreach (object root in m_view.Roots)
{
if (m_inTransaction)
m_queueInserts.Add(new ItemInsertedEventArgs<object>(-1, root, null));
else
AddItem(root, null);
}
}
}
finally
{
m_treeListView.EndUpdate();
}
}
Unload()
clears the tree and all the work queues, such as m_queueInserts
. The method TreeListView.ColumnCollection.Add()
adds a column to the tree.
In the second loop, data items are obtained from the Roots
property, which holds all the root data objects. This data is added to either to m_queueInserts
or immediately added to the tree by AddItem()
, as in the ItemInserted
handler ObservableContextItemInserted
shown in ItemInserted Event. For TreeListEditor, Roots
is a property of DataContainer
that enumerates the DataItem
objects in this container.
In the end, TreeListView.EndUpdate()
calls EndUpdate()
on the underlying control, which resumes redrawing the tree. Drawing was prevented earlier by the TreeListView.BeginUpdate()
method.
IValidationContext
provides events to encapsulate processes in transactions, so the operation can be cancelled if a problem occurs. These event handlers also modify tree data in a similar fashion to the IObservableContext
events.
The Beginning
event handler simply sets flags:
private void ValidationContextBeginning(object sender, EventArgs e)
{
m_inTransaction = true;
m_treeListView.UseInsertQueue = true;
}
The m_inTransaction
field is used throughout TreeListViewAdapter
to determine whether a transaction is occurring, as seen in ItemInserted Event.
TreeListViewAdapter
really handles only the Ended
event signalling the end of a transaction, using this method:
private void ValidationContextEnded(object sender, EventArgs e)
{
if (!m_inTransaction)
return;
try
{
m_treeListView.BeginUpdate();
try
{
m_treeListView.UseInsertQueue = true;
foreach (var args in m_queueInserts)
AddItem(args.Item, args.Parent);
}
finally
{
m_treeListView.UseInsertQueue = false;
m_treeListView.FlushInsertQueue();
}
foreach (var args in m_queueUpdates)
UpdateItem(args.Item);
foreach (var args in m_queueRemoves)
RemoveItem(args.Item);
}
finally
{
m_inTransaction = false;
m_queueInserts.Clear();
m_queueUpdates.Clear();
m_queueRemoves.Clear();
m_treeListView.EndUpdate();
}
}
ValidationContextEnded()
first stops display drawing by calling BeginUpdate()
.
If there are items to add in m_queueInserts
, it calls AddItem()
for each one. Update items in m_queueUpdates
are processed with UpdateItem()
, and items in m_queueRemoves
are removed with RemoveItem()
. All the work queues are emptied.
Finally, EndUpdate()
redraws the tree.
- 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