394 lines
16 KiB
C#
394 lines
16 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Drawing.Text;
|
|
|
|
namespace DevComponents.AdvTree.Layout
|
|
{
|
|
/// <summary>
|
|
/// Performs ListView Tile style layout.
|
|
/// </summary>
|
|
internal class NodeTileLayout : NodeLayout
|
|
{
|
|
public NodeTileLayout(AdvTree treeControl, Rectangle clientArea, LayoutSettings layoutSettings)
|
|
: base(treeControl, clientArea, layoutSettings)
|
|
{
|
|
}
|
|
|
|
public override void UpdateTopLevelColumnsWidth()
|
|
{
|
|
// Columns are not visible in tile layout
|
|
this.Tree.Columns.SetBounds(Rectangle.Empty);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns default top-level columns for tree control.
|
|
/// </summary>
|
|
/// <returns>Returns array list of ColumnInfo objects.</returns>
|
|
protected override NodeColumnInfo GetDefaultColumnInfo()
|
|
{
|
|
// There are no columns in Tile view
|
|
List<ColumnInfo> ci = new List<ColumnInfo>();
|
|
NodeColumnInfo info = new NodeColumnInfo(ci, false);
|
|
return info;
|
|
}
|
|
|
|
protected override NodeLayoutContextInfo GetDefaultNodeLayoutContextInfo(System.Drawing.Graphics graphics)
|
|
{
|
|
NodeLayoutContextInfo context = base.GetDefaultNodeLayoutContextInfo(graphics);
|
|
context.IsViewGroupping = this.Groupping;
|
|
return context;
|
|
}
|
|
|
|
public override void PerformLayout()
|
|
{
|
|
this.PrepareStyles();
|
|
Rectangle area = Rectangle.Empty;
|
|
Graphics g=this.GetGraphics();
|
|
SmoothingMode sm = g.SmoothingMode;
|
|
TextRenderingHint th = g.TextRenderingHint;
|
|
if (m_Tree.AntiAlias)
|
|
{
|
|
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
|
|
}
|
|
|
|
NodeLayoutContextInfo layoutInfo = GetDefaultNodeLayoutContextInfo(g);
|
|
layoutInfo.ExpandPartAlignedLeft = true;
|
|
layoutInfo.Left = ClientArea.X;
|
|
layoutInfo.Top = ClientArea.Y;
|
|
|
|
CellLayout cellLayout = this.GetCellLayout();
|
|
cellLayout.ResetCheckBoxSize();
|
|
if (this.Tree.CheckBoxImageChecked != null)
|
|
cellLayout.CheckBoxSize = this.Tree.CheckBoxImageChecked.Size;
|
|
|
|
LayoutTopLevelColumns(layoutInfo);
|
|
|
|
// Get default Columns
|
|
NodeColumnInfo defaultColInfoList = this.GetDefaultColumnInfo();
|
|
layoutInfo.DefaultColumns = defaultColInfoList;
|
|
try
|
|
{
|
|
// Loop through each top-level node
|
|
Node[] topLevelNodes=this.GetTopLevelNodes();
|
|
int defaultTop = layoutInfo.Top;
|
|
area = ProcessTopLevelNodes(area, layoutInfo, topLevelNodes);
|
|
|
|
//if (defaultColInfoList.HasAutoSizeColumn)
|
|
//{
|
|
// foreach (ColumnInfo columnInfo in defaultColInfoList.ColumnInfo)
|
|
// {
|
|
// if (columnInfo.AutoSize)
|
|
// {
|
|
// columnInfo.AutoSize = false;
|
|
// columnInfo.Width = columnInfo.MaxWidth;
|
|
// columnInfo.ColumnHeader.Width.SetAutoSizeWidth(columnInfo.MaxWidth);
|
|
// columnInfo.MaxWidth = 0;
|
|
// }
|
|
// }
|
|
// layoutInfo.ContextNode = null;
|
|
// LayoutTopLevelColumns(layoutInfo);
|
|
// layoutInfo.Top = defaultTop;
|
|
// area = ProcessTopLevelNodes(Rectangle.Empty, layoutInfo, topLevelNodes);
|
|
//}
|
|
}
|
|
finally
|
|
{
|
|
if (m_Tree.AntiAlias)
|
|
{
|
|
g.SmoothingMode = sm;
|
|
//g.TextRenderingHint = th;
|
|
}
|
|
|
|
if(this.DisposeGraphics)
|
|
g.Dispose();
|
|
}
|
|
if (layoutInfo.FullRowBackgroundNodes.Count > 0)
|
|
Tree.FullRowBackgroundNodes = layoutInfo.FullRowBackgroundNodes;
|
|
else
|
|
Tree.FullRowBackgroundNodes = null;
|
|
|
|
m_Width = area.Width;
|
|
m_Height = area.Height;
|
|
}
|
|
|
|
private void LayoutTopLevelColumns(NodeLayoutContextInfo layoutInfo)
|
|
{
|
|
// There are no columns in Tile view
|
|
this.Tree.SetColumnHeaderControlVisibility(false);
|
|
}
|
|
|
|
private Rectangle ProcessTopLevelNodes(Rectangle area, NodeLayoutContextInfo layoutInfo, Node[] topLevelNodes)
|
|
{
|
|
layoutInfo.CurrentLevelLeft = layoutInfo.Left;
|
|
bool isPreviousGroupNode = false;
|
|
foreach (Node childNode in topLevelNodes)
|
|
{
|
|
layoutInfo.ContextNode = childNode;
|
|
if (childNode.Visible)
|
|
{
|
|
if (_Groupping && childNode.HasChildNodes)
|
|
{
|
|
if (layoutInfo.CurrentLineHeight > 0)
|
|
layoutInfo.Top += layoutInfo.CurrentLineHeight + this.LayoutSettings.NodeVerticalSpacing;
|
|
layoutInfo.CurrentLineHeight = 0;
|
|
layoutInfo.Left = layoutInfo.CurrentLevelLeft;
|
|
isPreviousGroupNode = true;
|
|
}
|
|
else
|
|
{
|
|
if (isPreviousGroupNode)
|
|
{
|
|
if (layoutInfo.CurrentLineHeight > 0)
|
|
layoutInfo.Top += layoutInfo.CurrentLineHeight + this.LayoutSettings.NodeVerticalSpacing;
|
|
layoutInfo.CurrentLineHeight = 0;
|
|
layoutInfo.Left = layoutInfo.CurrentLevelLeft;
|
|
}
|
|
isPreviousGroupNode = false;
|
|
}
|
|
}
|
|
|
|
ProcessNode(layoutInfo);
|
|
|
|
if (childNode.Visible)
|
|
{
|
|
area = Rectangle.Union(area, childNode.BoundsRelative);
|
|
if (childNode.Expanded && childNode.HasChildNodes)
|
|
area = Rectangle.Union(area, childNode.ChildNodesBounds);
|
|
if (!(_Groupping && childNode.HasChildNodes))
|
|
layoutInfo.Left += childNode.BoundsRelative.Width + this.LayoutSettings.NodeHorizontalSpacing;
|
|
}
|
|
}
|
|
return area;
|
|
}
|
|
|
|
private Rectangle ProcessChildNodes(NodeLayoutContextInfo layoutInfo, Node node, int nodeVerticalSpacing, NodeColumnInfo childColumns)
|
|
{
|
|
Rectangle childNodesBounds = new Rectangle(layoutInfo.Left, layoutInfo.Top, 0, 0);
|
|
bool isPreviousGroupNode = false;
|
|
|
|
foreach (Node childNode in node.Nodes)
|
|
{
|
|
if (!childNode.Visible) continue;
|
|
|
|
if (_Groupping && childNode.HasChildNodes)
|
|
{
|
|
if (layoutInfo.CurrentLineHeight > 0)
|
|
layoutInfo.Top += layoutInfo.CurrentLineHeight + this.LayoutSettings.NodeVerticalSpacing;
|
|
layoutInfo.CurrentLineHeight = 0;
|
|
layoutInfo.Left = layoutInfo.CurrentLevelLeft;
|
|
isPreviousGroupNode = true;
|
|
}
|
|
else
|
|
{
|
|
if (isPreviousGroupNode)
|
|
{
|
|
if (layoutInfo.CurrentLineHeight > 0)
|
|
layoutInfo.Top += layoutInfo.CurrentLineHeight + this.LayoutSettings.NodeVerticalSpacing;
|
|
layoutInfo.CurrentLineHeight = 0;
|
|
layoutInfo.Left = layoutInfo.CurrentLevelLeft;
|
|
isPreviousGroupNode = false;
|
|
}
|
|
}
|
|
|
|
layoutInfo.ContextNode = childNode;
|
|
layoutInfo.ChildColumns = childColumns;
|
|
ProcessNode(layoutInfo);
|
|
|
|
if (!(_Groupping && childNode.HasChildNodes))
|
|
layoutInfo.Left += childNode.BoundsRelative.Width + this.LayoutSettings.NodeHorizontalSpacing;
|
|
if (isPreviousGroupNode)
|
|
{
|
|
childNodesBounds.Width = Math.Max(childNodesBounds.Width,
|
|
Math.Max(childNode.BoundsRelative.Width, (childNode.Expanded && childNode.ChildNodesBounds.Width > 0 ? childNode.ChildNodesBounds.Right - childNodesBounds.X : 0)));
|
|
childNodesBounds.Height += childNode.BoundsRelative.Height + (childNode.Expanded ? childNode.ChildNodesBounds.Height + childNode.ColumnHeaderHeight : 0) + nodeVerticalSpacing;
|
|
}
|
|
else
|
|
{
|
|
childNodesBounds = Rectangle.Union(childNodesBounds, childNode.BoundsRelative);
|
|
childNodesBounds.Height += nodeVerticalSpacing;
|
|
}
|
|
}
|
|
return childNodesBounds;
|
|
}
|
|
|
|
|
|
#region Node routines
|
|
private void ProcessNode(NodeLayoutContextInfo layoutInfo)
|
|
{
|
|
Node node=layoutInfo.ContextNode;
|
|
if (!node.Visible || node.Cells.Count == 0) return;
|
|
|
|
if (node.SizeChanged || _Groupping && node.HasChildNodes)
|
|
{
|
|
// Calculate size of the node itself...
|
|
LayoutNode(layoutInfo);
|
|
}
|
|
if (node.FullRowBackground)
|
|
layoutInfo.FullRowBackgroundNodes.Add(node);
|
|
|
|
// Position the node
|
|
if (_Groupping && node.HasChildNodes)
|
|
{
|
|
if (layoutInfo.CurrentLineHeight > 0)
|
|
layoutInfo.Top += layoutInfo.CurrentLineHeight + this.LayoutSettings.NodeVerticalSpacing;
|
|
layoutInfo.CurrentLineHeight = 0;
|
|
}
|
|
else
|
|
{
|
|
if (layoutInfo.Left + node.BoundsRelative.Width > this.ClientArea.Right)
|
|
{
|
|
layoutInfo.Left = layoutInfo.CurrentLevelLeft;
|
|
layoutInfo.Top += layoutInfo.CurrentLineHeight + this.LayoutSettings.NodeVerticalSpacing;
|
|
layoutInfo.CurrentLineHeight = 0;
|
|
}
|
|
}
|
|
layoutInfo.CurrentLineHeight = Math.Max(layoutInfo.CurrentLineHeight, node.BoundsRelative.Height);
|
|
|
|
if (node.BoundsRelative.X != layoutInfo.Left || node.BoundsRelative.Y != layoutInfo.Top)
|
|
{
|
|
// Adjust top position
|
|
node.SetBounds(new Rectangle(layoutInfo.Left,layoutInfo.Top,node.BoundsRelative.Width,node.BoundsRelative.Height));
|
|
}
|
|
|
|
// Position the node
|
|
if (_Groupping && node.HasChildNodes)
|
|
{
|
|
if (layoutInfo.CurrentLineHeight > 0)
|
|
layoutInfo.Top += layoutInfo.CurrentLineHeight + this.LayoutSettings.NodeVerticalSpacing;
|
|
layoutInfo.CurrentLineHeight = 0;
|
|
}
|
|
|
|
int nodeVerticalSpacing = this.LayoutSettings.NodeVerticalSpacing;
|
|
// Need to set the Top position properly
|
|
//layoutInfo.Top += (node.BoundsRelative.Height + nodeVerticalSpacing);
|
|
// No columns in tile view
|
|
//if (DevComponents.AdvTree.Display.NodeDisplay.HasColumnsVisible(node))
|
|
// layoutInfo.Top += node.ColumnHeaderHeight;
|
|
|
|
if(_Groupping && node.HasChildNodes && node.Expanded)
|
|
{
|
|
int originalLevelOffset=layoutInfo.Left;
|
|
int originalLevelLeft = layoutInfo.CurrentLevelLeft;
|
|
int childNodesTop = layoutInfo.Top;
|
|
layoutInfo.Left += this.NodeLevelOffset + node.NodesIndent;
|
|
layoutInfo.CurrentLevelLeft = layoutInfo.Left;
|
|
NodeColumnInfo parentColumns = layoutInfo.ChildColumns;
|
|
NodeColumnInfo childColumns = GetNodeColumnInfo(node);
|
|
Rectangle childNodesBounds = ProcessChildNodes(layoutInfo, node, nodeVerticalSpacing, childColumns);
|
|
|
|
//if (childColumns != null && childColumns.HasAutoSizeColumn)
|
|
//{
|
|
// foreach (ColumnInfo columnInfo in childColumns.ColumnInfo)
|
|
// {
|
|
// if (columnInfo.AutoSize)
|
|
// {
|
|
// columnInfo.Width = columnInfo.MaxWidth;
|
|
// columnInfo.ColumnHeader.Width.SetAutoSizeWidth(columnInfo.MaxWidth);
|
|
// columnInfo.AutoSize = false;
|
|
// columnInfo.MaxWidth = 0;
|
|
// }
|
|
// }
|
|
// layoutInfo.Top = originalTop;
|
|
// layoutInfo.Left = originalLevelOffset;
|
|
// layoutInfo.ContextNode = node;
|
|
// layoutInfo.ChildColumns = null;
|
|
// LayoutNode(layoutInfo);
|
|
// layoutInfo.Top = childNodesTop;
|
|
// layoutInfo.Left += this.NodeLevelOffset + node.NodesIndent;
|
|
// childNodesBounds = ProcessChildNodes(layoutInfo, node, nodeVerticalSpacing, childColumns);
|
|
//}
|
|
|
|
node.ChildNodesBounds = childNodesBounds;
|
|
|
|
layoutInfo.ChildColumns=parentColumns;
|
|
|
|
layoutInfo.ContextNode=node;
|
|
layoutInfo.Left=originalLevelOffset;
|
|
layoutInfo.CurrentLevelLeft = originalLevelLeft;
|
|
}
|
|
else
|
|
node.ChildNodesBounds = Rectangle.Empty;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets whether the expand part of the node +/- is aligned to the left of the node in left-to-right layout.
|
|
/// </summary>
|
|
/// <param name="node">Node to get expand part alignment for</param>
|
|
/// <returns>true if node expand part is aligned to the left in left-to-right layout.</returns>
|
|
private bool ExpandPartAlignedNear(Node node)
|
|
{
|
|
return true; // If changed LayoutExpandPart needs to be updated as well
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns column information for a given node.
|
|
/// </summary>
|
|
/// <param name="node">Node to return column information for</param>
|
|
/// <returns>Returns array list of ColumnInfo objects or null if there are no columns defined.</returns>
|
|
protected override NodeColumnInfo GetNodeColumnInfo(Node node)
|
|
{
|
|
// No columns in tile-view
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if expand part space should be accounted for even if they expand part is not visible or need to be displayed. Default value is false.
|
|
/// </summary>
|
|
protected override bool ReserveExpandPartSpace
|
|
{
|
|
get
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if given node has expand part.
|
|
/// </summary>
|
|
/// <param name="layoutInfo">Layout context information.</param>
|
|
/// <returns></returns>
|
|
protected override bool HasExpandPart(NodeLayoutContextInfo layoutInfo)
|
|
{
|
|
Node node = layoutInfo.ContextNode;
|
|
if (node.ExpandVisibility == eNodeExpandVisibility.Auto)
|
|
{
|
|
if (!_Groupping || !NodeOperations.GetAnyVisibleNodes(node))
|
|
return false;
|
|
return true;
|
|
}
|
|
else
|
|
return (node.ExpandVisibility == eNodeExpandVisibility.Visible);
|
|
}
|
|
|
|
|
|
private bool _Groupping = true;
|
|
/// <summary>
|
|
/// Gets or sets whether parent/child node relationship is displayed as groups.
|
|
/// </summary>
|
|
public bool Groupping
|
|
{
|
|
get { return _Groupping; }
|
|
set
|
|
{
|
|
_Groupping = value;
|
|
}
|
|
}
|
|
|
|
private CellTileLayout _CellLayout = null;
|
|
/// <summary>
|
|
/// Returns class responsible for cell layout.
|
|
/// </summary>
|
|
/// <returns>Cell layout class.</returns>
|
|
protected internal override CellLayout GetCellLayout()
|
|
{
|
|
if (_CellLayout == null)
|
|
_CellLayout = new CellTileLayout(this.LayoutSettings);
|
|
return _CellLayout;
|
|
}
|
|
#endregion
|
|
}
|
|
}
|