📄 treelistview.cs
字号:
this.flags |= Branch.BranchFlags.LastChild;
else
this.flags &= ~Branch.BranchFlags.LastChild;
}
}
/// <summary>
/// Return true if this branch is the only top level branch
/// </summary>
public virtual bool IsOnlyBranch
{
get
{
return ((this.flags & Branch.BranchFlags.OnlyBranch) != 0);
}
set
{
if (value)
this.flags |= Branch.BranchFlags.OnlyBranch;
else
this.flags &= ~Branch.BranchFlags.OnlyBranch;
}
}
//------------------------------------------------------------------------------------------
// Commands
/// <summary>
/// Clear any cached information that this branch is holding
/// </summary>
public virtual void ClearCachedInfo()
{
this.Children = new ArrayList();
this.alreadyHasChildren = false;
}
/// <summary>
/// Collapse this branch
/// </summary>
public virtual void Collapse()
{
this.IsExpanded = false;
}
/// <summary>
/// Expand this branch
/// </summary>
public virtual void Expand()
{
if (!this.CanExpand)
return;
// THINK: Should we cache the children or fetch them each time? If we cache, we need a "DiscardCache" ability
this.IsExpanded = true;
if (this.alreadyHasChildren)
return;
if (this.Tree.ChildrenGetter != null) {
Cursor previous = Cursor.Current;
try {
if (this.Tree.TreeView.UseWaitCursorWhenExpanding)
Cursor.Current = Cursors.WaitCursor;
this.Children = this.Tree.ChildrenGetter(this.Model);
}
finally {
if (this.Tree.TreeView.UseWaitCursorWhenExpanding)
Cursor.Current = previous;
}
}
this.alreadyHasChildren = true;
}
/// <summary>
/// Expand this branch recursively
/// </summary>
public virtual void ExpandAll()
{
this.Expand();
foreach (Branch br in this.ChildBranches)
br.ExpandAll();
}
/// <summary>
/// Collapse the visible descendents of this branch into list of model objects
/// </summary>
/// <returns></returns>
public virtual IList Flatten()
{
ArrayList flatList = new ArrayList();
if (this.IsExpanded)
this.FlattenOnto(flatList);
return flatList;
}
/// <summary>
/// Flatten this branch's visible descendents onto the given list.
/// </summary>
/// <param name="flatList"></param>
/// <remarks>The branch itself is <b>not</b> included in the list.</remarks>
public virtual void FlattenOnto(IList flatList)
{
foreach (Branch br in this.ChildBranches) {
flatList.Add(br.Model);
if (br.IsExpanded)
br.FlattenOnto(flatList);
}
}
/// <summary>
/// Sort the sub-branches and their descendents so they are ordered according
/// to the given comparer.
/// </summary>
/// <param name="comparer">The comparer that orders the branches</param>
public virtual void Sort(BranchComparer comparer)
{
if (comparer == null || this.ChildBranches.Count == 0)
return;
// We're about to sort the children, so clear the last child flag
this.ChildBranches[this.ChildBranches.Count - 1].IsLastChild = false;
this.ChildBranches.Sort(comparer);
this.ChildBranches[this.ChildBranches.Count - 1].IsLastChild = true;
foreach (Branch br in this.ChildBranches)
br.Sort(comparer);
}
//------------------------------------------------------------------------------------------
// Public instance variables
public Object Model;
public Tree Tree;
public Branch ParentBranch;
public List<Branch> ChildBranches = new List<Branch>();
//public bool CanExpand = false;
public bool IsExpanded = false;
public int Level = 0;
//------------------------------------------------------------------------------------------
// Private instance variables
private bool alreadyHasChildren = false;
private BranchFlags flags;
}
/// <summary>
/// This class sorts branches according to how their respective model objects are sorted
/// </summary>
protected class BranchComparer : IComparer<Branch>
{
public BranchComparer(IComparer actualComparer)
{
this.actualComparer = actualComparer;
}
public int Compare(Branch x, Branch y)
{
return this.actualComparer.Compare(x.Model, y.Model);
}
private IComparer actualComparer;
}
/// <summary>
/// This class handles drawing the tree structure of the primary column.
/// </summary>
public class TreeRenderer : BaseRenderer
{
public TreeRenderer()
{
this.LinePen = new Pen(Color.Blue, 1.0f);
this.LinePen.DashStyle = DashStyle.Dot;
}
/// <summary>
/// Return the branch that the renderer is currently drawing.
/// </summary>
private Branch Branch
{
get {
return this.TreeListView.TreeModel.GetBranch(this.RowObject);
}
}
/// <summary>
/// Return the pen that will be used to draw the lines between branches
/// </summary>
public Pen LinePen
{
get { return linePen; }
set { linePen = value; }
}
private Pen linePen;
/// <summary>
/// Return the TreeListView for which the renderer is being used.
/// </summary>
public TreeListView TreeListView
{
get {
return (TreeListView)this.ListView;
}
}
/// <summary>
/// Should the renderer draw lines connecting siblings?
/// </summary>
public bool IsShowLines = true;
/// <summary>
/// How many pixels will be reserved for each level of indentation?
/// </summary>
public static int PIXELS_PER_LEVEL = 16 + 1;
/// <summary>
/// The real work of drawing the tree is done in this method
/// </summary>
/// <param name="g"></param>
/// <param name="r"></param>
public override void Render(System.Drawing.Graphics g, System.Drawing.Rectangle r)
{
this.DrawBackground(g, r);
Branch br = this.Branch;
if (this.IsShowLines)
this.DrawLines(g, r, this.LinePen, br);
if (br.CanExpand) {
Rectangle r2 = r;
r2.Offset((br.Level - 1) * PIXELS_PER_LEVEL, 0);
r2.Width = PIXELS_PER_LEVEL;
if (!this.IsPrinting && Application.RenderWithVisualStyles) {
VisualStyleElement element = VisualStyleElement.TreeView.Glyph.Closed;
if (br.IsExpanded)
element = VisualStyleElement.TreeView.Glyph.Opened;
VisualStyleRenderer renderer = new VisualStyleRenderer(element);
renderer.DrawBackground(g, r2);
} else {
int h = 8;
int w = 8;
int x = r2.X + 4;
int y = r2.Y + (r2.Height / 2) - 4;
g.DrawRectangle(new Pen(SystemBrushes.ControlDark), x, y, w, h);
g.FillRectangle(Brushes.White, x + 1, y + 1, w - 1, h - 1);
g.DrawLine(Pens.Black, x + 2, y + 4, x + w - 2, y + 4);
if (!br.IsExpanded)
g.DrawLine(Pens.Black, x + 4, y + 2, x + 4, y + h - 2);
}
}
int indent = br.Level * PIXELS_PER_LEVEL;
r.Offset(indent, 0);
r.Width -= indent;
this.DrawImageAndText(g, r);
}
private void DrawLines(Graphics g, Rectangle r, Pen p, Branch br)
{
Rectangle r2 = r;
r2.Width = PIXELS_PER_LEVEL;
// Vertical lines have to start on even points, otherwise the dotted line looks wrong.
// This isn't need if pen isn't dotted.
int top = r2.Top;
if (p.DashStyle == DashStyle.Dot && (top & 1) == 1)
top += 1;
// Draw lines for ancestors
int midX;
IList<Branch> ancestors = br.Ancestors;
foreach (Branch ancestor in ancestors) {
if (!ancestor.IsLastChild) {
midX = r2.Left + r2.Width / 2;
g.DrawLine(p, midX, top, midX, r2.Bottom);
}
r2.Offset(PIXELS_PER_LEVEL, 0);
}
// Draw lines for this branch
midX = r2.Left + r2.Width / 2;
int midY = r2.Top + r2.Height / 2;
// Horizontal line first
g.DrawLine(p, midX, midY, r2.Right, midY);
// Vertical line second
if (br.IsFirstBranch) {
if (!br.IsOnlyBranch)
g.DrawLine(p, midX, midY, midX, r2.Bottom);
} else {
if (br.IsLastChild)
g.DrawLine(p, midX, top, midX, midY);
else
g.DrawLine(p, midX, top, midX, r2.Bottom);
}
}
protected override void HandleHitTest(Graphics g, OlvListViewHitTestInfo hti, int x, int y)
{
Branch br = this.Branch;
Rectangle r = this.Bounds;
if (br.CanExpand) {
r.Offset((br.Level - 1) * PIXELS_PER_LEVEL, 0);
r.Width = PIXELS_PER_LEVEL;
if (r.Contains(x, y)) {
hti.HitTestLocation = HitTestLocation.ExpandButton;
return;
}
}
r = this.Bounds;
int indent = br.Level * PIXELS_PER_LEVEL;
r.X += indent;
r.Width -= indent;
this.StandardHitTest(g, hti, r, x, y);
}
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -