Skip to content

Data binding. Inspection#

Validation#

During binding of the data source, all public properties (as columns and node controls) and objects (as nodes) located in the data source will be added to the treeview. Flexible TreeView also allows you to perform validation of these added objects. To do this, turn on the Validate property of the DataBinding object and subscribe to the ColumnPopulating, NodeControlPopulating, and NodePopulating events.

ColumnPopulating event#

The ColumnPopulating event allows validating or changing properties of columns added to the treeview, which are formed from the data source object properties. You can refuse to add such a column in the event handler by setting the value of the Cancel property to true.

Example

void tree_ColumnPopulating(FlexibleTreeView treeview, ColumnPopulatingEventArgs args)
{
    if (args.FieldName == "SomeField")
    {
      args.Cancel = true;
    }
}
Private Sub tree_ColumnPopulating(treeview As FlexibleTreeView, args As ColumnPopulatingEventArgs)
    If args.FieldName = "SomeField" Then
        args.Cancel = True
    End If
End Sub

Substitute auto-created column#

While binding treeview to a data source, it will create a separate treeview column for each bound visible data field. To change the auto-created column properties, you could handle the ColumnPopulating event, but it does not allow replacing the created column.

To substitute the auto-created column, create an ARMSoft.FlexibleTreeView.FlexibleTreeView class descendant and override its CreateBoundColumn method as shown below:

class FlexibleTreeViewEx : FlexibleTreeView
{
  protected override TreeColumn CreateBoundColumn(string boundFieldName)
  {
    if(boundFieldName == "SpecialField")
    {
      TreeColumn column = new TreeColumn();
      column.Text = "Special field";
      column.Width = 200;
      return column;
    }
    return base.CreateBoundColumn(boundFieldName);
  }
}
Class FlexibleTreeViewEx
    Inherits FlexibleTreeView
    Protected Overrides Function CreateBoundColumn(boundFieldName As String) As TreeColumn
        If boundFieldName = "SpecialField" Then
            Dim column As New TreeColumn()
            column.Text = "Special field"
            column.Width = 200
            Return column
        End If
        Return MyBase.CreateBoundColumn(boundFieldName)
    End Function
End Class

After that, use this new treeview type instead of ARMSoft.FlexibleTreeView.FlexibleTreeView on forms that need custom columns.

NodeControlPopulating event#

The NodeControlPopulating event allows validating the auto-created node controls formed from the data source object properties. You can refuse to add such a node control in the event handler by setting the value of the Cancel property to true as shown below:

void tree_NodeControlPopulating(FlexibleTreeView treeview, ARMSoft.FlexibleTreeView.Events.DataBinding.NodeControlPopulatingEventArgs args)
{
  if(args.FieldName == "HiddenField")
  {
    args.Cancel = true;
  }
}
Private Sub tree_NodeControlPopulating(treeview As FlexibleTreeView, args As ARMSoft.FlexibleTreeView.Events.DataBinding.NodeControlPopulatingEventArgs)
    If args.FieldName = "HiddenField" Then
        args.Cancel = True
    End If
End Sub

Substitute auto-created node control#

When Flexible TreeView binds the new data source, it automatically creates a node control that can manage the bound data type. If you need to inject a custom node control that works better with your data, create a new treeview class that inherits from ARMSoft.FlexibleTreeView.FlexibleTreeView, override the CreateBoundNodeControl method, and create and return a custom node control when needed.

private class CustomTreeview : FlexibleTreeView
{
  protected override BindableControl CreateBoundNodeControl(Type nodeControlType, string boundFieldName, Type boundFieldType)
  {
    if (boundFieldName == "OrderTitle")
    {
      NodeTextBox customCtrl = new NodeTextBox();
      customCtrl.Style.Font = new Font(customCtrl.Style.Font, FontStyle.Bold);
      return customCtrl;
    }

    return base.CreateBoundNodeControl(nodeControlType, boundFieldName, boundFieldType);
  }
}
Private Class CustomTreeview
    Inherits FlexibleTreeView
    Protected Overrides Function CreateBoundNodeControl(nodeControlType As Type, boundFieldName As String, boundFieldType As Type) As BindableControl
        If boundFieldName = "OrderTitle" Then
            Dim customCtrl As New NodeTextBox()
            customCtrl.Style.Font = New Font(customCtrl.Style.Font, FontStyle.Bold)
            Return customCtrl
        End If
        Return MyBase.CreateBoundNodeControl(nodeControlType, boundFieldName, boundFieldType)
    End Function
End Class

The example above creates a custom node control (NodeTextBox, but you can create any node control) for the OrderTitle bound property and changes the font to display order titles in bold.

NodePopulating event#

The NodePopulating event allows validating or changing properties of added nodes which are formed from the data source objects. You can refuse to add such a node in the event handler by setting the value of the Cancel property to true.

void tree_NodePopulating(FlexibleTreeView treeview, NodePopulatingEventArgs args)
{
  if (args.Node.Level > 2)
  {
    args.Cancel = true;
  }
}
Private Sub tree_NodePopulating(treeview As FlexibleTreeView, args As NodePopulatingEventArgs)
    If args.Node.Level > 2 Then
        args.Cancel = True
    End If
End Sub

Substitute auto-created node#

When Flexible TreeView binds a new data source, it automatically creates nodes that either hold the bound object's data or are linked to this data. If you need to use a custom bindable node class in bound mode, create a new treeview class that inherits from ARMSoft.FlexibleTreeView.FlexibleTreeView, override the CreateBoundNode method, and return a custom node instance as shown below.

This example creates and returns a new bindable node class instance which converts the StringField column string value to an integer on the fly:

class MyBindableNode : BindableNode
{
  public MyBindableNode(object id) : base(id)
  {
  }

  public override object GetBoundFieldValue(string fieldName)
  {
    object value = base.GetBoundFieldValue(fieldName);
    if(fieldName == "StringField")
    {
      value = int.Parse((string)value);
    }
    return value;
  }
}

class FlexibleTreeViewEx : FlexibleTreeView
{
  protected override BindableNode CreateBoundNode(object id)
  {
    return new MyBindableNode(id);
  }
}
Class MyBindableNode
    Inherits BindableNode
    Public Sub New(id As Object)
        MyBase.New(id)
    End Sub

    Public Overrides Function GetBoundFieldValue(fieldName As String) As Object
        Dim value As Object = MyBase.GetBoundFieldValue(fieldName)
        If fieldName = "StringField" Then
            value = Integer.Parse(DirectCast(value, String))
        End If
        Return value
    End Function
End Class

Class FlexibleTreeViewEx
    Inherits FlexibleTreeView
    Protected Overrides Function CreateBoundNode(id As Object) As BindableNode
        Return New MyBindableNode(id)
    End Function
End Class

Lazy load (load on demand)#

By default, Flexible TreeView creates nodes for all objects in a data source. If you require lazy loading to speed up data source loading for any node, subscribe to the NodePopulating event. In the event handler, cancel the process of adding nodes that should be loaded later by setting the Cancel parameter to true, and set LoadOnDemand to true for all nodes whose children you need to load later.

Example

void tree_NodePopulating(FlexibleTreeView treeview, NodePopulatingEventArgs args)
{
  // Enable lazy load for all nodes because we don't know whether this node has children.
  args.Node.LoadOnDemand = true;

  // Do not add non-root nodes, load them later.
  if (args.ParentNode != null)
  {
    args.Cancel = true;
  }
}

// Lazy loading
private void tree_NodeExpanding(FlexibleTreeView treeview, NodeExpandingEventArgs args)
{
  // Lazy load for the child node
  if (args.Node.LoadOnDemand)
  {
    // Disable lazy load for the loaded node
    args.Node.LoadOnDemand = false;

    // Load node's children here...
  }
}
Private Sub tree_NodePopulating(treeview As FlexibleTreeView, args As NodePopulatingEventArgs)
    ' Enable lazy load for all nodes because we don't know whether this node has children.
    args.Node.LoadOnDemand = True

    ' Do not add non-root nodes, load them later.
    If args.ParentNode IsNot Nothing Then
        args.Cancel = True
    End If
End Sub

' Lazy loading
Private Sub tree_NodeExpanding(treeview As FlexibleTreeView, args As NodeExpandingEventArgs)
    ' Lazy load for the child node
    If args.Node.LoadOnDemand Then
        ' Disable lazy load for the loaded node
        args.Node.LoadOnDemand = False

        ' Load node's children here...
    End If
End Sub

See below to learn how to add custom nodes to the treeview with a bound data source.

Add custom columns, node controls, nodes into a bound treeview#

Many treeview controls become a black box after binding a data source, not allowing user changes or additions. Flexible TreeView, however, allows full customization of the treeview even after binding the data source in both bound and unbound modes.

Add column and node controls#

To add custom columns or node controls to the treeview, add a TreeColumn instance to the Columns property or a NodeControl (or its inheritor) instance to the NodeControls property after binding the data source.

Add node#

Flexible TreeView works with bound data according to the binding mode - bound or unbound.

In bound mode, Flexible TreeView stores only references to the bound objects and changes data directly. In unbound mode, Flexible TreeView copies all fields of the bound object that need to be shown.

Each node in a treeview with a bound data source inherits from the BindableNode type, which allows an object to be bound to a data source and store all its fields. The method of adding a node to a treeview depends on the binding type with the data source.

The Order class shown below is a data object that will be stored in a data source as shown further in the examples.

class Order
{
  // Class properties go here
}
Class Order
  ' Class properties go here
End Class

Add node in bound mode#

Let's assume that after binding the data source, we need to add one more object of type Order to the data source and treeview. Create a new object and add it to the data source:

Note

Replace object id here value below with the unique identifier of the new object. The string type is used for example only, you can use identifiers of any type.

Order order = new Order();
// Here 'list' is our data source
list.Add(order);   

// Add a new object to the treeview by creating a BindableNode instance and passing our new Order object to the constructor
BindableNode node = new BindableNode("object id here", order);
node.AttachTo(tree);
Dim order As New Order()
' Here 'list' is our data source
list.Add(order)

' Add a new object to the treeview by creating a BindableNode instance and passing our new Order object to the constructor
Dim node As New BindableNode("object id here", order)
node.AttachTo(tree)

That's it! The new object is added to the treeview.

Tip

You don't need to add new objects to your data source separately. Passing the object to the node constructor is sufficient.

Add node in unbound mode#

In unbound mode, the process is similar to bound mode, but node creation uses a different constructor. Pass a Dictionary<string, object> containing the names of properties to show in the treeview and their values:

Note

Replace object id here value below with the unique identifier of the new object. The string type is used for example only, you can use identifiers of any type.

// Acquire list of properties and their values using the DataSourceManager class
Dictionary<string, object> fieldValues = DataSourceManager.Current.GetFieldValues(order);

// Create and add a new node
BindableNode node = new BindableNode("object id here", fieldValues);
node.AttachTo(tree);
' Acquire list of properties and their values using the DataSourceManager class
Dim fieldValues As Dictionary(Of String, Object) = DataSourceManager.Current.GetFieldValues(order)

' Create and add a new node
Dim node As New BindableNode("object id here", fieldValues)
node.AttachTo(tree)

Warning

In unbound mode, changes made by the user in the treeview will not be reflected in your data object.

If you need to add nodes bound to instances of a different type than Order, follow the instructions above using an instance of your type. Your data type must have the same properties as the Order type.

Add node without linked object#

You can add a virtual object to the treeview, supplying needed data yourself without creating an instance. Inherit a class from BindableNode and override the GetBoundFieldValue, SetBoundFieldValue, and GetBoundFieldType methods as shown below:

// Custom node class that holds the bound value inside
class VirtualBindableNode : BindableNode
{
  private string _userNameValue = "John Smith";  // Initial data value

  protected override Type GetBoundFieldType(string fieldName)
  {
    if(fieldName == "UserName")
    {
      return typeof(string);
    }
    return base.GetBoundFieldType(fieldName);
  }

  public override object GetBoundFieldValue(string fieldName)
  {
    if(fieldName == "UserName")
    {
      return _userNameValue;
    }

    return base.GetBoundFieldValue(fieldName);
  }

  public override void SetBoundFieldValue(string fieldName, object value)
  {
    if(fieldName == "UserName")
    {
      _userNameValue = (string)value;
    }
    base.SetBoundFieldValue(fieldName, value);
  }
}

// Use our custom node with a NodeTextBox node control to edit the node data
NodeTextBox tb = new NodeTextBox();
tb.AttachTo(tree);
tb.DataFieldName = "UserName";
tb.Editable = true;
tb.EditStartMode = eEditStartMode.Click;
tb.IsInBoundMode = true;

VirtualBindableNode node = new VirtualBindableNode();
node.AttachTo(tree);
' Custom node class that holds the bound value inside
Class VirtualBindableNode
    Inherits BindableNode
    Private _userNameValue As String = "John Smith"
    ' Initial data value
    Protected Overrides Function GetBoundFieldType(fieldName As String) As Type
        If fieldName = "UserName" Then
            Return GetType(String)
        End If
        Return MyBase.GetBoundFieldType(fieldName)
    End Function

    Public Overrides Function GetBoundFieldValue(fieldName As String) As Object
        If fieldName = "UserName" Then
            Return _userNameValue
        End If
        Return MyBase.GetBoundFieldValue(fieldName)
    End Function

    Public Overrides Sub SetBoundFieldValue(fieldName As String, value As Object)
        If fieldName = "UserName" Then
            _userNameValue = DirectCast(value, String)
        End If
        MyBase.SetBoundFieldValue(fieldName, value)
    End Sub
End Class

' Use our custom node with a NodeTextBox node control to edit the node data
Dim tb As New NodeTextBox()
tb.AttachTo(tree)
tb.DataFieldName = "UserName"
tb.Editable = True
tb.EditStartMode = eEditStartMode.Click
tb.IsInBoundMode = True

Dim node As New VirtualBindableNode()
node.AttachTo(tree)

The GetBoundFieldType method defines the data type of the virtually bound property, while GetBoundFieldValue and SetBoundFieldValue methods retrieve and save the bound data, respectively.

Warning

Enable the IsInBoundMode node control property to force the node control to manage bound data via GetBoundFieldValue and SetBoundFieldValue bindable node methods.