-1

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

我的TreeListView

  • 复选框:真
  • CheckedAspectName:{布尔名称?我的模型中的属性)
  • HierarchicalCheckboxes:真
  • TriStateCheckboxes:真
  • 查看详情

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

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

意识到三态复选框循环:选中,不确定,未选中,当用户单击选中的复选框时,我尝试将不确定状态强制为未选中状态,但这不起作用,即 TreeListView 中的复选框状态在之后保持不变执行我的 ObjTreeListViewPreview_ItemChecked 代码。

注意:类 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;
    }

}

TreeListView使用that hastristatehierarchicalcheckboxes可以实现我想要的吗?如果是这样,哪些委托和方法是必要的?

4

1 回答 1

0

我有一个 Winforms可以在用户拒绝设置的情况下TreeListView工作tristate checkboxes。仅当用户将复选框的子对象设置为选中和未选中的混合时,indeterminate该设置才会出现在复选框中。indeterminate

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

它使用 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();
        }
    }
}

类 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; }
        }
    }
}
于 2019-01-14T19:42:02.573 回答