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
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:
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
.
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.
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.
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.
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.