XtraTreeList feature preview: business objects binding

WinForms Team Blog
01 June 2006

   The XtraTreeList is a powerful control for visualizing and editing hierarchical data. The XtraTreeList v6.1 can be bound to any standard datasource or an IList instance, or it can be used in unbound mode. In v6.1 unbound mode was presented by the AppendNode() method. We have discovered that unbound mode is often used for visualizing/editing the structure of business objects. But it takes a lot of time to get your objects shown in the XtraTreeList since you need to write a lot of code which constructs the desired tree by repeatedly calling AppendNode().

  In XtraTreeList v6.2 we decided to implement binding of business objects. You will be able to write the following code:

myTreeList.DataSource = myBusinessObject;

How does business objects binding work? Take a look at the picture below.

   Since the TreeList does not know the business object structure it fires events that will be processed in the user code. These events are InitChildren and InitCell. On InitChildren your code should return the children list via InitChildrenInfo.Children. On InitCell your code should return the required cell's value via InitCellInfo.CellData.
Another approach is to get the business object's structure via the interface. This idea is illustrated in the following diagram.

Well, let's see how business object binding saves you time. We have rewritten the TreeListExplorer demo, so let's compare the code that creates the tree.

Old version (append node approach - you can find it in the v6.1 TreeListExplorer demo sources)

  
   private bool HasFiles(string path) {
    string[] root = Directory.GetFiles(path);
    if(root.Length > 0) return true;
    root = Directory.GetDirectories(path);
    if(root.Length > 0) return true;
    return false;
   }
   private void InitFolders(string path, TreeListNode pNode, CheckState check) {
   treeList1.BeginUnboundLoad();
   TreeListNode node;
   DirectoryInfo di;
   try {
    string[] root = Directory.GetDirectories(path);
    foreach(string s in root) {
     try {
      di = new DirectoryInfo(s);
      node = treeList1.AppendNode(new object[] {s, di.Name, "Folder", null, di.Attributes, check}, pNode);
      node.HasChildren = HasFiles(s);
      if(node.HasChildren)
       node.Tag = true;
     } catch {}
    }
   }
   catch {}
   InitFiles(path, pNode, check);
   treeList1.EndUnboundLoad();
  } 

  private void InitFiles(string path, TreeListNode pNode, CheckState check) {
   TreeListNode node;
   FileInfo fi;
   try {
    string[] root = Directory.GetFiles(path);
    foreach(string s in root) {
     fi = new FileInfo(s);
     node = treeList1.AppendNode(new object[] {s, fi.Name, "File", fi.Length, fi.Attributes, check}, pNode);
     node.HasChildren = false;
    }
   } catch {}
  }
  
  private void InitDrives() {
   treeList1.BeginUnboundLoad();
   TreeListNode node;
   try {
    string[] root = Directory.GetLogicalDrives();
    
    foreach(string s in root) {
     node = treeList1.AppendNode(new object[] {s, s, "Logical Driver", null, null, CheckState.Unchecked}, null);
     node.HasChildren = true;
     node.Tag = true;
    }
   } catch {}
   treeList1.EndUnboundLoad();
  }
  private void InitRoot() {
   if(!toolBar1.Buttons[0].Pushed)
    InitFolders(Directory.GetDirectoryRoot(Directory.GetCurrentDirectory()), null, CheckState.Unchecked);
   else
    InitDrives();
  }

  private void treeList1_BeforeExpand(object sender, DevExpress.XtraTreeList.BeforeExpandEventArgs e) {
   if(e.Node.Tag != null) {
    Cursor currentCursor = Cursor.Current;
    Cursor.Current = Cursors.WaitCursor;
    InitFolders(e.Node.GetDisplayText("FullName"), e.Node, (CheckState)e.Node.GetValue("Check"));
    e.Node.Tag = null;
    Cursor.Current = currentCursor;
   }
  }
 }

New version (business objects binding approach)

private void treeList1_InitCell(object sender, DevExpress.XtraTreeList.InitCellInfo e) {
   DirectoryInfo di = new DirectoryInfo((string)e.Node);
   if (e.Column == treeListColumn1) 
     e.CellData = di.Name;
   if (e.Column == treeListColumn2) {
    if (!IsFile(di))
     e.CellData = "Folder";
    else 
     e.CellData = "File";
   }
   if (e.Column == treeListColumn3) {
    if (IsFile(di)){
     e.CellData = new FileInfo((string)e.Node).Length;
    }
    else e.CellData = null;
    
   }
  }

  private void treeList1_InitChildren(object sender, DevExpress.XtraTreeList.InitChildrenInfo e) {
   if (loadDrives == false){ // create drives
    string[] root = Directory.GetLogicalDrives();
    e.Children = root;
    loadDrives = true;
   }
   else {
    try{
     string path  = (string)e.Node;
     string[] dirs = Directory.GetDirectories(path);
     string[] files = Directory.GetFiles(path);
     string[] arr = new string[dirs.Length + files.Length];
     dirs.CopyTo(arr,0);
     files.CopyTo(arr,dirs.Length);
     e.Children = arr;
    }
    catch { e.Children = new object[]{}; }
   }
  }

   Let's analyze the differences. 

  • The code was reduced by at least half and is easier to read and understand.
  • There are no for loops in the tree's construction code.
  • You do not need to handle the BeforeExpand event since child nodes are initialized by demand when a parent is expanded
  • Like in the append node approach, you can control the data displayed by the TreeList based upon runtime conditions.

   This new business object binding feature will be available in v6.2. We have implemented both event-based and interface-based approaches.

 

2 comment(s)
Anonymous
Business Directory

I was just thinking about business objects binding  and you've really helped out. Thanks!

12 October, 2009
Anonymous
Broker Price Opinion

good post

12 October, 2009

Please login or register to post comments.