using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace uk.org.aspellclark.todolist
{
/// <summary>
/// Summary description for TreeList.
/// </summary>
public class TreeList : System.Windows.Forms.UserControl
{
private System.Windows.Forms.DataGrid _treeGrid;
private const int __TREE_COLUMN_SIZE__ = 200;
public TreeListNodeSelected NodeSelectedEvent;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public class TreeListNode
{
private object[] _columns;
private ArrayList _childs;
private TreeListNode _parent;
public TreeListNode(int cols)
{
_columns = new object[cols];
}
public TreeListNode(object[] cols)
{
_columns = cols;
}
public void AddChild(TreeListNode child)
{
if (_childs == null)
_childs = new ArrayList();
_childs.Add(child);
}
public object[] Columns
{
get { return _columns; }
set { _columns = value; }
}
public ArrayList ChildNodes
{
get { return _childs; }
set { _childs = value; }
}
public TreeListNode Parent
{
get { return _parent; }
set { _parent = value; }
}
}//class()
private class TreeListColumnStyle : DataGridTextBoxColumn
{
private const int __BOX_SIZE__ = 8;
private const int __BOX_MARGIN__ = 2;
private int _col;
public TreeListColumnStyle(int col)
{
_col = col;
}
protected override void Paint(System.Drawing.Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, Brush backBrush, Brush foreBrush, bool alignToRight)
{
if (_col == 0)
{
g.FillRectangle(backBrush, bounds);
DataRow row = ((DataRowView)source.List[rowNum]).Row;
bounds.X += GetXOffset(row);
object expanded = row["__IS_EXPANDED__"];
if (!Convert.IsDBNull(expanded))
{
Pen pen = new Pen(foreBrush);
int x = bounds.Left + __BOX_MARGIN__;
int y = bounds.Top + __BOX_MARGIN__ * 2;
g.DrawRectangle(pen, x, y, __BOX_SIZE__, __BOX_SIZE__);
g.DrawLine(pen, x + __BOX_MARGIN__, y + __BOX_SIZE__ / 2, x + __BOX_SIZE__ - __BOX_MARGIN__, y + __BOX_SIZE__ / 2);
if (!Convert.ToBoolean(expanded))
g.DrawLine(pen, x + __BOX_SIZE__ / 2, y + __BOX_MARGIN__, x + __BOX_SIZE__ / 2, y + __BOX_SIZE__ - __BOX_MARGIN__);
bounds.X += __BOX_SIZE__;
}
bounds.X += (__BOX_MARGIN__ * 3);
}
base.Paint(g, bounds, source, rowNum, backBrush, foreBrush, alignToRight);
}
private int GetXOffset(DataRow row)
{
object parent = row["__PARENT_ROW__"];
if (Convert.IsDBNull(parent))
return 0;
else
return __BOX_SIZE__ * 2 + GetXOffset((DataRow)parent);
}
protected override void Edit(System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly, string instantText, bool cellIsVisible)
{
// don't call the baseclass so no editing done...
// base.Edit(source, rowNum, bounds, readOnly, instantText, cellIsVisible);
}
}//class()
public TreeList()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
Clear();
}
base.Dispose( disposing );
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this._treeGrid = new System.Windows.Forms.DataGrid();
((System.ComponentModel.ISupportInitialize)(this._treeGrid)).BeginInit();
this.SuspendLayout();
//
// _treeGrid
//
this._treeGrid.CaptionVisible = false;
this._treeGrid.DataMember = "";
this._treeGrid.Dock = System.Windows.Forms.DockStyle.Fill;
this._treeGrid.GridLineStyle = System.Windows.Forms.DataGridLineStyle.None;
this._treeGrid.HeaderForeColor = System.Drawing.SystemColors.ControlText;
this._treeGrid.Location = new System.Drawing.Point(0, 0);
this._treeGrid.Name = "_treeGrid";
this._treeGrid.ReadOnly = true;
this._treeGrid.Size = new System.Drawing.Size(320, 256);
this._treeGrid.TabIndex = 0;
this._treeGrid.TabStop = false;
this._treeGrid.MouseDown += new System.Windows.Forms.MouseEventHandler(this.OnMouseDown);
this._treeGrid.MouseUp += new System.Windows.Forms.MouseEventHandler(this.OnMouseUp);
//
// TreeList
//
this.Controls.Add(this._treeGrid);
this.Name = "TreeList";
this.Size = new System.Drawing.Size(320, 256);
((System.ComponentModel.ISupportInitialize)(this._treeGrid)).EndInit();
this.ResumeLayout(false);
}
#endregion
private void SetColumnStyles()
{
CurrencyManager cm = (CurrencyManager)this.BindingContext[_treeGrid.DataSource];
((DataView)cm.List).AllowNew = false;
((DataView)cm.List).AllowEdit = false;
((DataView)cm.List).AllowDelete = false;
_treeGrid.ReadOnly = false;
DataTable dt = (DataTable)_treeGrid.DataSource;
DataGridTableStyle styles = new DataGridTableStyle(cm);
styles.GridColumnStyles.Clear();
styles.ReadOnly = true;
styles.AllowSorting = false;
styles.RowHeadersVisible = false;
styles.GridLineStyle = DataGridLineStyle.None;
for (int i = 0; i < dt.Columns.Count - 3; i++)
{
TreeListColumnStyle style = new TreeListColumnStyle(i);
style.ReadOnly = true;
style.Width = __TREE_COLUMN_SIZE__;
style.MappingName = dt.Columns[i].ColumnName;
style.HeaderText = dt.Columns[i].ColumnName;
style.TextBox.Visible = false;
style.NullText = "";
styles.GridColumnStyles.Add(style);
}
DataGridTextBoxColumn hstyle1 = new DataGridTextBoxColumn();
hstyle1.Width = 0;
styles.GridColumnStyles.Add(hstyle1);
DataGridBoolColumn hstyle2 = new DataGridBoolColumn();
hstyle2.Width = 0;
styles.GridColumnStyles.Add(hstyle2);
DataGridBoolColumn hstyle3 = new DataGridBoolColumn();
hstyle3.Width = 0;
styles.GridColumnStyles.Add(hstyle3);
_treeGrid.TableStyles.Add(styles);
}
public void SetColumns(string[] names)
{
_treeGrid.BeginInit();
DataTable dt = new DataTable();
for (int i = 0; i < names.Length; i++)
dt.Columns.Add(names[i]);
//add some hidden columns
DataColumn hcol1 = new DataColumn("__PARENT_ROW__", typeof(object));
hcol1.ColumnMapping = MappingType.Hidden;
dt.Columns.Add(hcol1);
DataColumn hcol2 = new DataColumn("__IS_EXPANDED__", typeof(bool));
hcol2.ColumnMapping = MappingType.Hidden;
dt.Columns.Add(hcol2);
DataColumn hcol3 = new DataColumn("__IS_VISIBLE__", typeof(bool));
hcol3.ColumnMapping = MappingType.Hidden;
dt.Columns.Add(hcol3);
_treeGrid.DataSource = dt;
SetColumnStyles();
_treeGrid.EndInit();
}
static private void AddNode(DataTable dt, TreeListNode node, DataRow parentRow)
{
if (node.Columns.Length != dt.Columns.Count - 3)
throw new Exception("Node values don't match expected columns.");
DataRow row = dt.NewRow();
for (int i = 0; i < node.Columns.Length; i++)
row[i] = node.Columns[i];
if (parentRow != null)
{
row["__PARENT_ROW__"] = parentRow;
node.Parent = (TreeList.TreeListNode)dt.ExtendedProperties[parentRow];
}
if (node.ChildNodes != null)
row["__IS_EXPANDED__"] = true;
row["__IS_VISIBLE__"] = true;
dt.Rows.Add(row);
dt.ExtendedProperties.Add(row, node);
if (node.ChildNodes != null)
{
foreach (TreeListNode child in node.ChildNodes)
AddNode(dt, child, row);
}
}
private void UpdateChildrenVisibility(DataTable dt, int startIdx, DataRow parentRow, bool visible)
{
for (int i = startIdx; i < dt.Rows.Count; i++)
{
DataRow row = dt.Rows[i];
object parent = row["__PARENT_ROW__"];
if (Convert.IsDBNull(parent))
continue;
if (!parentRow.Equals(parent))
continue;
row["__IS_VISIBLE__"] = visible;
if (!Convert.IsDBNull(row["__IS_EXPANDED__"]) && Convert.ToBoolean(row["__IS_EXPANDED__"]))
UpdateChildrenVisibility(dt, i + 1, row, visible);
}
}
private void OnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
return;
DataGrid.HitTestInfo hi = _treeGrid.HitTest(e.X, e.Y);
if (hi.Type != DataGrid.HitTestType.Cell)
return;
CurrencyManager source = (CurrencyManager)_treeGrid.BindingContext[_treeGrid.DataSource];
if (hi.Row >= 0 && hi.Row < source.List.Count)
{
_treeGrid.CurrentRowIndex = hi.Row;
DataRow parentRow = ((DataRowView)source.List[hi.Row]).Row;
object expanded = parentRow["__IS_EXPANDED__"];
if (Convert.IsDBNull(expanded))
return;
//this.dataGrid1.TableStyles[0].GridColumnStyles[0];
bool visibility = Convert.ToBoolean(expanded);
parentRow["__IS_EXPANDED__"] = !visibility;
UpdateChildrenVisibility((DataTable)_treeGrid.DataSource, hi.Row + 1, parentRow, !visibility);
}
}
private void OnMouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
_treeGrid.Select(_treeGrid.CurrentRowIndex);
if (NodeSelectedEvent != null)
NodeSelectedEvent(this.SelectedNode);
}
public void AddNode(TreeListNode node)
{
_treeGrid.SuspendLayout();
_treeGrid.BeginInit();
DataTable dt = (DataTable)_treeGrid.DataSource;
AddNode(dt, node, null);
_treeGrid.EndInit();
dt.DefaultView.RowFilter = "__IS_VISIBLE__=true";
_treeGrid.Select(0);
_treeGrid.ResumeLayout(false);
}
public void Clear()
{
if (_treeGrid.DataSource != null)
{
((DataTable)_treeGrid.DataSource).Dispose();
_treeGrid.DataSource = null;
_treeGrid.TableStyles.Clear();
}
}
public TreeList.TreeListNode SelectedNode
{
get
{
CurrencyManager cm = (CurrencyManager)_treeGrid.BindingContext[_treeGrid.DataSource];
DataRowView rv = (DataRowView)cm.List[_treeGrid.CurrentRowIndex];
return (TreeList.TreeListNode)((DataTable)_treeGrid.DataSource).ExtendedProperties[rv.Row];
}
}
}
public delegate void TreeListNodeSelected(TreeList.TreeListNode node);
}