?? treelistview.cs
字號:
/*
* TreeListView - A listview that can show a tree of objects in a column
*
* Author: Phillip Piper
* Date: 23/09/2008 11:15 AM
*
* Change log:
* 2009-02-24 JPP - All commands now work when the list is empty (SF #2631054)
* - TreeListViews can now be printed with ListViewPrinter
* 2009-01-27 JPP - Changed to use new Renderer and HitTest scheme
* 2009-01-22 JPP - Added RevealAfterExpand property. If this is true (the default),
* after expanding a branch, the control scrolls to reveal as much of the
* expanded branch as possible.
* 2009-01-13 JPP - Changed TreeRenderer to work with visual styles are disabled
* v2.0.1
* 2009-01-07 JPP - Made all public and protected methods virtual
* - Changed some classes from 'internal' to 'protected' so that they
* can be accessed by subclasses of TreeListView.
* 2008-12-22 JPP - Added UseWaitCursorWhenExpanding property
* - Made TreeRenderer public so that it can be subclassed
* - Added LinePen property to TreeRenderer to allow the connection drawing
* pen to be changed
* - Fixed some rendering issues where the text highlight rect was miscalculated
* - Fixed connection line problem when there is only a single root
* v2.0
* 2008-12-10 JPP - Expand/collapse with mouse now works when there is no SmallImageList.
* 2008-12-01 JPP - Search-by-typing now works.
* 2008-11-26 JPP - Corrected calculation of expand/collapse icon (SF#2338819)
* - Fixed ugliness with dotted lines in renderer (SF#2332889)
* - Fixed problem with custom selection colors (SF#2338805)
* 2008-11-19 JPP - Expand/collapse now preserve the selection -- more or less :)
* - Overrode RefreshObjects() to rebuild the given objects and their children
* 2008-11-05 JPP - Added ExpandAll() and CollapseAll() commands
* - CanExpand is no longer cached
* - Renamed InitialBranches to RootModels since it deals with model objects
* 2008-09-23 JPP Initial version
*
* TO DO:
* 2008-12-10 If the TreeListView doesn't have a small image list, checkboxes do not work.
* [Is this still the case? 2009/01/27]
* 2008-10-19 Can we remove the need to ownerdraw the tree view?
* If tree does not have checkboxes, we could use the state image
* to show the expand/collapse icon. If the tree has check boxes,
* it has to be owner drawn.
*
* Copyright (C) 2006-2008 Phillip Piper
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If you wish to use this code in a closed source application, please contact phillip_piper@bigfoot.com.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
namespace BrightIdeasSoftware
{
/// <summary>
/// A TreeListView combines an expandable tree structure with list view columns.
/// </summary>
/// <remarks>
/// <para>To support tree operations, two delegates must be provided:</para>
/// <list>
/// <item>CanExpandGetter. This delegate must accept a model object and return a boolean indicating
/// if that model should be expandable. </item>
/// <item>ChildrenGetter. This delegate must accept a model object and return an IEnumerable of model
/// objects that will be displayed as children of the parent model. This delegate will only be called
/// for a model object if the CanExpandGetter has already returned true for that model.</item>
/// </list>
/// <para>
/// The top level branches of the tree are set via the Roots property. SetObjects(), AddObjects()
/// and RemoveObjects() are interpreted as operations on this collection of roots.
/// </para>
/// <para>
/// To add new children to an existing branch, make changes to your model objects and then
/// call RefreshObject() on the parent.
/// </para>
/// <para>The tree must be a directed acyclic graph -- no cycles are allowed.</para>
/// <para>More generally, each model object must appear only once in the tree. If the same model object appears in two
/// places in the tree, the control will become confused.</para>
/// </remarks>
public class TreeListView : VirtualObjectListView
{
/// <summary>
/// Make a default TreeListView
/// </summary>
public TreeListView()
{
this.TreeModel = new Tree(this);
this.OwnerDraw = true;
this.View = View.Details;
this.DataSource = this.TreeModel;
this.TreeColumnRenderer = new TreeRenderer();
}
//------------------------------------------------------------------------------------------
// Properties
/// <summary>
/// This is the delegate that will be used to decide if a model object can be expanded.
/// </summary>
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public virtual CanExpandGetterDelegate CanExpandGetter
{
get { return this.TreeModel.CanExpandGetter; }
set { this.TreeModel.CanExpandGetter = value; }
}
/// <summary>
/// This is the delegate that will be used to fetch the children of a model object
/// </summary>
/// <remarks>This delegate will only be called if the CanExpand delegate has
/// returned true for the model object.</remarks>
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public virtual ChildrenGetterDelegate ChildrenGetter
{
get { return this.TreeModel.ChildrenGetter; }
set { this.TreeModel.ChildrenGetter = value; }
}
/// <summary>
/// After expanding a branch, should the TreeListView attempts to show as much of the
/// revealed descendents as possible.
/// </summary>
[Category("Behavior - ObjectListView"),
Description("Should a wait cursor be shown when a branch is being expaned?"),
DefaultValue(true)]
public bool RevealAfterExpand
{
get { return revealAfterExpand; }
set { revealAfterExpand = value; }
}
private bool revealAfterExpand = true;
/// <summary>
/// The model objects that form the top level branches of the tree.
/// </summary>
/// <remarks>Setting this does <b>NOT</b> reset the state of the control.
/// In particular, it does not collapse branches.</remarks>
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public virtual IEnumerable Roots
{
get { return this.TreeModel.RootObjects; }
set {
// Make sure that column 0 is showing a tree
if (this.GetColumn(0).Renderer == null)
this.GetColumn(0).Renderer = this.TreeColumnRenderer;
if (value == null)
this.TreeModel.RootObjects = new ArrayList();
else
this.TreeModel.RootObjects = value;
this.UpdateVirtualListSize();
}
}
/// <summary>
/// The renderer that will be used to draw the tree structure
/// </summary>
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public virtual BaseRenderer TreeColumnRenderer
{
get { return treeRenderer; }
set {
treeRenderer = value;
if (this.Columns.Count > 0)
this.GetColumn(0).Renderer = value;
}
}
private BaseRenderer treeRenderer;
/// <summary>
/// Should a wait cursor be shown when a branch is being expanded?
/// </summary>
/// <remarks>When this is true, the wait cursor will be shown whilst the children of the
/// branch are being fetched. If the children of the branch have already been cached,
/// the cursor will not change.</remarks>
[Category("Behavior - ObjectListView"),
Description("Should a wait cursor be shown when a branch is being expaned?"),
DefaultValue(true)]
public virtual bool UseWaitCursorWhenExpanding
{
get { return useWaitCursorWhenExpanding; }
set { useWaitCursorWhenExpanding = value; }
}
private bool useWaitCursorWhenExpanding = true;
/// <summary>
/// The model that is used to manage the tree structure
/// </summary>
protected Tree TreeModel;
//------------------------------------------------------------------------------------------
// Accessing
/// <summary>
/// Return true if the branch at the given model is expanded
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public virtual bool IsExpanded(Object model)
{
Branch br = this.TreeModel.GetBranch(model);
return (br != null && br.IsExpanded);
}
//------------------------------------------------------------------------------------------
// Commands
/// <summary>
/// Collapse the subtree underneath the given model
/// </summary>
/// <param name="model"></param>
public virtual void Collapse(Object model)
{
if (this.GetItemCount() == 0)
return;
IList selection = this.SelectedObjects;
int idx = this.TreeModel.Collapse(model);
if (idx >= 0) {
this.UpdateVirtualListSize();
this.SelectedObjects = selection;
this.RedrawItems(idx, this.GetItemCount() - 1, false);
}
}
/// <summary>
/// Collapse all subtrees within this control
/// </summary>
public virtual void CollapseAll()
{
if (this.GetItemCount() == 0)
return;
IList selection = this.SelectedObjects;
int idx = this.TreeModel.CollapseAll();
if (idx >= 0) {
this.UpdateVirtualListSize();
this.SelectedObjects = selection;
this.RedrawItems(idx, this.GetItemCount() - 1, false);
}
}
/// <summary>
/// Expand the subtree underneath the given model object
/// </summary>
/// <param name="model"></param>
public virtual void Expand(Object model)
{
if (this.GetItemCount() == 0)
return;
IList selection = this.SelectedObjects;
int idx = this.TreeModel.Expand(model);
if (idx >= 0) {
this.UpdateVirtualListSize();
this.SelectedObjects = selection;
this.RedrawItems(idx, this.GetItemCount() - 1, false);
if (this.RevealAfterExpand && idx > 0) {
this.BeginUpdate();
try {
int countPerPage = NativeMethods.GetCountPerPage(this);
int descedentCount = this.TreeModel.GetVisibleDescendentCount(model);
if (descedentCount < countPerPage)
this.EnsureVisible(idx + descedentCount);
else
this.TopItemIndex = idx;
}
finally {
this.EndUpdate();
}
}
}
}
/// <summary>
/// Expand all the branches within this tree recursively.
/// </summary>
/// <remarks>Be careful: this method could take a long time for large trees.</remarks>
public virtual void ExpandAll()
{
if (this.GetItemCount() == 0)
return;
IList selection = this.SelectedObjects;
int idx = this.TreeModel.ExpandAll();
if (idx >= 0) {
this.UpdateVirtualListSize();
this.SelectedObjects = selection;
this.RedrawItems(idx, this.GetItemCount() - 1, false);
}
}
/// <summary>
/// Update the rows that are showing the given objects
/// </summary>
public override void RefreshObjects(IList modelObjects)
{
if (this.InvokeRequired) {
this.Invoke((MethodInvoker)delegate { this.RefreshObjects(modelObjects); });
return;
}
if (modelObjects.Count == 0)
return;
IList selection = this.SelectedObjects;
// Refresh each object, remembering where the first update occured
int firstChange = Int32.MaxValue;
foreach (Object x in modelObjects) {
int idx = this.TreeModel.RebuildChildren(x);
if (idx >= 0)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -