Flexible TreeView 5.1.9 Help
Data binding. Bidirectional (two-way) binding mode

Flexible TreeView supports one-way and bidirectional (two-way) binding modes. Bidirectional binding mode allows to synchronize the changes of the treeview and data source without writing a single line of code.

If synchronization of the changes can't be made automatically,  Flexible TreeView neither does any actions nor gives alerts, that is why you must strictly follow the demands specified in this section.

Prepare data source

For the automatic bidirectional binding your data source should be of the DataTable type or the type that implements IBindingList and IList<T>. For other types a semi-automatic change of data source is possible.

While being in the bound mode treeview still allow to manipulate its structure. I.e. you are free to add or delete the bound nodes. Moreover, you can add non-bound nodes to the treeview in bound mode!

There are two ways to change the treeview data structure:

Note that when the objects (data table rows, array items, etc.) are deleted from the data source (which is followed by changes in a treeview structure), automatic synchronization with the bound treeview is supported for DataTable or IBindingList data source types only. In order to solve this problem you can delete nodes in the treeview instead, while the objects will be automatically deleted from any data source that inherits from IBindingList or IList<T> or of DataTable type.

In order to synchronize changes between the treeview and data source automatically, the following rules are to be applied:


Supported edit operations

Flexible TreeView provides automatic synchronization of changes for the following operations in the treeview or data source:

 

Edit data source

One of the ways of treeview data editing is object addition and deletion in the bound data source.
In bidirectional mode, types indicated in the 'Prepare data source' section above can be used as a data source. The only exception is: object deletion from the data source may be traced only when the source is of the DataTable or the IBindingList types.

For example, say we have the following data source object class:

class DataObject
{
  public int Id { get; set; }
  public int? ParentId { get; set; }
  public string Text { get; set; }
}
Class DataObject
    Public Property Id() As Integer
        Get
            Return m_Id
        End Get
        Set
            m_Id = Value
        End Set
    End Property
    Private m_Id As Integer
    Public Property ParentId() As System.Nullable(Of Integer)
        Get
            Return m_ParentId
        End Get
        Set
            m_ParentId = Value
        End Set
    End Property
    Private m_ParentId As System.Nullable(Of Integer)
    Public Property Text() As String
        Get
            Return m_Text
        End Get
        Set
            m_Text = Value
        End Set
    End Property
    Private m_Text As String
End Class

This is the way we can build the data source, bind it to the treeview and enable bidirectional binding mode:

// prepare data source once.
BindingList<DataObject> list = LoadDataSource();
tree.DataBinding.KeyFieldName = "Id";
tree.DataBinding.ParentFieldName = "ParentId";
tree.DataBinding.DataSource = list;
// enable bidirectional binding mode.
tree.DataBinding.BidirectionalMode = true;
 
BindingList<DataObject> LoadDataSource()
{
  BindingList<DataObject> list = new BindingList<DataObject>();
  list.Add(new DataObject
                 {
                   Id = 1,
                   ParentId = null,
                   Text = "1"
                 });
  list.Add(new DataObject
                 {
                   Id = 2,
                   ParentId = 1,
                   Text = "2"
                 });
  return list;
}
' prepare data source once.
Dim list As BindingList(Of DataObject) = LoadDataSource()
tree.DataBinding.KeyFieldName = "Id"
tree.DataBinding.ParentFieldName = "ParentId"
tree.DataBinding.DataSource = list
' enable bidirectional binding mode.
tree.DataBinding.BidirectionalMode = True

Private Function LoadDataSource() As BindingList(Of DataObject)
    Dim list As New BindingList(Of DataObject)()
    list.Add(New DataObject() With { _
        Key .Id = 1, _
        Key .ParentId = Nothing, _
        Key .Text = "1" _
    })
    list.Add(New DataObject() With { _
        Key .Id = 2, _
        Key .ParentId = 1, _
        Key .Text = "2" _
    })
    Return list
End Function

As a result, the following treeview is generated:

 

Then you may add a new object into this data source, which will automatically be followed with the node addition to the tree:

// create new object.
DataObject newObject = new DataObject();
newObject.Id = 3;
newObject.ParentId = 2;
newObject.Text="third level object";
 
// add new object to the data source.
BindingList<DataObject> list = (BindingList<DataObject>)tree.DataBinding.DataSource;
list.Add(newObject);
' create new object.
Dim newObject As New DataObject()
newObject.Id = 3
newObject.ParentId = 2
newObject.Text = "third level object"

' add new object to the data source.
Dim list As BindingList(Of DataObject) = DirectCast(tree.DataBinding.DataSource, BindingList(Of DataObject))
list.Add(newObject)

And there is no need to trace the treeview changes and synchronize them with the data source.

Note that when a new object is added to the data source, a generated node will be added to the end of child nodes of the parent node list.


If you need to delete an object from the data source and a corresponding node from the treeview, you may just delete this object from the bound data source:

BindingList<DataObject> list = (BindingList<DataObject>)tree.DataBinding.DataSource;
list.RemoveAt(objectIndex);
Dim list As BindingList(Of DataObject) = DirectCast(tree.DataBinding.DataSource, BindingList(Of DataObject))
list.RemoveAt(objectIndex)

Note that on object deletion, all child objects will be deleted as well!

If columns sorting is enabled within the tree, after data source object deletion or addition, you need to resort the treeview manually, as its structure has been changed:

// remove first object in the data source.
DataTable table = (DataTable)tree.DataBinding.DataSource;
table.Rows.RemoveAt(0);
 
// resort treeview after linked node has been removed.
tree.ReSort();
' remove first object in the data source.
Dim table As DataTable = DirectCast(tree.DataBinding.DataSource, DataTable)
table.Rows.RemoveAt(0)

' resort treeview after linked node has been removed.
tree.ReSort()

Edit treeview

Addition and deletion of the nodes in the treeview is another way of data editing. As a result, the corresponding objects will be added or deleted in the data source when bidirectional binding mode is enabled.

For example, if we bind the data source like this (LoadDataSource method is defined above):

BindingList<DataObject> list = LoadDataSource();
tree.DataBinding.KeyFieldName = "Id";
tree.DataBinding.ParentFieldName = "ParentId";
tree.DataBinding.DataSource = list;
// enable bidirectional binding mode.
tree.DataBinding.BidirectionalMode = true;
Dim list As BindingList(Of DataObject) = LoadDataSource()
tree.DataBinding.KeyFieldName = "Id"
tree.DataBinding.ParentFieldName = "ParentId"
tree.DataBinding.DataSource = list
' enable bidirectional binding mode.
tree.DataBinding.BidirectionalMode = True

the following treeview is generated:

 

You can delete the root node '1' by using the Detach node method as shown below.

tree.Nodes[0].Detach();
tree.Nodes(0).Detach()

The code above will automatically delete all the objects bound both to the node '1' and to all its child nodes.

In order to automatically add a new object into the data source, you need to add a node into the treeview, which will automatically create an object in your bound data source:

int? newObjectId = 3;
BindableNode node = new BindableNode(newObjectId);
node.AttachTo(tree.Nodes[0]);
Dim newObjectId As System.Nullable(Of Integer) = 3
Dim node As New BindableNode(newObjectId)
node.AttachTo(tree.Nodes(0))

Note that when creating a node, you need to pass a unique identifier of a new object to the BindableNode class constructor, if you're using a hierarchical data source. The data type of that identifier must be identical to property type indicated by DataBinding.ParentFieldName.

There are some cases, when you need to create a new object in the data source manually, as the automatic addition logic is not appropriate. In this case when adding a node to the treeview, the BoundObject node property must not be set to NULL:

int? newObjectId = 3;
BindableNode node = new BindableNode(newObjectId);
node.BoundObject = "temp fake object";
node.AttachTo(tree.Nodes[0]);
Dim newObjectId As System.Nullable(Of Integer) = 3
Dim node As New BindableNode(newObjectId)
node.BoundObject = "temp fake object"
node.AttachTo(tree.Nodes(0))

In this case, Flexible TreeView does not generate a data source object for such a node, a user must handle the NodeInserted treeview event, create an object manually and add it to the bound data source as shown below.

void tree_NodeInserted(FlexibleTreeView pTreeview, Node pArgs)
{
  BindableNode node = pArgs as BindableNode;
  if (node != null && node.BoundObject != null)
  {
    // create new object.
    DataObject newObject = new DataObject();
 
    // new object's identifier should be passed to the BindableNode class's constructor so we can use it as the new object's Id value.
    newObject.Id = (int)node.Id;
 
    // get the parent node's Id to fill the ParentId property value.
    BindableNode parentNode = node.Parent as BindableNode;
    int? parentId = parentNode != null ? (int?)parentNode.Id : null;
    newObject.ParentId = parentId;
 
    // fill other properties by default.
    newObject.Text = "New object";
 
    // add new object into the data source.
    BindingList<DataObject> list = (BindingList<DataObject>)tree.DataBinding.DataSource;
    list.Add(newObject);
 
    // IMPORTANT! Newly create object should be linked with apropriate node.
    node.BoundObject = newObject;
  }
}
Private Sub tree_NodeInserted(pTreeview As FlexibleTreeView, pArgs As Node)
    Dim node As BindableNode = TryCast(pArgs, BindableNode)
    If node IsNot Nothing AndAlso node.BoundObject IsNot Nothing Then
        ' create new object.
        Dim newObject As New DataObject()

        ' new object's identifier should be passed to the BindableNode class's constructor so we can use it as the new object's Id value.
        newObject.Id = CInt(node.Id)

        ' get the parent node's Id to fill the ParentId property value.
        Dim parentNode As BindableNode = TryCast(node.Parent, BindableNode)
        Dim parentId As System.Nullable(Of Integer) = If(parentNode IsNot Nothing, DirectCast(parentNode.Id, System.Nullable(Of Integer)), Nothing)
        newObject.ParentId = parentId

        ' fill other properties by default.
        newObject.Text = "New object"

        ' add new object into the data source.
        Dim list As BindingList(Of DataObject) = DirectCast(tree.DataBinding.DataSource, BindingList(Of DataObject))
        list.Add(newObject)

        ' IMPORTANT! Newly create object should be linked with apropriate node.
        node.BoundObject = newObject
    End If
End Sub

Pay attention to the last line! You are obliged to bind a newly created object to the added node before exit from the event handler.

 

Mixed binding mode

Mixed binding mode, when both bound and unbound nodes coexist in the same treeview, is also supported in the bidirectional binding mode, what gives you unlimited possibilities for data manipulations.

Note that the data source will be automatically synchronized with treeview changes only if an instance of the BindableNode class is added to the treeview.

 

Drag & drop

Besides the manual node addition or deletion, Flexible TreeView supports the automatic bound object re-linking to another parent object using drag & drop within the current treeview. You just need to allow drag & drop using the DragDropOptions treeview property and enable bidirectional binding mode by enabling the DataBinding.BidirectionalMode property as shown below.

tree.DragDropOptions.AllowDrag = true;
tree.DragDropOptions.AllowDrop = true;
tree.DataBinding.BidirectionalMode = true;
tree.DragDropOptions.AllowDrag = True
tree.DragDropOptions.AllowDrop = True
tree.DataBinding.BidirectionalMode = True

In this case, the bound data object's property, indicated in DataBinding.ParentFieldName, for the dragged object will automatically be changed to the new parent node's identifier value.

There are some restrictions for this case:

 

Tune synchronization results

It is not always possible to automatically synchronize the treeview and data source, or sometimes you need to monitor this. Therefore Flexible TreeView allows to execute additional user code right after automatic procedures went through.

As the main problem may occur while adding the treeview nodes or data source objects, the main means of control is the NodeInserted treeview event in this case. It is raised right after the node is added to the treeview. A node may be added to the treeview with linked data source when a new object is added to the data source, or when this very node is added manually by the user to create a data source object automatically.

Thus, if there is a possibility that the new object may not be created automatically, you need to handle the NodeInserted event and create a data source object manually. You may find out if Flexible TreeView created a new object with the help of the BoundObject property of the node, passed into the event handler.
For example, the following code creates a new object in the data source and binds it to the added node in the only case if Flexible TreeView did not create the object automatically:

void tree_NodeInserted(FlexibleTreeView pTreeview, Node pArgs)
{
  BindableNode node = pArgs as BindableNode;
  if (node != null && node.BoundObject == null)
  {
    // create new object with Id=5 and link it with a parent object with Id=1.
    DataObject newObject = new DataObject();
    newObject.Id = 5;
    newObject.ParentId = 1;
    newObject.Text = "3";
 
    // add created object to the data source.
    BindingList<DataObject> list = (BindingList<DataObject>)tree.DataBinding.DataSource;
    list.Add(newObject);
 
    // IMPORTANT! Newly create object should be linked with apropriate node.
    node.BoundObject = newObject;
  }
}
Private Sub tree_NodeInserted(pTreeview As FlexibleTreeView, pArgs As Node)
    Dim node As BindableNode = TryCast(pArgs, BindableNode)
    If node IsNot Nothing AndAlso node.BoundObject Is Nothing Then
        ' create new object with Id=5 and link it with a parent object with Id=1.
        Dim newObject As New DataObject()
        newObject.Id = 5
        newObject.ParentId = 1
        newObject.Text = "3"

        ' add created object to the data source.
        Dim list As BindingList(Of DataObject) = DirectCast(tree.DataBinding.DataSource, BindingList(Of DataObject))
        list.Add(newObject)

        ' IMPORTANT! Newly create object should be linked with apropriate node.
        node.BoundObject = newObject
    End If
End Sub
Note that we execute the code only if the object hasn't been created automatically!

Also, NodeInserted may be useful when the object has been created by Flexible TreeView automatically, however there is a necessity to initialize some object fields after creation:

void tree_NodeInserted(FlexibleTreeView pTreeview, Node pArgs)
{
  BindableNode node = pArgs as BindableNode;
  if (node != null && node.BoundObject != null)
  {
    DataObject createdObject = (DataObject)node.BoundObject;
    createdObject.Text = "New object";
  }
}
Private Sub tree_NodeInserted(pTreeview As FlexibleTreeView, pArgs As Node)
    Dim node As BindableNode = TryCast(pArgs, BindableNode)
    If node IsNot Nothing AndAlso node.BoundObject IsNot Nothing Then
        Dim createdObject As DataObject = DirectCast(node.BoundObject, DataObject)
        createdObject.Text = "New object"
    End If
End Sub

 

 

 


Copyright © 2006-2016 ARMSoft

www.FlexibleTreeView.com