Question

Is there anyone out there that has successfully gotten tri-state checkboxes working correctly in a TreeListView, without using a property in the model object to store the checkstate?

So, for example, a node is checked. All of its children (and childrens children etc) should be checked and then all its parents/grandparents should be checked according to its siblings CheckStates.

I have tried a couple of methods, but each way I get an ArgumentOutOfRange/ArgumentException within the TreeListView class. This includes the following:

  • Storing all the nodes CheckStates in a Dictionary and using as a lookup with CheckStateGetter event
  • Recursively calling a function when an items CheckState changes (and ensuring the subsequent ItemCheck events are ignored while programatically changing CheckStates)
  • Calling a function to determine immediate children/parent states and letting the TreeListView fire the ItemChecked event for each of the affected nodes.

I constantly get errors from the following functions (in TreeListView.cs):

  • GetNthItem()
  • GetChildren() - and then expanding/collapsing the shild in the GUI
  • ProcessLButtonDown()

If anyone out there has been successful with this I am all ears.

Was it helpful?

Solution

I had some problems with the TreeListView as well and found a problem in the GetChildren() functions.

GetChildren was unnecessarily trying to expand the related branch to fetch the children. This caused the internal state to be shown as expanded while the view stayed collapsed and caused problems with internal indexing.

Original method:

    public virtual IEnumerable GetChildren(Object model) {
        Branch br = this.TreeModel.GetBranch(model);
        if (br == null || !br.CanExpand)
            return new ArrayList();

        if (!br.IsExpanded)  // THIS IS THE PROBLEM
            br.Expand();

        return br.Children;
    }

Fixed method:

    public virtual IEnumerable GetChildren(Object model) {
        Branch br = this.TreeModel.GetBranch(model);
        if (br == null || !br.CanExpand)
            return new ArrayList();

        br.FetchChildren();

        return br.Children;
    }

Maybe this solves at least some of your problems.

OTHER TIPS

"Recursively calling a function when an items CheckState changes (and ensuring the subsequent ItemCheck events are ignored while programatically changing CheckStates)"

In case TreeListView self-checking ("from box") is good for you and you just want to know when all staff after your manual some main checkbox clicking is done (this mean: you click, TreeListView (un)check all childrens and parents), so you need to make extra event in ObjectListView Library (in rough way):

\objectlistviewdemo\objectlistview\treelistview.cs

Firstly add event (my events are bad but you know what to fix)

    #region Events

    public delegate void AfterTreeCheckEventHandler(object sender/*, object rowmodel*/);

    /// <summary>
    /// Triggered when clicked main checkbox checked and all related too.
    /// </summary>
    [Category("ObjectListView"),
    Description("This event is triggered when clicked main checkbox checked and all related too.")]
    public event AfterTreeCheckEventHandler AfterTreeCheckAndRecalculate;

    #endregion

    #region OnEvents

    /// <summary>
    /// Tell the world when a cell has finished being edited.
    /// </summary>
    protected virtual void OnAfterTreeCheckAndRecalculate(/*CellEditEventArgs e*/)
    {
        if (this.AfterTreeCheckAndRecalculate != null)
            this.AfterTreeCheckAndRecalculate(this/*, e*/);
    }

    #endregion

Secondly you need to fix "SetObjectCheckedness" method (1 recursive method for 1 simple + 1 recursive)

        /// <summary>
    /// Change the check state of the given object to be the given state.
    /// </summary>
    /// <remarks>
    /// If the given model object isn't in the list, we still try to remember
    /// its state, in case it is referenced in the future.</remarks>
    /// <param name="modelObject"></param>
    /// <param name="state"></param>
    /// <returns>True if the checkedness of the model changed</returns>
    protected override bool SetObjectCheckedness(object modelObject, CheckState state) {
        // If the checkedness of the given model changes AND this tree has 
        // hierarchical checkboxes, then we need to update the checkedness of 
        // its children, and recalculate the checkedness of the parent (recursively)

        bool result = SetObjectCheckednessHelper(modelObject, state, 0);

        if (this.AfterTreeCheckAndRecalculate != null)
            this.AfterTreeCheckAndRecalculate(this); //report that work is done
        return result;
    }

    protected bool SetObjectCheckednessHelper(object modelObject, CheckState state, int i) //recursive
    {
        if (!base.SetObjectCheckedness(modelObject, state))
            return false;

        if (!this.HierarchicalCheckboxes)
            return true;

        // Give each child the same checkedness as the model

        CheckState? checkedness = this.GetCheckState(modelObject);
        if (!checkedness.HasValue || checkedness.Value == CheckState.Indeterminate)
            return true;

        foreach (object child in this.GetChildrenWithoutExpanding(modelObject))
        {
            this.SetObjectCheckednessHelper(child, checkedness.Value, i+1);
        } //(un)check all children checkboxes

        if (i == 0) //recalculate upper levels only in the case of first call
        {
            ArrayList args = new ArrayList();
            args.Add(modelObject);
            this.RecalculateHierarchicalCheckBoxGraph(args); //all upper checkboxes in intermediate state or (un)check 
        }   

        return true;
    }

usage:

    this.olvDataTree.AfterTreeCheckAndRecalculate += new BrightIdeasSoftware.TreeListView.AfterTreeCheckEventHandler(this.olvDataTree_TreeChecked); 

    private void olvDataTree_TreeChecked(object sender)
    {
            //some staff here
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top