WinForms TreeView with both radios and checkboxes

て烟熏妆下的殇ゞ 提交于 2019-12-19 11:02:13

问题


I have a case where I would like TreeView to be able to show radio buttons on multiple root nodes, and checkboxes on their children. There would only be one level of children beneath any root node.

The radios should also behave like a group, ie one root is selected and the others' radios deselect.

I've been trying to fake it with images, but it doesn't look realistic. I originally had a listbox and a separate checkedlistbox, but the usability gods struck it down.

Has anyone implemented this functionality or have another suggestion?

Think of it this way:

(o) McDonalds
   [ ] Burger
   [ ] Fries
   [ ] Drink
(o) Burger King
   [ ] Burger
   [ ] Fries
   [ ] Drink
(*) Wendy's
   [x] Burger
   [x] Fries
   [ ] Drink

You can have one big option, but select 1..n underneath the big option.


回答1:


I came up with a solution based on the article http://www.codeproject.com/KB/combobox/RadioListBoxDotNetVersion.aspx. My implementation inherits from CheckedListBox and includes the following methods:

    protected override void OnDrawItem (DrawItemEventArgs e)
    {
        // Erase all background if control has no items
        if (e.Index < 0 || e.Index > this.Items.Count - 1)
        {
            e.Graphics.FillRectangle(new SolidBrush(this.BackColor), this.ClientRectangle);
            return;
        }

        // Calculate bounds for background, if last item paint up to bottom of control
        Rectangle rectBack = e.Bounds;
        if (e.Index == this.Items.Count - 1)
            rectBack.Height = this.ClientRectangle.Top + this.ClientRectangle.Height - e.Bounds.Top;
        e.Graphics.FillRectangle(new SolidBrush(this.BackColor), rectBack);

        // Determines text color/brush
        Brush brushText = SystemBrushes.FromSystemColor(this.ForeColor);
        if ((e.State & DrawItemState.Disabled) == DrawItemState.Disabled || (e.State & DrawItemState.Grayed) == DrawItemState.Grayed)
            brushText = SystemBrushes.GrayText;
        Boolean bIsChecked = this.GetItemChecked(e.Index);

        String strText;
        if (!string.IsNullOrEmpty(DisplayMember)) // Bound Datatable? Then show the column written in Displaymember
            strText = ((System.Data.DataRowView) this.Items[e.Index])[this.DisplayMember].ToString();
        else
            strText = this.Items[e.Index].ToString();

        Size sizeGlyph;
        Point ptGlyph;

        if (this.GetItemType(e.Index) == ItemType.Radio)
        {
            RadioButtonState stateRadio = bIsChecked ? RadioButtonState.CheckedNormal : RadioButtonState.UncheckedNormal;
            if ((e.State & DrawItemState.Disabled) == DrawItemState.Disabled || (e.State & DrawItemState.Grayed) == DrawItemState.Grayed)
                stateRadio = bIsChecked ? RadioButtonState.CheckedDisabled : RadioButtonState.UncheckedDisabled;

            // Determines bounds for text and radio button
            sizeGlyph = RadioButtonRenderer.GetGlyphSize(e.Graphics, stateRadio);
            ptGlyph = e.Bounds.Location;
            ptGlyph.X += 4; // a comfortable distance from the edge
            ptGlyph.Y += (e.Bounds.Height - sizeGlyph.Height) / 2;

            // Draws the radio button
            RadioButtonRenderer.DrawRadioButton(e.Graphics, ptGlyph, stateRadio);
        }
        else
        {
            CheckBoxState stateCheck = bIsChecked ? CheckBoxState.CheckedNormal : CheckBoxState.UncheckedNormal;
            if ((e.State & DrawItemState.Disabled) == DrawItemState.Disabled || (e.State & DrawItemState.Grayed) == DrawItemState.Grayed)
                stateCheck = bIsChecked ? CheckBoxState.CheckedDisabled : CheckBoxState.UncheckedDisabled;

            // Determines bounds for text and radio button
            sizeGlyph = CheckBoxRenderer.GetGlyphSize(e.Graphics, stateCheck);
            ptGlyph = e.Bounds.Location;
            ptGlyph.X += 20; // a comfortable distance from the edge
            ptGlyph.Y += (e.Bounds.Height - sizeGlyph.Height) / 2;

            // Draws the radio button
            CheckBoxRenderer.DrawCheckBox(e.Graphics, ptGlyph, stateCheck);
        }

        // Draws the text
        Rectangle rectText = new Rectangle(ptGlyph.X + sizeGlyph.Width + 3, e.Bounds.Y, e.Bounds.Width - sizeGlyph.Width, e.Bounds.Height);
        e.Graphics.DrawString(strText.Substring(4), e.Font, brushText, rectText, this.oAlign);

        // If the ListBox has focus, draw a focus rectangle around the selected item.
        e.DrawFocusRectangle();
    }

    protected override void OnItemCheck (ItemCheckEventArgs ice)
    {
        base.OnItemCheck(ice);

        if (ice.NewValue == CheckState.Unchecked)
            return;

        if (this.GetItemType(ice.Index) == ItemType.Radio) // if they selected a root, deselect other roots and their children
        {
            for (Int32 i = 0; i < this.Items.Count; ++i)
            {
                if (i == ice.Index)
                    continue;
                if (this.GetItemType(i) == ItemType.Radio)
                {
                    this.SetItemChecked(i, false);
                    Int32 j = i + 1;
                    while (j < this.Items.Count && this.GetItemType(j) == ItemType.Checkbox)
                    {
                        this.SetItemChecked(j, false);
                        j++;
                    }
                }
            }
        }
        else if (this.GetItemType(ice.Index) == ItemType.Checkbox) // they selected a child; select the root too and deselect other roots and their children
        {
            // Find parent
            Int32 iParentIdx = ice.Index - 1;
            while (iParentIdx >= 0 && this.GetItemType(iParentIdx) == ItemType.Checkbox)
                iParentIdx--;
            this.SetItemChecked(iParentIdx, true);
        }
    }

    protected ItemType GetItemType (Int32 iIdx)
    {
        String strText = this.Items[iIdx].ToString();
        if (strText.StartsWith("(o)"))
            return (ItemType.Radio);
        else if (strText.StartsWith("[x]"))
            return (ItemType.Checkbox);

        throw (new Exception("Invalid item type"));
    }


来源:https://stackoverflow.com/questions/3375799/winforms-treeview-with-both-radios-and-checkboxes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!