当设置了 CheckedAspectName 名称时,为什么复选标记不会出现在 TreeListView 复选框中(以及如何以编程方式设置复选框)?

Why won't checkmarks appear in an TreeListView checkbox when CheckedAspectName name is set (and how do I programmatically set checkboxes)?

我的问题基于 ObjectListView gettingstarted code (GettingStartedTree project ) ObjectListView sourceforge 在线文档的 Getting Started 部分中提到的

我的目标是将 checkboxes 添加到 GettingStartedTree 项目中 TreeListView 的标题列。

我能够简单地通过将 treeListView1.CheckBoxes 设置为 true 并将 treeListView1.CheckedAspectName 设置为 Title(参见下面的 After making changes)来添加复选框,如说明。 问题: 但是,当我 运行 程序并单击一个复选框时,复选框中 不会 出现复选标记。我希望用户能够 "check" UI 上的复选框。

注意:如果我将 treeListView1.CheckBoxes 设置为 true 并将 treeListView1.CheckedAspectName 设置为 null,则复选标记 出现在复选框中。

TreeListView 配置正确吗?

进行任何更改之前

treeListView1:

CheckBoxes = false
CheckedAspectName = null

OLV 列 Collection,olvColumn1(标题):

Name = olvColumn1
AspectName = Title
CheckBoxes = false (Title column)

修改后

treeListView1:

CheckBoxes = true
CheckedAspectName = Title

OLV 列 Collection,olvColumn1(标题):

Name = olvColumn1
AspectName = Title
CheckBoxes = false (Title column)

HierarchicalCheckboxes 不适用于 CheckedAspectName(或 CheckedAspectGetter),尽管找到这方面的文档并不容易。

这里有一些附加信息:

Hierarchy-aware checkboxes

只是为了说明这一点...

要启用 TreeListView(或 ObjectListView 的任何其他版本)的复选框,只需在控件本身上将 CheckBoxes 设置为 true

这就是你需要做的。

如果您的模型上有一个 属性 指示是否应选中一行,您可以通过将 CheckedAspectName 设置为该 属性。例如,如果您的模型有一个 IsSelected 属性,那么这会将复选框挂接到那个 属性:

treeListView1.CheckedAspectName = "IsSelected";

属性 的类型必须是 bool(如果您有三态复选框,则为 bool?)。因此,在问题中,将 CheckedAspectName 设置为 "Title" 无法工作,因为 Title 不是布尔值。

不要更改任何 列中的任何值 以使复选框起作用。

如果您使用的是分层复选框,则不能使用 CheckedAspectName 或任何其他最终会安装 CheckStateGetter 的东西。 Patrick 提到的 Hierarchy-aware checkboxes 页面上的文档解释了原因。

更新

After posting this answer, I came across a TreeViewList property named HierarchicalCheckboxes that I hadn't noticed before and that I don't see discussed/described anywhere in the ObjectListView sourceforge docs. I now believe this is what Grammarian was referring to in his comment under Patricks answer. In my code (in this answer), this property is set false, so I assume I'm NOT using HierarchicalCheckboxes. I thought hierarchical checkboxes were checkboxes in a model that has a hierarchical structure like the model in my code in this answer.

I guess/assume HierarchicalCheckboxes is associated with the write-up on Hierarchy-aware checkboxes , although I'm not sure. ObjectListView is a great control. I just wish the ObjectListView sourceforge docs did a better job differentiating the explaining the collection of lists required to draw the treeview portion of a TreeListView along with adding and managing checkboxes to such a model both with with and without the use of the hierarchical checkboxes feature (enabled by the property HierarchicalCheckboxes). I guess all the pieces are in the docs, but its spread out and a bit disjointed.

更新结束

为了清楚起见,我有点被迫自己回答这个问题......

温馨提示:所有这些都是基于原问题中描述的gettingstartedcode

首先,我最初的问题的答案是清除 treeview1.CheckedAspectName,就像在原始示例代码中一样。这样做会导致在单击复选框时(如预期的那样)在复选框中出现复选标记。

Grammarian 亲切地描述了如何以编程方式 check/uncheck 一个复选框(我假设)一个 non-hierarchical checkbox 场景,即添加一个 bool?bool 属性 到模型并将 属性 的名称添加到 TreeListView 的 CheckedAspectName 属性。

我得出的结论是这对 hierarchical checkboxes 不起作用,因为 Grammarian 说了以下内容并将 bool?bool 属性 的名称加载到根据他的解释,CheckedAspectName 是必需的。

If you are using hierarchical checkboxes, you can't use CheckedAspectName or anything else that eventually installs a CheckStateGetter

因此,忽略为 hierarchical checkboxes 设置 CheckedAspectName 属性 无效的建议,我在我的层次结构中的所有级别实施了 bool? isChecked 属性模型。它工作正常,即我现在可以通过模型以编程方式检查层次结构中任何级别的复选框。

下面是一个简单示例的代码(我需要编写代码来根据子行的选中或未选中为父行设置三态复选框。)。 “# To Keep”列设计为仅与层次结构中的 2 级项目相关

表单只有一个 FooTreeListView : BrightIdeasSoftware.TreeListView 控件

FooTreeListView.cs

using System;
using System.Collections.Generic;

namespace ObjectListView_TreeListView
{
    class FooTreeListView : BrightIdeasSoftware.TreeListView
    {
        private List<Categories> categoriesList;

        private readonly string[] categoryDescriptors = { "Cat A", "Cat B", "Cat C", "Cat D" };

        internal List<Categories> CategoriesList { get => categoriesList; set => categoriesList = value; }

        public enum CategoryEnum
        {
            CategoryA = 0,
            CategoryB = 1,
            CategoryC = 2,
            CategoryD = 3
        }

        public FooTreeListView() : base()
        {
            CategoriesList = new List<Categories>();
            CategoriesList.Clear();

            CanExpandGetter = delegate (Object x)
            {
                if (x is Categories && ((Categories)x).ItemList.Count > 0)
                {
                    return true;
                }

                if (x is Categories.Item && ((Categories.Item)x).ActionList.Count > 0)
                {return true;
                }

                return false;

            };

            ChildrenGetter = delegate (Object x)
            {
                if (x is Categories)
                    return ((Categories)x).ItemList;

                if (x is Categories.Item)
                    return ((Categories.Item)x).ActionList;

                throw new ArgumentException("Should be Categories or Categories.Item");
            };

            //Load the 4 top-level categories into the tree
            CategoriesList.Add(new Categories(categoryDescriptors[(int)CategoryEnum.CategoryA],false));
            CategoriesList.Add(new Categories(categoryDescriptors[(int)CategoryEnum.CategoryB], false));
            CategoriesList.Add(new Categories(categoryDescriptors[(int)CategoryEnum.CategoryC], false));
            CategoriesList.Add(new Categories(categoryDescriptors[(int)CategoryEnum.CategoryD], false));
        }

        internal class Categories
        {
            private string action;
            private bool? isChecked;
            public string Action { get { return action; } set { action = value; } }
            public bool? IsChecked { get => isChecked; set => isChecked = value; }

            private List<Item> itemList;
            internal List<Item> ItemList { get => itemList; set => itemList = value; }

            public Categories(string action, bool? isChecked)
            {
                this.action = action;
                this.isChecked = isChecked;
                ItemList = new List<Item>();
            }

            internal class Item
            {
                private string action;
                private bool? isChecked;
                private int numberToKeep = 0;
                private List<ItemAction> actionList;

                public string Action { get { return action; } set { action = value; } }
                public int NumberToKeep { get => numberToKeep; set => numberToKeep = value; }
                public bool? IsChecked { get => isChecked; set => isChecked = value; }

                internal List<ItemAction> ActionList { get => actionList; set => actionList = value; }

                internal Item(string action, bool? isChecked, int numberToKeep)
                {
                    this.action = action;
                    this.isChecked = isChecked;
                    this.NumberToKeep = numberToKeep;
                    ActionList = new List<ItemAction>();
                }

                internal class ItemAction
                {
                    private string action;
                    private bool? isChecked;
                    public string Action { get { return action; } set { action = value; } }
                    public bool? IsChecked { get { return isChecked; } set { isChecked = value; } }

                    internal ItemAction(string action, bool? isChecked)
                    {
                        this.action = action;
                        this.isChecked = isChecked;
                    }
                }
            }
        }

        public void AddCategoryItemName(CategoryEnum category, string itemName, bool? isChecked, int numberToKeep)
        {
            CategoriesList[(int)category].ItemList.Add(new Categories.Item(itemName, isChecked, numberToKeep));
        }

        public void AddItemAction(CategoryEnum category, string itemName, string action, Boolean isChecked)
        {
            Categories.Item itemMatch = CategoriesList[(int)category].ItemList.Find(x => x.Action.Equals(itemName));

            if (itemMatch != null)
            {
                itemMatch.ActionList.Add(new Categories.Item.ItemAction(action, isChecked));
            }
            else
            {
                throw new ArgumentException(String.Format("Can't find treeviewlist item '{0}'->'{1}'", categoryDescriptors[(int)category], itemName));
            }
        }

        public void AddItemAction(CategoryEnum category, string itemName, string action)
        {
            Categories.Item itemMatch = CategoriesList[(int)category].ItemList.Find(x => x.Action.Equals(itemName));

            if (itemMatch != null)
            {
                itemMatch.ActionList.Add(new Categories.Item.ItemAction(action, false));
            }
            else
            {
                throw new ArgumentException(String.Format("Can't find treeviewlist item '{0}'->'{1}'", categoryDescriptors[(int)category], itemName));
            }
        }

        public void LoadTree()
        {
            Roots = CategoriesList;
            ExpandAll();
        }
    }
}

Form1.cs

using System.Windows.Forms;
using static ObjectListView_TreeListView.FooTreeListView;

namespace ObjectListView_TreeListView
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            SuspendLayout();

            xenSnapshotsTreeListView1.AddCategoryItemName(CategoryEnum.CategoryA, "Item A", true, 0);
            xenSnapshotsTreeListView1.AddCategoryItemName(CategoryEnum.CategoryA, "Item B", false, 1);

            xenSnapshotsTreeListView1.AddItemAction(CategoryEnum.CategoryA, "Item A", "Item A foo", true);
            xenSnapshotsTreeListView1.AddItemAction(CategoryEnum.CategoryA, "Item A", "Item A bar", false);

            xenSnapshotsTreeListView1.AddItemAction(CategoryEnum.CategoryA, "Item B", "Item B foo");
            xenSnapshotsTreeListView1.AddItemAction(CategoryEnum.CategoryA, "Item B", "Item B bar", true);

            xenSnapshotsTreeListView1.LoadTree();

            ResumeLayout();
        }
    }
}

Form1.Designer.cs

namespace ObjectListView_TreeListView
{
    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.xenSnapshotsTreeListView1 = new ObjectListView_TreeListView.FooTreeListView();
            this.olvColumnAction = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn()));
            this.olvColumnNumbSsToKeep = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn()));
            ((System.ComponentModel.ISupportInitialize)(this.xenSnapshotsTreeListView1)).BeginInit();
            this.SuspendLayout();
            // 
            // xenSnapshotsTreeListView1
            // 
            this.xenSnapshotsTreeListView1.AllColumns.Add(this.olvColumnAction);
            this.xenSnapshotsTreeListView1.AllColumns.Add(this.olvColumnNumbSsToKeep);
            this.xenSnapshotsTreeListView1.CellEditUseWholeCell = false;
            this.xenSnapshotsTreeListView1.CheckBoxes = true;
            this.xenSnapshotsTreeListView1.CheckedAspectName = "IsChecked";
            this.xenSnapshotsTreeListView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
            this.olvColumnAction,
            this.olvColumnNumbSsToKeep});
            this.xenSnapshotsTreeListView1.Cursor = System.Windows.Forms.Cursors.Default;
            this.xenSnapshotsTreeListView1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.xenSnapshotsTreeListView1.GridLines = true;
            this.xenSnapshotsTreeListView1.Location = new System.Drawing.Point(0, 0);
            this.xenSnapshotsTreeListView1.MultiSelect = false;
            this.xenSnapshotsTreeListView1.Name = "xenSnapshotsTreeListView1";
            this.xenSnapshotsTreeListView1.ShowGroups = false;
            this.xenSnapshotsTreeListView1.ShowImagesOnSubItems = true;
            this.xenSnapshotsTreeListView1.Size = new System.Drawing.Size(800, 450);
            this.xenSnapshotsTreeListView1.TabIndex = 0;
            this.xenSnapshotsTreeListView1.UseAlternatingBackColors = true;
            this.xenSnapshotsTreeListView1.UseCompatibleStateImageBehavior = false;
            this.xenSnapshotsTreeListView1.View = System.Windows.Forms.View.Details;
            this.xenSnapshotsTreeListView1.VirtualMode = true;
            // 
            // olvColumnAction
            // 
            this.olvColumnAction.AspectName = "Action";
            this.olvColumnAction.Text = "Action";
            this.olvColumnAction.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
            this.olvColumnAction.Width = 200;
            // 
            // olvColumnNumbSsToKeep
            // 
            this.olvColumnNumbSsToKeep.AspectName = "NumberToKeep";
            this.olvColumnNumbSsToKeep.Text = "# To Keep";
            this.olvColumnNumbSsToKeep.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
            this.olvColumnNumbSsToKeep.Width = 65;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(800, 450);
            this.Controls.Add(this.xenSnapshotsTreeListView1);
            this.Name = "Form1";
            this.Text = "Form1";
            ((System.ComponentModel.ISupportInitialize)(this.xenSnapshotsTreeListView1)).EndInit();
            this.ResumeLayout(false);

        }

        #endregion

        private FooTreeListView xenSnapshotsTreeListView1;
        private BrightIdeasSoftware.OLVColumn olvColumnAction;
        private BrightIdeasSoftware.OLVColumn olvColumnNumbSsToKeep;
    }
}