My question is based on the ObjectListView
gettingstarted code (GettingStartedTree project ) referred to in the Getting Started
section of the ObjectListView
sourceforge online docs.
My goal is to add checkboxes
to the Title column of the TreeListView in the GettingStartedTree project.
I was able to add checkboxes simply by setting treeListView1.CheckBoxes
to true
and setting treeListView1.CheckedAspectName
to Title
(see After making changes
below) as described in the instructions. Problem: However, when I run the program and click on a checkbox, a checkmark does not appear in the checkbox. I expect that a user should be able to "check" a checkbox on the UI.
Note: If I leave treeListView1.CheckBoxes
set to true
and set treeListView1.CheckedAspectName
to null
, then a checkmark does appear in the checkbox.
Am I configuring the TreeListView
correctly?
Before making any changes
treeListView1:
CheckBoxes = false
CheckedAspectName = null
OLVColumn Collection, olvColumn1(Title):
Name = olvColumn1
AspectName = Title
CheckBoxes = false (Title column)
After making changes
treeListView1:
CheckBoxes = true
CheckedAspectName = Title
OLVColumn Collection, olvColumn1(Title):
Name = olvColumn1
AspectName = Title
CheckBoxes = false (Title column)
Update
After posting this answer, I came across a
TreeViewList
property namedHierarchicalCheckboxes
that I hadn't noticed before and that I don't see discussed/described anywhere in theObjectListView
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 usingHierarchicalCheckboxes
. 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 theObjectListView
sourceforge docs did a better job differentiating the explaining the collection of lists required to draw thetreeview
portion of aTreeListView
along with adding and managing checkboxes to such a model both with with and without the use of thehierarchical checkboxes
feature (enabled by the propertyHierarchicalCheckboxes
). I guess all the pieces are in the docs, but its spread out and a bit disjointed.
End of Update
For the sake of clarity I was kind of forced to answer this myself.....
Reminder: all this is based on the gettingstartedcode
described in the original question.
First, the answer to my initial problem was to clear treeview1.CheckedAspectName
as in the original example code. Doing so resulted in a checkmark appearing in the checkbox (as expected) when the checkbox is clicked.
Grammarian kindly described how to programmatically check/uncheck a checkbox for (I assume) a non-hierarchical checkbox
scenario, i.e., add a bool?
or bool
property to the model and add the property's name to the TreeListView's CheckedAspectName property.
I came to the conclusion that this won't work for hierarchical checkboxes
because Grammarian said the following and loading the bool?
or bool
property's name into CheckedAspectName is required based on his explanation.
If you are using hierarchical checkboxes, you can't use CheckedAspectName or anything else that eventually installs a CheckStateGetter
So, ignoring the advice that setting the CheckedAspectName property for hierarchical checkboxes
won't work, I implemented a bool?
isChecked property at all levels in my hierarchical model. It works fine, i.e., I can now programmatically check a checkbox at any level in the hierarchy via the model.
Code for a simple example follows (I need to write the code to set the tri-state checkbox for parent rows based on the checkedness or uncheckedness of child rows.). The column "# To Keep" is designed to be relevant only for the level-2 items in the hierarchy
The form has only a FooTreeListView : BrightIdeasSoftware.TreeListView
control on it
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;
}
}