📄 treelistview.cs
字号:
firstChange = Math.Min(firstChange, idx);
}
this.UpdateVirtualListSize();
this.SelectedObjects = selection;
// Redraw everything from the first update to the end of the list
this.RedrawItems(firstChange, this.GetItemCount() - 1, false);
}
/// <summary>
/// Toggle the expanded state of the branch at the given model object
/// </summary>
/// <param name="model"></param>
public virtual void ToggleExpansion(Object model)
{
if (this.IsExpanded(model))
this.Collapse(model);
else
this.Expand(model);
}
//------------------------------------------------------------------------------------------
// Delegates
/// <summary>
/// Delegates of this type are use to decide if the given model object can be expanded
/// </summary>
/// <param name="model">The model under consideration</param>
/// <returns>Can the given model be expanded?</returns>
public delegate bool CanExpandGetterDelegate(Object model);
/// <summary>
/// Delegates of this type are used to fetch the children of the given model object
/// </summary>
/// <param name="model">The parent whose children should be fetched</param>
/// <returns>An enumerable over the children</returns>
public delegate IEnumerable ChildrenGetterDelegate(Object model);
//------------------------------------------------------------------------------------------
// Implementation
/// <summary>
/// Intercept the basic message pump to customise the mouse down and hit testing.
/// </summary>
/// <param name="m"></param>
protected override void WndProc(ref Message m)
{
switch (m.Msg) {
case 0x1012: // LVM_HITTEST = (LVM_FIRST + 18)
this.HandleHitTest(ref m);
break;
default:
base.WndProc(ref m);
break;
}
}
/// <summary>
/// Handle a hit test to account for the indent of the branch
/// </summary>
/// <param name="m"></param>
unsafe protected virtual void HandleHitTest(ref Message m)
{
//THINK: Do we need to do this, since we are using the build-in Level ability of
// of ListCtrl, which should take the indent into account
// We want to change our base behavior by taking the indentation of tree into account
// when performing a hit test. So we figure out which row is at the test point,
// then calculate the indentation for that row, and modify the hit test *inplace*
// so that the normal hittest is done, but indented by the correct amount.
this.DefWndProc(ref m);
NativeMethods.LVHITTESTINFO* hittest = (NativeMethods.LVHITTESTINFO*)m.LParam;
// Find which row was hit...
int row = hittest->iItem;
if (row < 0)
return;
// ...from that decide the model object...
Object model = this.TreeModel.GetNthObject(row);
if (model == null)
return;
// ...and from that, the branch of the tree showing that model...
Branch br = this.TreeModel.GetBranch(model);
if (br == null)
return;
// ...use the indentation on that branch to modify the hittest
hittest->pt_x += (br.Level * TreeRenderer.PIXELS_PER_LEVEL);
this.DefWndProc(ref m);
}
/// <summary>
/// Handle a left button down event
/// </summary>
/// <param name="hti"></param>
/// <returns></returns>
protected override bool ProcessLButtonDown(OlvListViewHitTestInfo hti)
{
// Did they click in the expander?
if (hti.HitTestLocation == HitTestLocation.ExpandButton) {
this.PossibleFinishCellEditing();
this.ToggleExpansion(hti.RowObject);
return true;
}
return base.ProcessLButtonDown(hti);
}
/// <summary>
/// Create a OLVListItem for given row index
/// </summary>
/// <param name="itemIndex">The index of the row that is needed</param>
/// <returns>An OLVListItem</returns>
/// <remarks>This differs from the base method by also setting up the IndentCount property.</remarks>
public override OLVListItem MakeListViewItem(int itemIndex)
{
OLVListItem olvItem = base.MakeListViewItem(itemIndex);
Branch br = this.TreeModel.GetBranch(olvItem.RowObject);
if (br != null)
olvItem.IndentCount = br.Level;
return olvItem;
}
#region Event handlers
/// <summary>
/// Decide if the given key event should be handled as a normal key input to the control?
/// </summary>
/// <param name="keyData"></param>
/// <returns></returns>
protected override bool IsInputKey(Keys keyData)
{
// We want to handle Left and Right keys within the control
if (((keyData & Keys.KeyCode) == Keys.Left) || ((keyData & Keys.KeyCode) == Keys.Right)) {
return true;
} else
return base.IsInputKey(keyData);
}
/// <summary>
/// Handle the keyboard input to mimic a TreeView.
/// </summary>
/// <param name="keyData"></param>
/// <returns>Was the key press handled?</returns>
protected override void OnKeyDown(KeyEventArgs e)
{
OLVListItem focused = this.FocusedItem as OLVListItem;
if (focused == null) {
base.OnKeyDown(e);
return;
}
Object modelObject = focused.RowObject;
Branch br = this.TreeModel.GetBranch(modelObject);
switch (e.KeyCode) {
case Keys.Left:
// If the branch is expanded, collapse it. If it's collapsed,
// select the parent of the branch.
if (br.IsExpanded)
this.Collapse(modelObject);
else {
if (br.ParentBranch != null && br.ParentBranch.Model != null)
this.SelectObject(br.ParentBranch.Model, true);
}
e.Handled = true;
break;
case Keys.Right:
// If the branch is expanded, select the first child.
// If it isn't expanded and can be, expand it.
if (br.IsExpanded) {
if (br.ChildBranches.Count > 0)
this.SelectObject(br.ChildBranches[0].Model, true);
} else {
if (br.CanExpand)
this.Expand(modelObject);
}
e.Handled = true;
break;
}
base.OnKeyDown(e);
}
#endregion
//------------------------------------------------------------------------------------------
// Support classes
/// <summary>
/// A Tree object represents a tree structure data model that supports both
/// tree and flat list operations as well as fast access to branches.
/// </summary>
protected class Tree : IVirtualListDataSource
{
public Tree(TreeListView treeView)
{
this.treeView = treeView;
this.trunk = new Branch(null, this, null);
this.trunk.IsExpanded = true;
}
//------------------------------------------------------------------------------------------
// Properties
/// <summary>
/// This is the delegate that will be used to decide if a model object can be expanded.
/// </summary>
public CanExpandGetterDelegate CanExpandGetter
{
get { return canExpandGetter; }
set { canExpandGetter = value; }
}
private CanExpandGetterDelegate canExpandGetter;
/// <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>
public ChildrenGetterDelegate ChildrenGetter
{
get { return childrenGetter; }
set { childrenGetter = value; }
}
private ChildrenGetterDelegate childrenGetter;
/// <summary>
/// Get or return the top level model objects in the tree
/// </summary>
public IEnumerable RootObjects
{
get { return this.trunk.Children; }
set
{
this.trunk.Children = value;
this.RebuildList();
}
}
/// <summary>
/// What tree view is this Tree the model for?
/// </summary>
public TreeListView TreeView
{
get { return this.treeView; }
}
//------------------------------------------------------------------------------------------
// Commands
/// <summary>
/// Collapse the subtree underneath the given model
/// </summary>
/// <param name="model">The model to be collapsed. If the model isn't in the tree,
/// or if it is already collapsed, the command does nothing.</param>
/// <returns>The index of the model in flat list version of the tree</returns>
public virtual int Collapse(Object model)
{
Branch br = this.GetBranch(model);
if (br == null || !br.IsExpanded)
return -1;
int count = br.NumberVisibleDescendents;
br.Collapse();
// Remove the visible descendents from after the branch itself
int idx = this.GetObjectIndex(model);
this.objectList.RemoveRange(idx + 1, count);
this.RebuildObjectMap(idx + 1);
return idx;
}
/// <summary>
/// Collapse all branches in this tree
/// </summary>
/// <returns>Return the index of the first root that was not collapsed</returns>
public virtual int CollapseAll()
{
foreach (Branch br in this.trunk.ChildBranches) {
if (br.IsExpanded)
br.Collapse();
}
this.RebuildList();
return 0;
}
/// <summary>
/// Expand the subtree underneath the given model object
/// </summary>
/// <param name="model">The model to be expanded. If the model isn't in the tree,
/// or if it cannot be expanded, the command does nothing.</param>
/// <returns>The index of the model in flat list version of the tree</returns>
public virtual int Expand(Object model)
{
Branch br = this.GetBranch(model);
if (br == null || !br.CanExpand)
return -1;
int idx = this.GetObjectIndex(model);
this.InsertChildren(br, idx+1);
return idx;
}
/// <summary>
/// Expand all branches in this tree
/// </summary>
/// <returns>Return the index of the first branch that was expanded</returns>
public virtual int ExpandAll()
{
this.trunk.ExpandAll();
this.Sort(this.lastSortColumn, this.lastSortOrder);
return 0;
}
/// <summary>
/// Return the Branch object that represents the given model in the tree
/// </summary>
/// <param name="model">The model whose branches is to be returned</param>
/// <returns>The branch that represents the given model, or null if the model
/// isn't in the tree.</returns>
public virtual Branch GetBranch(object model)
{
Branch br;
if (this.mapObjectToBranch.TryGetValue(model, out br))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -