带有无线电和复选框的WinForms TreeView

时间:2010-07-30 22:23:23

标签: winforms checkbox radio-button

我有一个案例,我希望TreeView能够在多个根节点上显示单选按钮,并在其子节点上显示复选框。在任何根节点下只有一个级别的子节点。

无线电也应该像一个组一样,即选择一个根,其他无线电取消选择。

我一直试图用图像伪造它,但它看起来不太真实。我最初有一个列表框和一个单独的checkedlistbox,但是可用性之神击倒了它。

有没有人实现此功能或有其他建议?

这样想:

(o)麦当劳 []汉堡
[]炸薯条 []饮料 (o)汉堡王
[]汉堡
[]炸薯条 []饮料 (*)Wendy的 [x]汉堡
[x]炸薯条 []饮料

您可以选择一个大选项,但在大选项下方选择1..n。

1 个答案:

答案 0 :(得分:2)

我想出了一篇基于文章http://www.codeproject.com/KB/combobox/RadioListBoxDotNetVersion.aspx的解决方案。我的实现继承自CheckedListBox,包括以下方法:

    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"));
    }