These samples will show the basic handling of the treelist control, to create a structure and react to clicks by the user. Used in this example is the treelist control from the DevExpress suit, but you can apply the same technique to basicaly any treelist control with minimal changes. The first example shows how to manualy build a tree from code, and then react to clicks. The second example archives the exact same tree, but retrieves it’s data from a simple sql server table, making the content of the tree list much more dynamic. The final result for both the manualy created treelist and the data bound treelist will look like this: Image The code in the treeList1_CustomDrawNodeCell event was taken from the DevExpress documentation channel, which provides an excelent introduction to working with treelist controls. The movie can be found here
Manualy building a treelist
This first example shows how to create a treeview manualy. Top nodes are created, which receive a sub node referencing to them, and then finaly item nodes are added. Altho this sample uses two levels, you can create any amount of levels in any of the tree nodes by simply adding more sub nodes on the places where you need them. The sample uses the FocusedNodeChanged event to detect the user clicking on nodes. This is the point where the rest of the application could hook in to handle the event if you are using the tree list as a menu like structure.
using System; using System.Drawing; using System.Windows.Forms; using DevExpress.XtraTreeList; using DevExpress.XtraTreeList.Nodes; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } /// <summary> /// Populate the Treelist. /// In the designer mode a single column has been added with the NAME property set to colName and the FIELDNAME property set to name /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Form1_Load(object sender, EventArgs e) { // Look and feel TreeListNode childNode = null; treeList1.LookAndFeel.UseDefaultLookAndFeel = false; treeList1.LookAndFeel.SkinName = "iMaginary"; treeList1.OptionsBehavior.Editable = false; treeList1.OptionsView.ShowColumns = false; treeList1.OptionsView.ShowIndicator = false; treeList1.OptionsView.ShowHorzLines = false; treeList1.OptionsView.ShowVertLines = false; // Finance (top node) TreeListNode financeNode = treeList1.AppendNode(null, null); financeNode.SetValue("name", "Finance"); // Finance - Revenues (sub node) TreeListNode financeOmzettenNode = treeList1.AppendNode(null, financeNode); financeOmzettenNode.SetValue("name", "Revenues"); // Items childNode = treeList1.AppendNode(null, financeOmzettenNode); childNode.SetValue("name", "All Revenues"); childNode = treeList1.AppendNode(null, financeOmzettenNode); childNode.SetValue("name", "Webshop Revenues"); // Finance - Margins (sub node) TreeListNode financeMargeNode = treeList1.AppendNode(null, financeNode); financeMargeNode.SetValue("name", "Margins"); // Items childNode = treeList1.AppendNode(null, financeMargeNode); childNode.SetValue("name", "Average margin per supplier"); childNode = treeList1.AppendNode(null, financeMargeNode); childNode.SetValue("name", "Margins per product for a supplier"); // Logistics TreeListNode logisticsNode = treeList1.AppendNode(null, null); logisticsNode.SetValue("name", "Logistics"); // ... subnodes ... items // Customers TreeListNode customersNode = treeList1.AppendNode(null, null); customersNode.SetValue("name", "Customers"); // ... subnodes ... items } /// <summary> /// Highlight the clicked node /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void treeList1_CustomDrawNodeCell(object sender, CustomDrawNodeCellEventArgs e) { TreeList tl = sender as TreeList; if (e.Node == tl.FocusedNode) { e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds); Rectangle rect = new Rectangle(e.EditViewInfo.ContentRect.Left, e.EditViewInfo.ContentRect.Top, Convert.ToInt32(e.Graphics.MeasureString(e.CellText, treeList1.Font).Width + 1), Convert.ToInt32(e.Graphics.MeasureString(e.CellText, treeList1.Font).Height)); e.Graphics.FillRectangle(SystemBrushes.Highlight, rect); e.Graphics.DrawString(e.CellText, treeList1.Font, SystemBrushes.HighlightText, rect); e.Handled = true; } } /// <summary> /// Detect and respond to clicks on the nodes /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void treeList1_FocusedNodeChanged(object sender, FocusedNodeChangedEventArgs e) { string sNodename = e.Node.GetDisplayText("name"); if (sNodename != String.Empty) { MessageBox.Show(e.Node.GetDisplayText("name")); } } } }
Building a treelist from a data table
This second example uses data binding to archieve the exact same result. The data we are going to supply to the treelist looks like this:
The key here is the way the data links to itsself. The first node has id 1000 and parent id 1000 and therefore is a root node. The second level node has id 1100 and has the parentid 1000 making int a child of the Finance line. The final two options with id 1101 and 1102 point to 1100 as the parentid making them sub nodes from Revenues node.
using System; using System.Data; using System.Drawing; using System.Windows.Forms; using DevExpress.XtraTreeList; using DevExpress.XtraTreeList.Nodes; using System.Data.SqlClient; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } /// <summary> /// Populate the Treelist. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Form1_Load(object sender, EventArgs e) { // Look and feel TreeListNode childNode = null; treeList1.LookAndFeel.UseDefaultLookAndFeel = false; treeList1.LookAndFeel.SkinName = "iMaginary"; treeList1.OptionsBehavior.Editable = false; treeList1.OptionsView.ShowColumns = false; treeList1.OptionsView.ShowIndicator = false; treeList1.OptionsView.ShowHorzLines = false; treeList1.OptionsView.ShowVertLines = false; // Retrieve the data table that holds the treelist information from the sql server string sConstring = "Data Source=VERTEX;Initial Catalog=AReporter;Integrated Security=True"; using (var oCmd = new SqlCommand("select * from repo")) { var oTable = GetDataTableFromCommand(oCmd, sConstring); treeList1.DataSource = oTable; treeList1.KeyFieldName = "id"; treeList1.ParentFieldName = "ParentID"; } } /// <summary> /// Highlight the clicked node /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void treeList1_CustomDrawNodeCell(object sender, CustomDrawNodeCellEventArgs e) { TreeList tl = sender as TreeList; if (e.Node == tl.FocusedNode) { e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds); Rectangle rect = new Rectangle(e.EditViewInfo.ContentRect.Left, e.EditViewInfo.ContentRect.Top, Convert.ToInt32(e.Graphics.MeasureString(e.CellText, treeList1.Font).Width + 1), Convert.ToInt32(e.Graphics.MeasureString(e.CellText, treeList1.Font).Height)); e.Graphics.FillRectangle(SystemBrushes.Highlight, rect); e.Graphics.DrawString(e.CellText, treeList1.Font, SystemBrushes.HighlightText, rect); e.Handled = true; } } /// <summary> /// Detect and respond to clicks on the nodes /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void treeList1_FocusedNodeChanged(object sender, FocusedNodeChangedEventArgs e) { if (e.Node == null) { return; } string sNodename = e.Node.GetDisplayText("Name"); if (sNodename != String.Empty &amp;amp;amp;amp;amp;amp;amp; sNodename != null) { MessageBox.Show(e.Node.GetDisplayText("Name")); } } /// <summary> /// Run the passed SQL Command against the database, and return any results as a datatable object /// </summary> /// <param name="oCmd"></param> /// <param name="sConString"></param> /// <returns></returns> /// <remarks></remarks> public static DataTable GetDataTableFromCommand(SqlCommand oCmd, string sConString) { using (SqlConnection oCon = new SqlConnection(sConString)) { oCon.Open(); oCmd.Connection = oCon; using (SqlDataAdapter oDa = new SqlDataAdapter(oCmd)) { using (DataSet oDs = new DataSet()) { oDa.Fill(oDs); return oDs.Tables[0].Copy(); } } } } } }
This second example binds against a simple datatable, that is retrieved from an sql server with the GetDataTableFromCommand command. You can of course use data from any source you want and place it in the datatable for binding. Additionaly note that in the second example a treelist control is placed on the form with no properties or designer settings made in the gui, All settings are made from code.
Using the click results
The final example makes the treelist more pratical. I added a new column to the table with a description for some of the items, which will be displayed in a textbox. The column Name has been made unique, so we can distinguish all options in the list from eachother. To prevent the descriptions from showing up in the treelist, the treelist data table now sepcificaly only retrieves the fields it needs. Once an option is clicked, the description is looked up and displayed to the user. The images below show the new version of the datatable and our mini-gui with the added textbox control.
the columns id and parentid are of type integer and unique, the columns name and description are nvarchar type. Name needs to be unique too to be able to distinguish the options from eachother. The sourcecode now looks like this;
using System; using System.Data; using System.Drawing; using System.Windows.Forms; using DevExpress.XtraTreeList; using DevExpress.XtraTreeList.Nodes; using System.Data.SqlClient; namespace WindowsFormsApplication1 { public partial class Form1 : Form { private const string sConstring = "Data Source=VERTEX;Initial Catalog=AReporter;Integrated Security=True"; public Form1() { InitializeComponent(); } /// <summary> /// Populate the Treelist. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Form1_Load(object sender, EventArgs e) { // Look and feel TreeListNode childNode = null; treeList1.LookAndFeel.UseDefaultLookAndFeel = false; treeList1.LookAndFeel.SkinName = "iMaginary"; treeList1.OptionsBehavior.Editable = false; treeList1.OptionsView.ShowColumns = false; treeList1.OptionsView.ShowIndicator = false; treeList1.OptionsView.ShowHorzLines = false; treeList1.OptionsView.ShowVertLines = false; // Retrieve the data table that holds the treelist information from the sql server using (var oCmd = new SqlCommand("select id,ParentID,Name from repo")) { var oTable = GetDataTableFromCommand(oCmd, sConstring); treeList1.DataSource = oTable; treeList1.KeyFieldName = "id"; treeList1.ParentFieldName = "ParentID"; } } /// <summary> /// Highlight the clicked node /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void treeList1_CustomDrawNodeCell(object sender, CustomDrawNodeCellEventArgs e) { TreeList tl = sender as TreeList; if (e.Node == tl.FocusedNode) { e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds); Rectangle rect = new Rectangle(e.EditViewInfo.ContentRect.Left, e.EditViewInfo.ContentRect.Top, Convert.ToInt32(e.Graphics.MeasureString(e.CellText, treeList1.Font).Width + 1), Convert.ToInt32(e.Graphics.MeasureString(e.CellText, treeList1.Font).Height)); e.Graphics.FillRectangle(SystemBrushes.Highlight, rect); e.Graphics.DrawString(e.CellText, treeList1.Font, SystemBrushes.HighlightText, rect); e.Handled = true; } } /// <summary> /// Detect and respond to clicks on the nodes /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void treeList1_FocusedNodeChanged(object sender, FocusedNodeChangedEventArgs e) { if (e.Node == null) { return; } string sNodename = e.Node.GetDisplayText("Name"); if (sNodename != String.Empty &amp;amp;amp; sNodename != null) { string sReportname = e.Node.GetDisplayText("Name"); DisplayDescription(sReportname); } } /// <summary> /// This procedure will retrieve the description from the clicked item and display it in the textbox /// </summary> /// <param name="sName"></param> public void DisplayDescription(string sName) { using (var oCon = new SqlConnection(sConstring)) { oCon.Open(); using (var oCmd = new SqlCommand("select description from repo where Name=@key", oCon)) { oCmd.Parameters.AddWithValue("@key", sName); using (var oReader = oCmd.ExecuteReader()) { oReader.Read(); string sDescription = oReader["description"].ToString(); textBoxDescription.Text = sDescription; } } } } /// <summary> /// Run the passed SQL Command against the database, and return any results as a datatable object /// </summary> /// <param name="oCmd"></param> /// <param name="sConString"></param> /// <returns></returns> /// <remarks></remarks> public static DataTable GetDataTableFromCommand(SqlCommand oCmd, string sConString) { using (SqlConnection oCon = new SqlConnection(sConString)) { oCon.Open(); oCmd.Connection = oCon; using (SqlDataAdapter oDa = new SqlDataAdapter(oCmd)) { using (DataSet oDs = new DataSet()) { oDa.Fill(oDs); return oDs.Tables[0].Copy(); } } } } } }
As you can see in the last example, in a mere 130 lines of codes we created a tree like menu system that reacts to clicks of the user and displays additional data. With this framework you can make any kind of data into a menu like system and use the results for further processing in your application.
2011-10-17 Update Treelist demo with SqlCe and downloadable project
This version is an updated version of the previous treelist demo, and comes with a downloadable project. Changes compared to the samples above are:
- uses SqlCE so i could easily include a database file with correct field types and “unique” requirements
- Moved the init from the load to the shown event to allow easyer tinkering on 64 bit windows
- Included the ID from the clicked record on the forms output
- Minor refactoring
using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.SqlServerCe; using System.Drawing; using System.Linq; using System.Windows.Forms; using DevExpress.XtraTreeList; using DevExpress.XtraTreeList.Nodes; namespace TreeListSample { public partial class Form1 : Form { // Local database file with the demo data private const string SConstring = @"Data Source=|DataDirectory|\DatabaseDemo.sdf"; public Form1() { InitializeComponent(); } // // Populate the treelist. Initialy this code was in the LOAD event, but for demo purposes // it is more practical in the shown event in 64 bit systems. // see: http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/ // private void Form1Shown(object sender, EventArgs e) { // Look and feel TreeListNode childNode = null; treeList1.LookAndFeel.UseDefaultLookAndFeel = false; treeList1.LookAndFeel.SkinName = "iMaginary"; treeList1.OptionsBehavior.Editable = false; treeList1.OptionsView.ShowColumns = false; treeList1.OptionsView.ShowIndicator = false; treeList1.OptionsView.ShowHorzLines = false; treeList1.OptionsView.ShowVertLines = false; // Retrieve the data table that holds the treelist information from the sql server using (var oCmd = new SqlCeCommand("select id,ParentID,Name from TreeListDemoData")) { var oTable = GetDataTableFromCommand(oCmd, SConstring); treeList1.DataSource = oTable; treeList1.KeyFieldName = "id"; treeList1.ParentFieldName = "ParentID"; } } /// <summary> /// Highlight the clicked node /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void TreeList1CustomDrawNodeCell(object sender, CustomDrawNodeCellEventArgs e) { var tl = sender as TreeList; if (e.Node == tl.FocusedNode) { e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds); var rect = new Rectangle(e.EditViewInfo.ContentRect.Left, e.EditViewInfo.ContentRect.Top, Convert.ToInt32(e.Graphics.MeasureString(e.CellText, treeList1.Font).Width + 1), Convert.ToInt32(e.Graphics.MeasureString(e.CellText, treeList1.Font).Height)); e.Graphics.FillRectangle(SystemBrushes.Highlight, rect); e.Graphics.DrawString(e.CellText, treeList1.Font, SystemBrushes.HighlightText, rect); e.Handled = true; } } /// <summary> /// Detect and respond to clicks on the nodes /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void TreeList1FocusedNodeChanged(object sender, FocusedNodeChangedEventArgs e) { if (e.Node == null) { return; } string sNodename = e.Node.GetDisplayText("Name"); if (!string.IsNullOrEmpty(sNodename)) { string sReportname = e.Node.GetDisplayText("Name"); DisplayDescription(sReportname); } } /// <summary> /// This procedure will retrieve the description and the ID from the clicked item and display it in the textbox /// </summary> /// <param name="sName"></param> public void DisplayDescription(string sName) { using (var oCon = new SqlCeConnection(SConstring)) { oCon.Open(); using (var oCmd = new SqlCeCommand("select id, description from TreeListDemoData where Name=@key", oCon)) { oCmd.Parameters.AddWithValue("@key", sName); using (var oReader = oCmd.ExecuteReader()) { oReader.Read(); string sDescription = oReader["description"].ToString(); int nId = int.Parse(oReader["id"].ToString()); textBoxDescription.Text = sDescription; textBoxID.Text = nId.ToString(); } } } } /// <summary> /// Run the passed SQL Command against the database, and return any results as a datatable object /// </summary> /// <param name="oCmd"></param> /// <param name="sConString"></param> /// <returns></returns> /// <remarks></remarks> public static DataTable GetDataTableFromCommand(SqlCeCommand oCmd, string sConString) { using (var oCon = new SqlCeConnection(sConString)) { oCon.Open(); oCmd.Connection = oCon; using (var oDa = new SqlCeDataAdapter(oCmd)) { using (var oDs = new DataSet()) { oDa.Fill(oDs); return oDs.Tables[0].Copy(); } } } } } }
Filling the ASP.NET AspxTreelist from a LINQ data source
Here is a sample populating the AspxTreelist from code, using a LINQ data source.
// Build the AspxTreelist with as main list the group's, and as nested node's the categories private void BuildTreeList() { ASPxTreeList1.Columns.Add(new TreeListDataColumn("name", "ContentGroup")); int key = 0; var grouplist = from tabgroup in _dc.PageGroup orderby tabgroup.ContentGroup select tabgroup; foreach (var grp in grouplist) { _logger.LogInfo("grp:" + grp.ContentGroup); TreeListNode masternode = ASPxTreeList1.AppendNode(key, null); masternode.SetValue("name", grp.ContentGroup); masternode.Expanded = true; // expand by default key++; var catlist = from tabcats in _dc.PageCategorie orderby tabcats.CatName where tabcats.CatParent == grp.Id select tabcats; foreach (var cat in catlist) { _logger.LogInfo("cat: " + cat.CatName); var childnode = ASPxTreeList1.AppendNode(key, masternode); childnode.SetValue("name", cat.CatName); key++; } } }