65.9K
CodeProject 正在变化。 阅读更多。
Home

简单的三态树状视图

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2012 年 3 月 5 日

CPOL
viewsIcon

23922

这是“简单的三态树状视图”的替代方案

引言

我遇到了原始代码的太多问题,决定重写大部分代码。

Using the Code

在更改后“刷新”treeview不再需要(但仍然有效)。 如果使用函数“SetState”设置其状态,新添加节点的复选框将自动显示。 或者,您可以使用函数“InitializeCBImages”(对于所有节点)或“InitializeStates”(对于特定的节点集合)以其初始状态显示checkbox

BeforeCheckAfterCheck现在仅对由SetState函数或鼠标单击修改的节点触发。 其他更改(父级和子级状态)会触发一个新事件:AutoCheck

这是类的新代码

public class TriStateCBTreeView : TreeView {

    // ~~~ fields ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    ImageList _ilStateImages;
    bool _bUseTriState;
    bool _bCheckBoxesVisible;
    bool _bPreventCheckEvent;

    // ~~~ constructor ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /// <summary>
    /// Creates a new instance
    /// of this control.
    /// </summary>
    public TriStateCBTreeView()
        : base() {
        _ilStateImages = new ImageList();                            // first we create 
                                                                     // our state image
        CheckBoxState cbsState = CheckBoxState.UncheckedNormal;      // list and pre-init 
                                                                     // check state.

        for (int i = 0; i <= 2; i++) {                               // let's iterate each tri-state
            Bitmap bmpCheckBox = new Bitmap(16, 16);                 // creating a new 
                                                                     // checkbox bitmap
            Graphics gfxCheckBox = Graphics.FromImage(bmpCheckBox);  // and getting 
                                                                     // graphics object from
            switch (i)                                               // it...
            {
                case 0: cbsState = CheckBoxState.UncheckedNormal; break;
                case 1: cbsState = CheckBoxState.CheckedNormal; break;
                case 2: cbsState = CheckBoxState.MixedNormal; break;
            }
            CheckBoxRenderer.DrawCheckBox(gfxCheckBox, new Point(2, 2), cbsState);  // ...rendering
            gfxCheckBox.Save();                                         // the checkbox and
            _ilStateImages.Images.Add(bmpCheckBox);                     // adding to state image list.
        }

        _bUseTriState = true;
    }

    // ~~~ properties ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /// <summary>
    /// Gets or sets to display
    /// checkboxes in the tree
    /// view.
    /// </summary>
    [Category("Appearance")]
    [Description("Sets tree view to display checkboxes or not.")]
    [DefaultValue(false)]
    public new bool CheckBoxes {
        get { return _bCheckBoxesVisible; }
        set {
            _bCheckBoxesVisible = value;
            base.CheckBoxes = _bCheckBoxesVisible;
            this.StateImageList = _bCheckBoxesVisible ? _ilStateImages : null;
        }
    }

    [Browsable(false)]
    public new ImageList StateImageList {
        get { return base.StateImageList; }
        set { base.StateImageList = value; }
    }

    /// <summary>
    /// Gets or sets to support
    /// tri-state in the checkboxes
    /// or not.
    /// </summary>
    [Category("Appearance")]
    [Description("Sets tree view to use tri-state checkboxes or not.")]
    [DefaultValue(true)]
    public bool CheckBoxesTriState {
        get { return _bUseTriState; }
        set { _bUseTriState = value; }
    }

    // ~~~ functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /// <summary>
    /// Changes nodes state.
    /// </summary>

    protected void SetParentState(TreeNode tNode) {
        TreeNode ParentNode = tNode.Parent;
        if (ParentNode != null) {
            try {
                if (tNode.StateImageIndex == 2) {                   // if the current node has 
                                                                    // a mixed state
                    ParentNode.Checked = false;                     // then its parent is set to 
                                                                    // the same state
                    ParentNode.StateImageIndex = 2;
                    return;
                }
                int CheckedCount = 0;
                int UnCheckedCount = 0;
                foreach (TreeNode ChildNode in ParentNode.Nodes) {  // we count the checked 
                                                                    // and unchecked states
                    if (ChildNode.StateImageIndex <= 0)             // of each node at 
                                                                    // the current level
                        UnCheckedCount++;
                    else if (ChildNode.StateImageIndex == 1)
                        CheckedCount++;
                    if (ChildNode.StateImageIndex == 2 ||           // if one node has a 
                                                                    // mixed state or there
                       (CheckedCount > 0 && UnCheckedCount > 0)) {  // are checked and 
                                                                    // unchecked states, then
                        ParentNode.Checked = false;                 // the parent node is set to 
                                                                    // a mixed state
                        ParentNode.StateImageIndex = 2;
                        return;
                    }
                }
                if (UnCheckedCount > 0) {
                    ParentNode.Checked = false;
                    ParentNode.StateImageIndex = 0;
                }
                else if (CheckedCount > 0) {
                    ParentNode.Checked = true;
                    ParentNode.StateImageIndex = 1;
                }
            }
            finally {
                SetParentState(ParentNode);                         // the parent node becomes 
                                                                    // the current node
            }
        }
    }
    protected void SetChildrenState(TreeNode tNode, bool RootNode) {
        if (!RootNode) {
            tNode.Checked = (tNode.Parent.StateImageIndex == 1);    // the child state 
                                                                    // is inherited
            tNode.StateImageIndex = tNode.Parent.StateImageIndex;   // from the parent state
        }
        foreach (TreeNode ChildNode in tNode.Nodes)
            SetChildrenState(ChildNode, false);
    }
    public void SetState(TreeNode tNode, int NewState) {
        if (NewState < 0 || NewState > 2)
            NewState = 0;
        tNode.Checked = (NewState == 1);
        if (tNode.Checked == (NewState == 1)) {                     // we verify if the checked 
                                                                    // state has
            tNode.StateImageIndex = NewState;                       // not been cancelled in a 
                                                                    // BeforeCheck event

            _bPreventCheckEvent = true;

            SetParentState(tNode);
            SetChildrenState(tNode, true);

            _bPreventCheckEvent = false;
        }
    }

    /// <summary>
    /// Initializes the nodes state.
    /// </summary>

    public void InitializeStates(TreeNodeCollection tNodes) {
        foreach (TreeNode tnCurrent in tNodes) {                   // set tree state image
            if (tnCurrent.StateImageIndex == -1) {                 // to each child node...
                _bPreventCheckEvent = true;

                if (tnCurrent.Parent != null) {
                    tnCurrent.Checked = tnCurrent.Parent.Checked;
                    tnCurrent.StateImageIndex = tnCurrent.Parent.StateImageIndex;
                }
                else
                    tnCurrent.StateImageIndex = tnCurrent.Checked ? 1 : 0;

                _bPreventCheckEvent = false;
            }
            InitializeStates(tnCurrent.Nodes);
        }
    }
    public void InitializeCBImages() {
        if (!CheckBoxes)                        // nothing to do here if
            return;                             // checkboxes are hidden.

        base.CheckBoxes = false;                // hide normal checkboxes...

        InitializeStates(this.Nodes);
    }

    /// <summary>
    /// Refreshes this control.
    /// </summary>

    public override void Refresh() {
        base.Refresh();

        InitializeCBImages();
    }

    // ~~~ events ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    protected override void OnLayout(LayoutEventArgs levent) {
        base.OnLayout(levent);

        InitializeCBImages();
    }

    protected override void OnAfterExpand(TreeViewEventArgs e) {
        if (CheckBoxes)
            InitializeStates(e.Node.Nodes);

        base.OnAfterExpand(e);
    }

    public delegate void AutoCheckEventHandler(object sender, TreeViewEventArgs e);
    public event AutoCheckEventHandler AutoCheck;

    protected override void OnBeforeCheck(TreeViewCancelEventArgs e) {
        if (_bPreventCheckEvent)
            return;

        base.OnBeforeCheck(e);
    }

    protected override void OnAfterCheck(TreeViewEventArgs e) {
        if (_bPreventCheckEvent) {
            if (AutoCheck != null)
                AutoCheck(this, e);
            return;
        }

        base.OnAfterCheck(e);
    }

    protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e) {
        base.OnNodeMouseClick(e);

        int iSpacing = ImageList == null ? 0 : 20;      // if user clicked area
        if (e.X > e.Node.Bounds.Left - iSpacing ||       // *not* used by the state
            e.X < e.Node.Bounds.Left - (iSpacing + 14) ||    // image we can leave here.
            e.Button != MouseButtons.Left) {
            return;
        }

        SetState(e.Node, e.Node.Checked ? 0 : 1);
    }
}

历史

  • 2012/03/05:首次提交
© . All rights reserved.