我可以拥有一个仅以编程方式断言(并拒绝给用户)具有不确定设置的TreeListView吗



我使用的是Phillip Piper的ObjectListView包装器,特别是TreeListView

我的TreeListView:

  • 复选框:True
  • CheckedAspectName:我的型号)
  • 层次结构复选框:True
  • TriStateCheckboxes:true
  • 查看:详细信息

我将TriStateCheckboxes设置为True,因为我希望我的TreeListView能够在TreeListView’s复选框中显示indeterminate符号。但是,我不希望用户能够明确地将复选框设置为indeterminate值。相反,当用户单击复选框时,我希望它只在选中和未选中之间切换。如果选中了分支复选框及其所有子复选框,并且子复选框转换为已选中和未选中的混合,则我希望分支的复选框显示indeterminate符号。

换句话说,不确定状态只能以编程方式断言,然后只有在分支的子级的复选框未全部选中或未全部取消选中时才能在分支上断言。

意识到三态复选框是循环的:已选中、不确定、未选中,当用户单击选中的复选框时,我试图将不确定状态强制为未选中状态,但没有成功,即执行ObjTreeListViewPreview_ItemChecked代码后,TreeListView中的复选框状态保持不变。

注意:类ClsTreeListViewPreview派生自TreeListView

private void ObjTreeListViewPreview_ItemChecked(object sender, ItemCheckedEventArgs e)
{
ClsTreeListViewPreview objTreeListViewPreview = (ClsTreeListViewPreview)sender;
if (objTreeListViewPreview.MouseMoveHitTest.Item.CheckState == CheckState.Indeterminate)
{
objTreeListViewPreview.MouseMoveHitTest.Item.CheckState = CheckState.Unchecked;
}
}

对于具有tristatehierarchical复选框的TreeListView,我想要的是可能的吗?如果是,哪些委托和方法是必要的?

我有一个WinformsTreeListViewtristate checkboxes,其中indeterminate设置被拒绝给用户。只有当用户将复选框的子对象设置为已选中和未选中的混合对象时,indeterminate设置才会显示在复选框中。

不使用CCD_ 17。所有复选框设置都是手动管理的。

它使用CheckStatePutter、ItemChecked事件处理程序和HeaderCheckBoxChanging事件处理程序(当使用标头复选框时)手动管理复选框设置

演示代码如下

代码是自包含的(ObjectListView.dll除外)。创建一个带有空表单的Winforms项目,并复制/粘贴下面的两个类

主要形式

using System.Windows.Forms;
namespace NovaSysEng
{
public partial class ClsFormMain : Form
{
public ClsFormMain()
{
InitializeComponent();
ClsTriStateTreeListView objFooTree = new ClsTriStateTreeListView(showHeaderCheckBox: true)
{
Location = new System.Drawing.Point(0, 0),
Dock = DockStyle.Fill
};
// The model supports the creation of a tree hierarchy whose depth is not known until run time.
// Each item requires an id and a parentId just like in a DataTreeListView.
// https://stackoverflow.com/questions/9409021/id-parentid-list-to-hierarchical-list
objFooTree.AddItem(id: 1, parentId: 0, name: "A", isChecked: false, foo: "foo A");
objFooTree.AddItem(id: 2, parentId: 1, name: "A.1", isChecked: false, foo: "foo A.1");
objFooTree.AddItem(id: 3, parentId: 1, name: "A.2", isChecked: false, foo: "foo A.2");
objFooTree.AddItem(id: 4, parentId: 1, name: "A.3", isChecked: false, foo: "foo A.3");
objFooTree.AddItem(id: 5, parentId: 0, name: "B", isChecked: false, foo: "foo B");
objFooTree.AddItem(id: 6, parentId: 5, name: "B.1", isChecked: false, foo: "foo B.1");
objFooTree.AddItem(id: 7, parentId: 5, name: "B.2", isChecked: false, foo: "foo B.2");
objFooTree.AddItem(id: 8, parentId: 5, name: "B.3", isChecked: false, foo: "foo B.3");
objFooTree.AddItem(id: 9, parentId: 8, name: "B.3.1", isChecked: false, foo: "foo B.3.1");
SuspendLayout();
Controls.Add(objFooTree);
objFooTree.Load();
objFooTree.ExpandAll();
objFooTree.AutoResizeColumns();
ResumeLayout();
}
}
}

Class ClsTriStateTreeListView

using BrightIdeasSoftware;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace NovaSysEng
{
class ClsTriStateTreeListView : BrightIdeasSoftware.TreeListView
{
private readonly bool showHeaderCheckBox = false;
private List<ClsItem> itemList;
readonly OLVColumn olvColumnName;
readonly OLVColumn olvColumnFoo;
/// <summary>
/// CTOR
/// </summary>
public ClsTriStateTreeListView() : this(false)
{
}
/// <summary>
/// CTOR
/// </summary>
/// <param name="showHeaderCheckBox"></param>
public ClsTriStateTreeListView(bool showHeaderCheckBox) : base()
{
this.showHeaderCheckBox = showHeaderCheckBox;
CheckBoxes = true;
View = View.Details;
UseAlternatingBackColors = true;
MultiSelect = false;
CheckedAspectName = "";
GridLines = true;
OwnerDraw = true;
ShowGroups = false;
TriStateCheckBoxes = true;
VirtualMode = true;
HierarchicalCheckboxes = false;
ItemChecked += FooTreeListView_ItemChecked;
HeaderCheckBoxChanging += ClsFooTreeListView_HeaderCheckBoxChanging;
itemList = new List<ClsItem>();
olvColumnName = new BrightIdeasSoftware.OLVColumn
{
AspectName = "Name",
Text = "Name",
HeaderCheckBoxUpdatesRowCheckBoxes = false
};
AllColumns.Add(olvColumnName);
Columns.Add(olvColumnName);
olvColumnFoo = new BrightIdeasSoftware.OLVColumn
{
AspectName = "Foo",
Text = "Foo",
};
AllColumns.Add(olvColumnFoo);
Columns.Add(olvColumnFoo);
// Don't allow the user to put a checkbox in the indeterminate state.
// When clicked, a checkbox cycles through 3 states in the order: checked, indeterminate, unchecked
// So, if the checkbox state is Indeterminate, force it to unchecked.
// An exception is when a checkbox has the Indeterminate setting and the model IgnoreIndeterminate
// property is true. This is for the case when we want to set the checkbox to Indeterminate
// programattically and have it stick.
CheckStatePutter = delegate (Object rowObject, CheckState newValue)
{
if (newValue == CheckState.Indeterminate && ((ClsItem)rowObject).IgnoreIndeterminate)
{
return CheckState.Indeterminate;
}
else if (newValue == CheckState.Indeterminate)
{
return CheckState.Unchecked;
}
else
{
return newValue;
}
};
CanExpandGetter = delegate (Object x)
{
return ((ClsItem)x).Children.Count > 0;
};
ChildrenGetter = delegate (Object x)
{
return ((ClsItem)x).Children;
};
}
/// <summary>
/// If the user clicks on the HeaderCheckBox, set all descendants accordingly
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ClsFooTreeListView_HeaderCheckBoxChanging(object sender, HeaderCheckBoxChangingEventArgs e)
{
ItemChecked -= FooTreeListView_ItemChecked;
foreach (ClsItem topLevelItem in this.Objects)
{
if (e.NewCheckState == CheckState.Checked)
{
CheckObject(topLevelItem);
CheckDescendantCheckboxes(topLevelItem);
}
else
{
UncheckObject(topLevelItem);
UncheckDescendantCheckboxes(topLevelItem);
}
}
ItemChecked += FooTreeListView_ItemChecked;
}
/// <summary>
/// Set the model IgnoreIndeterminate property to true then set the checkbox to Indeterminate
/// using the CheckIndeterminateObject method. CheckStatePutter will be invoked and the 
/// Indeterminate setting will be ignored for this particular case. Finally, set the model
/// IgnoreIndeterminate property to false so an Indeterminate checkbox setting will otherwise
/// be change to an Unchecked setting.
/// </summary>
/// <param name="rowObject"></param>
public override void CheckIndeterminateObject(object rowObject)
{
((ClsItem)rowObject).IgnoreIndeterminate = true;
base.CheckIndeterminateObject(rowObject);
((ClsItem)rowObject).IgnoreIndeterminate = false;
}
private void UncheckDescendantCheckboxes(ClsItem objTreeItemModel)
{
foreach (ClsItem child in GetChildren(objTreeItemModel))
{
UncheckObject(child);
UncheckDescendantCheckboxes(child);
}
}
private void CheckDescendantCheckboxes(ClsItem objTreeItemModel)
{
foreach (ClsItem child in GetChildren(objTreeItemModel))
{
CheckObject(child);
CheckDescendantCheckboxes(child);
}
}
private void FooTreeListView_ItemChecked(object sender, ItemCheckedEventArgs e)
{
Console.WriteLine("In FooTreeListView_ItemChecked");
ItemChecked -= FooTreeListView_ItemChecked;
ClsItem objTreeItemModel = (ClsItem)((OLVListItem)e.Item).RowObject;
//set descendant checkboxes
if (IsChecked(objTreeItemModel))
{
CheckDescendantCheckboxes(objTreeItemModel);
}
else
{
UncheckDescendantCheckboxes(objTreeItemModel);
}
ClsItem objTreeItemModelParent = (ClsItem)GetParent(objTreeItemModel);
//Set ancestor checkboxes 
while (objTreeItemModelParent != null)
{
bool allChildrenChecked = true;
bool allChildrenUnchecked = true;
// If all 1st generation children of the parent are checked, set parent to checked
// If all 1st generation children of the parent are unchecked, set parent to unchecked
// Otherwise, set parent to indeterminte
foreach (ClsItem child in GetChildren(objTreeItemModelParent))
{
if (IsChecked(child))
{
allChildrenUnchecked = false;
}
if (!IsChecked(child))
{
allChildrenChecked = false;
}
if (IsCheckedIndeterminate(child))
{
allChildrenUnchecked = false;
allChildrenChecked = false;
break;
}
}
if (allChildrenChecked)
{
CheckObject(objTreeItemModelParent);
}
else if (allChildrenUnchecked)
{
UncheckObject(objTreeItemModelParent);
}
else
{
CheckIndeterminateObject(objTreeItemModelParent);
}
objTreeItemModelParent = (ClsItem)GetParent(objTreeItemModelParent);
} // while

if (showHeaderCheckBox)
{
bool allChecked = true;
bool allUnchecked = true;
foreach (ClsItem topLevelItem in this.Objects)
{
if (IsChecked(topLevelItem))
{
allUnchecked = false;
}
if (!IsChecked(topLevelItem))
{
allChecked = false;
}
if (IsCheckedIndeterminate(topLevelItem))
{
allUnchecked = false;
allChecked = false;
break;
}
}
HeaderCheckBoxChanging -= ClsFooTreeListView_HeaderCheckBoxChanging;
if (allChecked)
{
CheckHeaderCheckBox(olvColumnName);
}
else if (allUnchecked)
{
UncheckHeaderCheckBox(olvColumnName);
}
else
{
CheckIndeterminateHeaderCheckBox(olvColumnName);
}
HeaderCheckBoxChanging += ClsFooTreeListView_HeaderCheckBoxChanging;
} // if (showHeaderCheckBox)
ItemChecked += FooTreeListView_ItemChecked;
} // private void FooTreeListView_ItemChecked(object sender, ItemCheckedEventArgs e)
public void AddItem(int id, int parentId, string name, bool? isChecked, string foo)
{
itemList.Add(new ClsItem(id, parentId, name, isChecked, foo));
}
/// <summary>
/// After using the AddItem method to add tree items, call this method to load the model into the TreeListView.
/// https://stackoverflow.com/questions/9409021/id-parentid-list-to-hierarchical-list
/// </summary>
public void Load()
{
itemList.ForEach(item => item.Children = itemList.Where(child => child.ParentId == item.Id).ToList());
List<ClsItem> topItems = itemList.Where(item => item.ParentId == 0).ToList();
((OLVColumn)this.Columns[0]).HeaderCheckBox = this.showHeaderCheckBox;
Roots = topItems;
}
/// <summary>
/// This model supports the creation of a tree hierarchy whose depth is not known until run time
/// https://stackoverflow.com/questions/9409021/id-parentid-list-to-hierarchical-list
/// </summary>
private class ClsItem
{
int id;
int parentId;
string name;
bool? isChecked;
string foo;
List<ClsItem> children = new List<ClsItem>();
bool ignoreIndeterminate;
public ClsItem(int id, int parentId, string name, bool? isChecked, string foo)
{
this.id = id;
this.parentId = parentId;
this.name = name;
this.isChecked = isChecked;
this.foo = foo;
}
public bool? IsChecked { get => isChecked; set => isChecked = value; }
public string Name { get => name; set => name = value; }
public string Foo { get => foo; set => foo = value; }
public int Id { get => id; set => id = value; }
public int ParentId { get => parentId; set => parentId = value; }
public List<ClsItem> Children { get => children; set => children = value; }
public bool IgnoreIndeterminate { get => ignoreIndeterminate; set => ignoreIndeterminate = value; }
}
}
}

最新更新