|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionSome time ago, my task was to write something like a virtual file system. Of course, I decided to use typed Binding implementationFirst of all, we need a method to fill all the data in the tree view. For this, I store references of the data items in the ArrayList unsortedNodes = new ArrayList();
//This is list of items that still have no place in tree
for (int i = 0; i < this.listManager.Count; i++)
{
//Fill this list with all items.
unsortedNodes.Add(this.CreateNode(this.listManager, i));
}
int startCount;
//Iterate until list will not empty.
while (unsortedNodes.Count > 0)
{
startCount = unsortedNodes.Count;
for (int i = unsortedNodes.Count-1; i >= 0 ; i--)
{
if (this.TryAddNode((DataTreeViewNode)unsortedNodes[i]))
{
//Item found its place.
unsortedNodes.RemoveAt(i);
}
}
if (startCount == unsortedNodes.Count)
{
//Throw if nothing was done, in another way this
//will continuous loop.
throw new ApplicationException("Tree view confused
when try to make your data hierarchical.");
}
}
private bool TryAddNode(DataTreeViewNode node)
{
if (this.IsIDNull(node.ParentID))
{
//If parent is null this mean that this is root node.
this.AddNode(this.Nodes, node);
return true;
}
else
{
if (this.items_Identifiers.ContainsKey(node.ParentID))
{
//Parent already exists in tree so we can add item to it.
TreeNode parentNode =
this.items_Identifiers[node.ParentID] as TreeNode;
if (parentNode != null)
{
this.AddNode(parentNode.Nodes, node);
return true;
}
}
}
//Parent was not found at this point.
return false;
}
Respond to external data changesOkay… Now we have our tree view filled with all items. Second one that we need is to respond to external data changes. For this we need to handle the ((IBindingList)this.listManager.List).ListChanged +=
new ListChangedEventHandler(DataTreeView_ListChanged);
Realization of the handle is very simple. private void DataTreeView_ListChanged(object sender, ListChangedEventArgs e)
{
switch(e.ListChangedType)
{
case ListChangedType.ItemAdded:
//Add item here.
break;
case ListChangedType.ItemChanged:
//Change node associated with this item
break;
case ListChangedType.ItemMoved:
//Parent changed.
break;
case ListChangedType.ItemDeleted:
//Item removed
break;
case ListChangedType.Reset:
//This reset all data and control need to refill all data.
break;
}
}
Now our control particularly supports data binding. You are able to see data, it will change synchronously with external data. Selected nodeYou can ask what additional functionality is required? -Oh! this only the start. So next point: If you change the data source position our control will not change it. Currency manager has a this.listManager.PositionChanged +=
new EventHandler(listManager_PositionChanged);
At the start point, we added index for positions and nodes according to them. This gives us an easy way to find a node by its position. So this short code will give us the ability to find the selected node by its position. DataTreeViewNode node =
this.items_Positions[this.listManager.Position] as DataTreeViewNode;
if (node != null)
{
this.SelectedNode = node;
}
Current context positionNow you are not able to use this control as parent to your table. Basically all that we need is according to the selection of the node, change position of the context. This is not a problem as we store position of the item in each node. Make the private void DataTreeView_AfterSelect(object sender,
System.Windows.Forms.TreeViewEventArgs e)
{
DataTreeViewNode node = e.Node as DataTreeViewNode;
if (node != null)
{
//Set position.
this.listManager.Position = node.Position;
}
}
Label editingNot a problem! Just use private void DataTreeView_AfterLabelEdit(object sender,
System.Windows.Forms.NodeLabelEditEventArgs e)
{
DataTreeViewNode node = e.Node as DataTreeViewNode;
if (node != null)
{
//This will found appropriate converter for
//type and see if it can convert from string.
if (this.PrepareValueConvertor()
&& this.valueConverter.IsValid(e.Label)//Lets converter to check value.
)
{
//Set property.
this.nameProperty.SetValue(
this.listManager.List[node.Position],
this.valueConverter.ConvertFromString(e.Label)
);
this.listManager.EndCurrentEdit();
return;
}
}
//Node text are not editable.
e.CancelEdit = true;
}
Using the codeAs In most cases data must have three columns: Identifier, Name and Identifier of parent row. If you need something like 'FirstName + " " + LastName' as Name field - you can make autocomputed columns in I am not including image index in binding because I didn't need it. Let me know if you need this functionality. I will update this article. BonusesFirst bonus is full design time support. Unlike all other data bound trees on “Code Project”, this tree view has all standard designers that, for example, That’s all! Enjoy. Visit my blog for the latest news!
|
||||||||||||||||||||||||||||||