//TreeListView.cs |
using System; |
using System.ComponentModel; |
using System.Drawing; |
using System.Windows.Forms; |
namespace CommonTools |
{ |
[Designer( typeof (TreeListViewDesigner))] |
public class TreeListView : Control, ISupportInitialize |
{ |
public event TreeViewEventHandler AfterSelect; |
protected virtual void OnAfterSelect(Node node) |
{ |
raiseAfterSelect(node); |
} |
protected virtual void raiseAfterSelect(Node node) |
{ |
if (AfterSelect != null ) |
AfterSelect( this , new TreeViewEventArgs( null )); |
} |
public delegate void NotifyBeforeExpandHandler(Node node, bool isExpanding); |
public event NotifyBeforeExpandHandler NotifyBeforeExpand; |
public virtual void OnNotifyBeforeExpand(Node node, bool isExpanding) |
{ |
raiseNotifyBeforeExpand(node, isExpanding); |
} |
protected virtual void raiseNotifyBeforeExpand(Node node, bool isExpanding) |
{ |
if (NotifyBeforeExpand != null ) |
NotifyBeforeExpand(node, isExpanding); |
} |
public delegate void NotifyAfterHandler(Node node, bool isExpanding); |
public event NotifyAfterHandler NotifyAfterExpand; |
public virtual void OnNotifyAfterExpand(Node node, bool isExpanded) |
{ |
raiseNotifyAfterExpand(node, isExpanded); |
} |
protected virtual void raiseNotifyAfterExpand(Node node, bool isExpanded) |
{ |
if (NotifyAfterExpand != null ) |
NotifyAfterExpand(node, isExpanded); |
} |
TreeListViewNodes m_nodes; |
TreeListColumnCollection m_columns; |
TreeList.RowSetting m_rowSetting; |
TreeList.ViewSetting m_viewSetting; |
[Category( "Columns" )] |
[Browsable( true )] |
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] |
public TreeListColumnCollection Columns |
{ |
get { return m_columns; } |
} |
[Category( "Options" )] |
[Browsable( true )] |
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] |
public TreeList.CollumnSetting ColumnsOptions |
{ |
get { return m_columns.Options; } |
} |
[Category( "Options" )] |
[Browsable( true )] |
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] |
public TreeList.RowSetting RowOptions |
{ |
get { return m_rowSetting; } |
} |
|
[Category( "Options" )] |
[Browsable( true )] |
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] |
public TreeList.ViewSetting ViewOptions |
{ |
get { return m_viewSetting; } |
} |
[Category( "Behavior" )] |
[DefaultValue( typeof ( bool ), "True" )] |
public bool MultiSelect |
{ |
get { return m_multiSelect; } |
set { m_multiSelect = value; } |
} |
[DefaultValue( typeof (Color), "Window" )] |
public new Color BackColor |
{ |
get { return base .BackColor; } |
set { base .BackColor = value; } |
} |
public ImageList Images |
{ |
get { return m_images; } |
set { m_images = value; } |
} |
//[Browsable(false)] |
public TreeListViewNodes Nodes |
{ |
get { return m_nodes; } |
} |
public TreeListView() |
{ |
this .DoubleBuffered = true ; |
this .BackColor = SystemColors.Window; |
this .TabStop = true ; |
m_rowPainter = new RowPainter(); |
m_cellPainter = new CellPainter( this ); |
m_nodes = new TreeListViewNodes( this ); |
m_columns = new TreeListColumnCollection( this ); |
m_rowSetting = new TreeList.RowSetting( this ); |
m_viewSetting = new TreeList.ViewSetting( this ); |
AddScroolBars(); |
} |
public void RecalcLayout() |
{ |
if (m_firstVisibleNode == null ) |
m_firstVisibleNode = Nodes.FirstNode; |
if (Nodes.Count == 0) |
m_firstVisibleNode = null ; |
UpdateScrollBars(); |
int vscroll = VScrollValue(); |
if (vscroll == 0) |
m_firstVisibleNode = Nodes.FirstNode; |
else |
m_firstVisibleNode = NodeCollection.GetNextNode(Nodes.FirstNode, vscroll); |
Invalidate(); |
} |
void AddScroolBars() |
{ |
// I was not able to get the wanted behavior by using ScrollableControl with AutoScroll enabled. |
// horizontal scrolling is ok to do it by pixels, but for vertical I want to maintain the headers |
// and only scroll the rows. |
// I was not able to manually overwrite the vscroll bar handling to get this behavior, instead I opted for |
// custom implementation of scrollbars |
// to get the 'filler' between hscroll and vscroll I dock scroll + filler in a panel |
m_hScroll = new HScrollBar(); |
m_hScroll.Scroll += new ScrollEventHandler(OnHScroll); |
m_hScroll.Dock = DockStyle.Fill; |
m_vScroll = new VScrollBar(); |
m_vScroll.Scroll += new ScrollEventHandler(OnVScroll); |
m_vScroll.Dock = DockStyle.Right; |
m_hScrollFiller = new Panel(); |
m_hScrollFiller.BackColor = Color.Transparent; |
m_hScrollFiller.Size = new Size(m_vScroll.Width-1, m_hScroll.Height); |
m_hScrollFiller.Dock = DockStyle.Right; |
|
Controls.Add(m_vScroll); |
m_hScrollPanel = new Panel(); |
m_hScrollPanel.Height = m_hScroll.Height; |
m_hScrollPanel.Dock = DockStyle.Bottom; |
m_hScrollPanel.Controls.Add(m_hScroll); |
m_hScrollPanel.Controls.Add(m_hScrollFiller); |
Controls.Add(m_hScrollPanel); |
} |
|
VScrollBar m_vScroll; |
HScrollBar m_hScroll; |
Panel m_hScrollFiller; |
Panel m_hScrollPanel; |
bool m_multiSelect = true ; |
Node m_firstVisibleNode = null ; |
ImageList m_images = null ; |
RowPainter m_rowPainter; |
CellPainter m_cellPainter; |
[Browsable( false )] |
public CellPainter CellPainter |
{ |
get { return m_cellPainter; } |
set { m_cellPainter = value; } |
} |
TreeListColumn m_resizingColumn; |
int m_resizingColumnScrollOffset; |
NodesSelection m_nodesSelection = new NodesSelection(); |
Node m_focusedNode = null ; |
[Browsable( false )] |
public NodesSelection NodesSelection |
{ |
get { return m_nodesSelection; } |
} |
[Browsable( false )] |
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] |
public Node FocusedNode |
{ |
get { return m_focusedNode; } |
set |
{ |
Node curNode = FocusedNode; |
if ( object .ReferenceEquals(curNode, value)) |
return ; |
if (MultiSelect == false ) |
NodesSelection.Clear(); |
|
int oldrow = NodeCollection.GetVisibleNodeIndex(curNode); |
int newrow = NodeCollection.GetVisibleNodeIndex(value); |
|
m_focusedNode = value; |
OnAfterSelect(value); |
InvalidateRow(oldrow); |
InvalidateRow(newrow); |
EnsureVisible(m_focusedNode); |
} |
} |
public void EnsureVisible(Node node) |
{ |
int screenvisible = MaxVisibleRows() - 1; |
int visibleIndex = NodeCollection.GetVisibleNodeIndex(node); |
if (visibleIndex < VScrollValue()) |
{ |
SetVScrollValue(visibleIndex); |
} |
if (visibleIndex > VScrollValue() + screenvisible) |
{ |
SetVScrollValue(visibleIndex - screenvisible); |
} |
} |
public Node CalcHitNode(Point mousepoint) |
{ |
int hitrow = CalcHitRow(mousepoint); |
if (hitrow < 0) |
return null ; |
return NodeCollection.GetNextNode(m_firstVisibleNode, hitrow); |
} |
public Node GetHitNode() |
{ |
return CalcHitNode(PointToClient(Control.MousePosition)); |
} |
public CommonTools.HitInfo CalcColumnHit(Point mousepoint) |
{ |
return Columns.CalcHitInfo(mousepoint, HScrollValue()); |
} |
public bool HitTestScrollbar(Point mousepoint) |
{ |
if (m_hScroll.Visible && mousepoint.Y >= ClientRectangle.Height - m_hScroll.Height) |
return true ; |
return false ; |
} |
protected override void OnSizeChanged(EventArgs e) |
{ |
base .OnSizeChanged(e); |
if (ClientRectangle.Width > 0 && ClientRectangle.Height > 0) |
{ |
Columns.RecalcVisibleColumsRect(); |
UpdateScrollBars(); |
} |
} |
protected virtual void BeforeShowContextMenu() |
{ |
} |
protected void InvalidateRow( int absoluteRowIndex) |
{ |
int visibleRowIndex = absoluteRowIndex - VScrollValue(); |
Rectangle r = CalcRowRecangle(visibleRowIndex); |
if (r != Rectangle.Empty) |
{ |
r.Inflate(1,1); |
Invalidate(r); |
} |
} |
void OnVScroll( object sender, ScrollEventArgs e) |
{ |
int diff = e.NewValue - e.OldValue; |
//assumedScrollPos += diff; |
if (e.NewValue == 0) |
{ |
m_firstVisibleNode = Nodes.FirstNode; |
diff = 0; |
} |
m_firstVisibleNode = NodeCollection.GetNextNode(m_firstVisibleNode, diff); |
Invalidate(); |
} |
void OnHScroll( object sender, ScrollEventArgs e) |
{ |
Invalidate(); |
} |
void SetVScrollValue( int value) |
{ |
if (value < 0) |
value = 0; |
int max = m_vScroll.Maximum - m_vScroll.LargeChange + 1; |
if (value > max) |
value = max; |
if ((value >= 0 && value <= max) && (value != m_vScroll.Value)) |
{ |
ScrollEventArgs e = new ScrollEventArgs(ScrollEventType.ThumbPosition, m_vScroll.Value, value, ScrollOrientation.VerticalScroll); |
// setting the scroll value does not cause a Scroll event |
m_vScroll.Value = value; |
// so we have to fake it |
OnVScroll(m_vScroll, e); |
} |
} |
int VScrollValue() |
{ |
if (m_vScroll.Visible == false ) |
return 0; |
return m_vScroll.Value; |
} |
int HScrollValue() |
{ |
if (m_hScroll.Visible == false ) |
return 0; |
return m_hScroll.Value; |
} |
void UpdateScrollBars() |
{ |
if (ClientRectangle.Width < 0) |
return ; |
int maxvisiblerows = MaxVisibleRows(); |
int totalrows = Nodes.VisibleNodeCount; |
if (maxvisiblerows > totalrows) |
{ |
m_vScroll.Visible = false ; |
SetVScrollValue(0); |
} |
else |
{ |
m_vScroll.Visible = true ; |
m_vScroll.SmallChange = 1; |
m_vScroll.LargeChange = maxvisiblerows; |
m_vScroll.Minimum = 0; |
m_vScroll.Maximum = totalrows-1; |
int maxscrollvalue = m_vScroll.Maximum - m_vScroll.LargeChange; |
if (maxscrollvalue < m_vScroll.Value) |
SetVScrollValue(maxscrollvalue); |
} |
if (ClientRectangle.Width > MinWidth()) |
{ |
m_hScrollPanel.Visible = false ; |
m_hScroll.Value = 0; |
} |
else |
{ |
m_hScroll.Minimum = 0; |
m_hScroll.Maximum = MinWidth(); |
m_hScroll.SmallChange = 5; |
m_hScroll.LargeChange = ClientRectangle.Width; |
m_hScrollFiller.Visible = m_vScroll.Visible; |
m_hScrollPanel.Visible = true ; |
} |
} |
int m_hotrow = -1; |
int CalcHitRow(Point mousepoint) |
{ |
if (mousepoint.Y <= Columns.Options.HeaderHeight) |
return -1; |
return (mousepoint.Y - Columns.Options.HeaderHeight) / RowOptions.ItemHeight; |
} |
int VisibleRowToYPoint( int visibleRowIndex) |
{ |
return Columns.Options.HeaderHeight + (visibleRowIndex * RowOptions.ItemHeight); |
} |
Rectangle CalcRowRecangle( int visibleRowIndex) |
{ |
Rectangle r = ClientRectangle; |
r.Y = VisibleRowToYPoint(visibleRowIndex); |
if (r.Top < Columns.Options.HeaderHeight || r.Top > ClientRectangle.Height) |
return Rectangle.Empty; |
r.Height = RowOptions.ItemHeight; |
return r; |
} |
void MultiSelectAdd(Node clickedNode, Keys modifierKeys) |
{ |
if (Control.ModifierKeys == Keys.None) |
{ |
foreach (Node node in NodesSelection) |
{ |
int newrow = NodeCollection.GetVisibleNodeIndex(node); |
InvalidateRow(newrow); |
} |
NodesSelection.Clear(); |
NodesSelection.Add(clickedNode); |
} |
if (Control.ModifierKeys == Keys.Shift) |
{ |
if (NodesSelection.Count == 0) |
NodesSelection.Add(clickedNode); |
else |
{ |
int startrow = NodeCollection.GetVisibleNodeIndex(NodesSelection[0]); |
int currow = NodeCollection.GetVisibleNodeIndex(clickedNode); |
if (currow > startrow) |
{ |
Node startingNode = NodesSelection[0]; |
NodesSelection.Clear(); |
foreach (Node node in NodeCollection.ForwardNodeIterator(startingNode, clickedNode, true )) |
NodesSelection.Add(node); |
Invalidate(); |
} |
if (currow < startrow) |
{ |
Node startingNode = NodesSelection[0]; |
NodesSelection.Clear(); |
foreach (Node node in NodeCollection.ReverseNodeIterator(startingNode, clickedNode, true )) |
NodesSelection.Add(node); |
Invalidate(); |
} |
} |
} |
if (Control.ModifierKeys == Keys.Control) |
{ |
if (NodesSelection.Contains(clickedNode)) |
NodesSelection.Remove(clickedNode); |
else |
NodesSelection.Add(clickedNode); |
} |
InvalidateRow(NodeCollection.GetVisibleNodeIndex(clickedNode)); |
FocusedNode = clickedNode; |
} |
internal event MouseEventHandler AfterResizingColumn; |
protected override void OnMouseClick(MouseEventArgs e) |
{ |
if (e.Button == MouseButtons.Left) |
{ |
Point mousePoint = new Point(e.X, e.Y); |
Node clickedNode = CalcHitNode(mousePoint); |
if (clickedNode != null && Columns.Count > 0) |
{ |
int clickedRow = CalcHitRow(mousePoint); |
Rectangle glyphRect = GetPlusMinusRectangle(clickedNode, Columns.VisibleColumns[0], clickedRow); |
if (clickedNode.HasChildren && glyphRect != Rectangle.Empty && glyphRect.Contains(mousePoint)) |
clickedNode.Expanded = !clickedNode.Expanded; |
if (MultiSelect) |
{ |
MultiSelectAdd(clickedNode, Control.ModifierKeys); |
} |
else |
FocusedNode = clickedNode; |
} |
} |
base .OnMouseClick(e); |
} |
protected override void OnMouseMove(MouseEventArgs e) |
{ |
base .OnMouseMove(e); |
if (m_resizingColumn != null ) |
{ |
int left = m_resizingColumn.CalculatedRect.Left - m_resizingColumnScrollOffset; |
int width = e.X - left; |
if (width < 10) |
width = 10; |
m_resizingColumn.Width = width; |
Columns.RecalcVisibleColumsRect( true ); |
Invalidate(); |
return ; |
} |
TreeListColumn hotcol = null ; |
CommonTools.HitInfo info = Columns.CalcHitInfo( new Point(e.X, e.Y), HScrollValue()); |
if (( int )(info.HitType & HitInfo.eHitType.kColumnHeader) > 0) |
hotcol = info.Column; |
if (( int )(info.HitType & HitInfo.eHitType.kColumnHeaderResize) > 0) |
Cursor = Cursors.VSplit; |
else |
Cursor = Cursors.Arrow; |
SetHotColumn(hotcol, true ); |
int vScrollOffset = VScrollValue(); |
|
int newhotrow = -1; |
if (hotcol == null ) |
{ |
int row = (e.Y - Columns.Options.HeaderHeight) / RowOptions.ItemHeight; |
newhotrow = row + vScrollOffset; |
} |
if (newhotrow != m_hotrow) |
{ |
InvalidateRow(m_hotrow); |
m_hotrow = newhotrow; |
InvalidateRow(m_hotrow); |
} |
} |
protected override void OnMouseLeave(EventArgs e) |
{ |
base .OnMouseLeave(e); |
SetHotColumn( null , false ); |
} |
protected override void OnMouseWheel(MouseEventArgs e) |
{ |
int value = m_vScroll.Value - (e.Delta * SystemInformation.MouseWheelScrollLines / 120); |
if (m_vScroll.Visible) |
SetVScrollValue(value); |
base .OnMouseWheel(e); |
} |
protected override void OnMouseDown(MouseEventArgs e) |
{ |
this .Focus(); |
if (e.Button == MouseButtons.Right) |
{ |
Point mousePoint = new Point(e.X, e.Y); |
Node clickedNode = CalcHitNode(mousePoint); |
if (clickedNode != null ) |
{ |
// if multi select the selection is cleard if clicked node is not in selection |
if (MultiSelect) |
{ |
if (NodesSelection.Contains(clickedNode) == false ) |
MultiSelectAdd(clickedNode, Control.ModifierKeys); |
} |
FocusedNode = clickedNode; |
Invalidate(); |
} |
BeforeShowContextMenu(); |
} |
if (e.Button == MouseButtons.Left) |
{ |
CommonTools.HitInfo info = Columns.CalcHitInfo( new Point(e.X, e.Y), HScrollValue()); |
if (( int )(info.HitType & HitInfo.eHitType.kColumnHeaderResize) > 0) |
{ |
m_resizingColumn = info.Column; |
m_resizingColumnScrollOffset = HScrollValue(); |
return ; |
} |
} |
base .OnMouseDown(e); |
} |
protected override void OnMouseUp(MouseEventArgs e) |
{ |
if (m_resizingColumn != null ) |
{ |
m_resizingColumn = null ; |
Columns.RecalcVisibleColumsRect(); |
UpdateScrollBars(); |
Invalidate(); |
if (AfterResizingColumn != null ) |
AfterResizingColumn( this , e); |
} |
base .OnMouseUp(e); |
} |
protected override void OnMouseDoubleClick(MouseEventArgs e) |
{ |
base .OnMouseDoubleClick(e); |
Point mousePoint = new Point(e.X, e.Y); |
Node clickedNode = CalcHitNode(mousePoint); |
if (clickedNode != null && clickedNode.HasChildren) |
clickedNode.Expanded = !clickedNode.Expanded; |
} |
// Somewhere I read that it could be risky to do any handling in GetFocus / LostFocus. |
// The reason is that it will throw exception incase you make a call which recreates the windows handle (e.g. |
// change the border style. Instead one should always use OnEnter and OnLeave instead. That is why I'm using |
// OnEnter and OnLeave instead, even though I'm only doing Invalidate. |
protected override void OnEnter(EventArgs e) |
{ |
base .OnEnter(e); |
Invalidate(); |
} |
protected override void OnLeave(EventArgs e) |
{ |
base .OnLeave(e); |
Invalidate(); |
} |
void SetHotColumn(TreeListColumn col, bool ishot) |
{ |
int scrolloffset = HScrollValue(); |
if (col != m_hotColumn) |
{ |
if (m_hotColumn != null ) |
{ |
m_hotColumn.ishot = false ; |
Rectangle r = m_hotColumn.CalculatedRect; |
r.X -= scrolloffset; |
Invalidate(r); |
} |
m_hotColumn = col; |
if (m_hotColumn != null ) |
{ |
m_hotColumn.ishot = ishot; |
Rectangle r = m_hotColumn.CalculatedRect; |
r.X -= scrolloffset; |
Invalidate(r); |
} |
} |
} |
internal int RowHeaderWidth() |
{ |
if (RowOptions.ShowHeader) |
return RowOptions.HeaderWidth; |
return 0; |
} |
int MinWidth() |
{ |
return RowHeaderWidth() + Columns.ColumnsWidth; |
} |
int MaxVisibleRows( out int remainder) |
{ |
remainder = 0; |
if (ClientRectangle.Height < 0) |
return 0; |
int height = ClientRectangle.Height - Columns.Options.HeaderHeight; |
//return (int) Math.Ceiling((double)(ClientRectangle.Height - Columns.HeaderHeight) / (double)Nodes.ItemHeight); |
remainder = (ClientRectangle.Height - Columns.Options.HeaderHeight) % RowOptions.ItemHeight ; |
return (ClientRectangle.Height - Columns.Options.HeaderHeight) / RowOptions.ItemHeight ; |
} |
int MaxVisibleRows() |
{ |
int unused; |
return MaxVisibleRows( out unused); |
} |
public void BeginUpdate() |
{ |
m_nodes.BeginUpdate(); |
} |
public void EndUpdate() |
{ |
m_nodes.EndUpdate(); |
RecalcLayout(); |
} |
protected override CreateParams CreateParams |
{ |
get |
{ |
CreateParams p = base .CreateParams; |
p.Style &= ~( int )CommonTools.WinUtil.WS_BORDER; |
p.ExStyle &= ~( int )CommonTools.WinUtil.WS_EX_CLIENTEDGE; |
switch (ViewOptions.BorderStyle) |
{ |
case BorderStyle.Fixed3D: |
p.ExStyle |= ( int )CommonTools.WinUtil.WS_EX_CLIENTEDGE; |
break ; |
case BorderStyle.FixedSingle: |
p.Style |= ( int )CommonTools.WinUtil.WS_BORDER; |
break ; |
default : |
break ; |
} |
return p; |
} |
} |
|
TreeListColumn m_hotColumn = null ; |
object GetDataDesignMode(Node node, TreeListColumn column) |
{ |
string id = string .Empty; |
while (node != null ) |
{ |
id = node.Owner.GetNodeIndex(node).ToString() + ":" + id; |
node = node.Parent; |
} |
return "<temp>" + id; |
} |
protected virtual object GetData(Node node, TreeListColumn column) |
{ |
if (node[column.Index] != null ) |
return node[column.Index]; |
return null ; |
} |
public new Rectangle ClientRectangle |
{ |
get |
{ |
Rectangle r = base .ClientRectangle; |
if (m_vScroll.Visible) |
r.Width -= m_vScroll.Width+1; |
if (m_hScroll.Visible) |
r.Height -= m_hScroll.Height+1; |
return r; |
} |
} |
protected virtual CommonTools.TreeList.TextFormatting GetFormatting(CommonTools.Node node, CommonTools.TreeListColumn column) |
{ |
return column.CellFormat; |
} |
protected virtual void PaintCell(Graphics dc, Rectangle cellRect, Node node, TreeListColumn column) |
{ |
if ( this .DesignMode) |
CellPainter.PaintCell(dc, cellRect, node, column, GetFormatting(node, column), GetDataDesignMode(node, column)); |
else |
CellPainter.PaintCell(dc, cellRect, node, column, GetFormatting(node, column), GetData(node, column)); |
} |
protected virtual void PaintImage(Graphics dc, Rectangle imageRect, Node node, Image image) |
{ |
if (image != null ) |
dc.DrawImageUnscaled(image, imageRect); |
} |
protected virtual void PaintNode(Graphics dc, Rectangle rowRect, Node node, TreeListColumn[] visibleColumns, int visibleRowIndex) |
{ |
CellPainter.DrawSelectionBackground(dc, rowRect, node); |
foreach (TreeListColumn col in visibleColumns) |
{ |
if (col.CalculatedRect.Right - HScrollValue() < RowHeaderWidth()) |
continue ; |
Rectangle cellRect = rowRect; |
cellRect.X = col.CalculatedRect.X - HScrollValue(); |
cellRect.Width = col.CalculatedRect.Width; |
if (col.VisibleIndex == 0) |
{ |
int lineindet = 10; |
// add left margin |
cellRect.X += Columns.Options.LeftMargin; |
cellRect.Width -= Columns.Options.LeftMargin; |
// add indent size |
int indentSize = GetIndentSize(node) + 5; |
cellRect.X += indentSize; |
cellRect.Width -= indentSize; |
if (ViewOptions.ShowLine) |
PaintLines(dc, cellRect, node); |
cellRect.X += lineindet; |
cellRect.Width -= lineindet; |
Rectangle glyphRect = GetPlusMinusRectangle(node, col, visibleRowIndex); |
if (glyphRect != Rectangle.Empty && ViewOptions.ShowPlusMinus) |
CellPainter.PaintCellPlusMinus(dc, glyphRect, node); |
if (!ViewOptions.ShowLine && !ViewOptions.ShowPlusMinus) |
{ |
cellRect.X -= (lineindet + 5); |
cellRect.Width += (lineindet + 5); |
} |
Image icon = GetNodeBitmap(node); |
if (icon != null ) |
{ |
// center the image vertically |
glyphRect.Y = cellRect.Y + (cellRect.Height / 2) - (icon.Height / 2); |
glyphRect.X = cellRect.X; |
glyphRect.Width = icon.Width; |
glyphRect.Height = icon.Height; |
PaintImage(dc, glyphRect, node, icon); |
cellRect.X += (glyphRect.Width + 2); |
cellRect.Width -= (glyphRect.Width + 2); |
} |
PaintCell(dc, cellRect, node, col); |
} |
else |
{ |
PaintCell(dc, cellRect, node, col); |
} |
} |
} |
protected virtual void PaintLines(Graphics dc, Rectangle cellRect, Node node) |
{ |
Pen pen = new Pen(Color.Gray); |
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot; |
int halfPoint = cellRect.Top + (cellRect.Height / 2); |
// line should start from center at first root node |
if (node.Parent == null && node.PrevSibling == null ) |
{ |
cellRect.Y += (cellRect.Height / 2); |
cellRect.Height -= (cellRect.Height / 2); |
} |
if (node.NextSibling != null ) // draw full height line |
dc.DrawLine(pen, cellRect.X, cellRect.Top, cellRect.X, cellRect.Bottom); |
else |
dc.DrawLine(pen, cellRect.X, cellRect.Top, cellRect.X, halfPoint); |
dc.DrawLine(pen, cellRect.X, halfPoint, cellRect.X + 8, halfPoint); |
// now draw the lines for the parents sibling |
Node parent = node.Parent; |
while (parent != null ) |
{ |
cellRect.X -= ViewOptions.Indent; |
if (parent.NextSibling != null ) |
dc.DrawLine(pen, cellRect.X, cellRect.Top, cellRect.X, cellRect.Bottom); |
parent = parent.Parent; |
} |
pen.Dispose(); |
} |
protected virtual int GetIndentSize(Node node) |
{ |
int indent = 0; |
Node parent = node.Parent; |
while (parent != null ) |
{ |
indent += ViewOptions.Indent; |
parent = parent.Parent; |
} |
return indent; |
} |
protected virtual Rectangle GetPlusMinusRectangle(Node node, TreeListColumn firstColumn, int visibleRowIndex) |
{ |
if (node.HasChildren == false ) |
return Rectangle.Empty; |
int hScrollOffset = HScrollValue(); |
if (firstColumn.CalculatedRect.Right - hScrollOffset < RowHeaderWidth()) |
return Rectangle.Empty; |
System.Diagnostics.Debug.Assert(firstColumn.VisibleIndex == 0); |
Rectangle glyphRect = firstColumn.CalculatedRect; |
glyphRect.X -= hScrollOffset; |
glyphRect.X += GetIndentSize(node); |
glyphRect.X += Columns.Options.LeftMargin; |
glyphRect.Width = 10; |
glyphRect.Y = VisibleRowToYPoint(visibleRowIndex); |
glyphRect.Height = RowOptions.ItemHeight; |
return glyphRect; |
} |
protected virtual Image GetNodeBitmap(Node node) |
{ |
if (Images != null && node.ImageId >= 0 && node.ImageId < Images.Images.Count) |
return Images.Images[node.ImageId]; |
return null ; |
} |
protected override void OnPaint(PaintEventArgs e) |
{ |
base .OnPaint(e); |
|
int hScrollOffset = HScrollValue(); |
int remainder = 0; |
int visiblerows = MaxVisibleRows( out remainder); |
if (remainder > 0) |
visiblerows++; |
|
bool drawColumnHeaders = true ; |
// draw columns |
if (drawColumnHeaders) |
{ |
Rectangle headerRect = e.ClipRectangle; |
Columns.Draw(e.Graphics, headerRect, hScrollOffset); |
} |
// draw vertical grid lines |
if (ViewOptions.ShowGridLines) |
{ |
// visible row count |
int remainRows = Nodes.VisibleNodeCount - m_vScroll.Value; |
if (visiblerows > remainRows) |
visiblerows = remainRows; |
|
Rectangle fullRect = ClientRectangle; |
if (drawColumnHeaders) |
fullRect.Y += Columns.Options.HeaderHeight; |
fullRect.Height = visiblerows * RowOptions.ItemHeight; |
Columns.Painter.DrawVerticalGridLines(Columns, e.Graphics, fullRect, hScrollOffset); |
} |
int visibleRowIndex = 0; |
TreeListColumn[] visibleColumns = this .Columns.VisibleColumns; |
int columnsWidth = Columns.ColumnsWidth; |
foreach (Node node in NodeCollection.ForwardNodeIterator(m_firstVisibleNode, true )) |
{ |
Rectangle rowRect = CalcRowRecangle(visibleRowIndex); |
if (rowRect == Rectangle.Empty || rowRect.Bottom <= e.ClipRectangle.Top || rowRect.Top >= e.ClipRectangle.Bottom) |
{ |
if (visibleRowIndex > visiblerows) |
break ; |
visibleRowIndex++; |
continue ; |
} |
rowRect.X = RowHeaderWidth() - hScrollOffset; |
rowRect.Width = columnsWidth; |
// draw horizontal grid line for current node |
if (ViewOptions.ShowGridLines) |
{ |
Rectangle r = rowRect; |
r.X = RowHeaderWidth(); |
r.Width = columnsWidth - hScrollOffset; |
m_rowPainter.DrawHorizontalGridLine(e.Graphics, r); |
} |
// draw the current node |
PaintNode(e.Graphics, rowRect, node, visibleColumns, visibleRowIndex); |
|
// drow row header for current node |
Rectangle headerRect = rowRect; |
headerRect.X = 0; |
headerRect.Width = RowHeaderWidth(); |
int absoluteRowIndex = visibleRowIndex + VScrollValue(); |
headerRect.Width = RowHeaderWidth(); |
m_rowPainter.DrawHeader(e.Graphics, headerRect, absoluteRowIndex == m_hotrow); |
|
visibleRowIndex++; |
} |
} |
protected override bool IsInputKey(Keys keyData) |
{ |
if (( int )(keyData & Keys.Shift) > 0) |
return true ; |
switch (keyData) |
{ |
case Keys.Left: |
case Keys.Right: |
case Keys.Down: |
case Keys.Up: |
case Keys.PageUp: |
case Keys.PageDown: |
case Keys.Home: |
case Keys.End: |
return true ; |
} |
return false ; |
} |
protected override void OnKeyDown(KeyEventArgs e) |
{ |
Node newnode = null ; |
if (e.KeyCode == Keys.PageUp) |
{ |
int remainder = 0; |
int diff = MaxVisibleRows( out remainder)-1; |
newnode = NodeCollection.GetNextNode(FocusedNode, -diff); |
if (newnode == null ) |
newnode = Nodes.FirstVisibleNode(); |
} |
if (e.KeyCode == Keys.PageDown) |
{ |
int remainder = 0; |
int diff = MaxVisibleRows( out remainder)-1; |
newnode = NodeCollection.GetNextNode(FocusedNode, diff); |
if (newnode == null ) |
newnode = Nodes.LastVisibleNode( true ); |
} |
if (e.KeyCode == Keys.Down) |
{ |
newnode = NodeCollection.GetNextNode(FocusedNode, 1); |
} |
if (e.KeyCode == Keys.Up) |
{ |
newnode = NodeCollection.GetNextNode(FocusedNode, -1); |
} |
if (e.KeyCode == Keys.Home) |
{ |
newnode = Nodes.FirstNode; |
} |
if (e.KeyCode == Keys.End) |
{ |
newnode = Nodes.LastVisibleNode( true ); |
} |
if (e.KeyCode == Keys.Left) |
{ |
if (FocusedNode != null ) |
{ |
if (FocusedNode.Expanded) |
{ |
FocusedNode.Collapse(); |
EnsureVisible(FocusedNode); |
return ; |
} |
if (FocusedNode.Parent != null ) |
{ |
FocusedNode = FocusedNode.Parent; |
EnsureVisible(FocusedNode); |
} |
} |
} |
if (e.KeyCode == Keys.Right) |
{ |
if (FocusedNode != null ) |
{ |
if (FocusedNode.Expanded == false && FocusedNode.HasChildren) |
{ |
FocusedNode.Expand(); |
EnsureVisible(FocusedNode); |
return ; |
} |
if (FocusedNode.Expanded == true && FocusedNode.HasChildren) |
{ |
FocusedNode = FocusedNode.Nodes.FirstNode; |
EnsureVisible(FocusedNode); |
} |
} |
} |
if (newnode != null ) |
{ |
if (MultiSelect) |
{ |
// tree behavior is |
// keys none, the selected node is added as the focused and selected node |
// keys control, only focused node is moved, the selected nodes collection is not modified |
// keys shift, selection from first selected node to current node is done |
if (Control.ModifierKeys == Keys.Control) |
FocusedNode = newnode; |
else |
MultiSelectAdd(newnode, Control.ModifierKeys); |
} |
else |
FocusedNode = newnode; |
EnsureVisible(FocusedNode); |
} |
base .OnKeyDown(e); |
} |
internal void internalUpdateStyles() |
{ |
base .UpdateStyles(); |
} |
#region ISupportInitialize Members |
public void BeginInit() |
{ |
Columns.BeginInit(); |
} |
public void EndInit() |
{ |
Columns.EndInit(); |
} |
#endregion |
internal new bool DesignMode |
{ |
get { return base .DesignMode; } |
} |
} |
public class TreeListViewNodes : NodeCollection |
{ |
TreeListView m_tree; |
bool m_isUpdating = false ; |
public void BeginUpdate() |
{ |
m_isUpdating = true ; |
} |
public void EndUpdate() |
{ |
m_isUpdating = false ; |
} |
public TreeListViewNodes(TreeListView owner) : base ( null ) |
{ |
m_tree = owner; |
} |
protected override void UpdateNodeCount( int oldvalue, int newvalue) |
{ |
base .UpdateNodeCount(oldvalue, newvalue); |
if (!m_isUpdating) |
m_tree.RecalcLayout(); |
} |
public override void Clear() |
{ |
base .Clear(); |
m_tree.RecalcLayout(); |
} |
public override void NodetifyBeforeExpand(Node nodeToExpand, bool expanding) |
{ |
if (!m_tree.DesignMode) |
m_tree.OnNotifyBeforeExpand(nodeToExpand, expanding); |
} |
public override void NodetifyAfterExpand(Node nodeToExpand, bool expanded) |
{ |
m_tree.OnNotifyAfterExpand(nodeToExpand, expanded); |
} |
protected override int GetFieldIndex( string fieldname) |
{ |
TreeListColumn col = m_tree.Columns[fieldname]; |
if (col != null ) |
return col.Index; |
return -1; |
} |
} |
} |
by: 发表于:2017-12-08 09:42:48 顶(0) | 踩(0) 回复
??
回复评论