tags:

views:

35

answers:

2

Are there any controls that can dynamically create a group of radio buttons from a list of objects? Something similar to the CheckedBoxList control, but with mutually exclusive selection. This question points out this control doesn't exist natively for WinForms but are there any third party controls that do this?

+2  A: 

Maybe; But this is easier and better to write yourself, though (unless somebody suggests either a free control or better yet sourcecode you can drop into your project).

A little GUI wisdom (I did not make this up but am too lazy to include references):

If a list of radio buttons will ever have > 7-10 items, use a list box.

Of course I gather either you don't have control of that or if you do, won't settle for that answer.

  • Add a scrollable panel to your form
  • in code, loop thru your list of objects. Inside the loop:
    • Make a new radiobutton
    • set the .top property to the .bottom of the previous one (or 0 if no previous)
    • put a copy of your object in the .Tag property (so you can tell which object was selected)
    • set the width so you don't get a horizontal scrollbar in your scrollable control
    • set the .text appropriately. You may need to truncate to avoid wrapping. If you want to go multiline for lines that wrap, you have to increase the height then, but this would require a lot of gymnastics with control.creategraphics, graphics.MeasureString, and other GDI+ features. See the Bob Powell's GDI+ FAQ.
    • add a handler so the checkchanged can be processed (AddHandler MyRB, addressof CC_Sub)
    • add it to the scrollable control
  • Add the CC_Sub mentioned above - can get right function signature by adding a radiobutton, putting on the handler for CheckChanged, and deleting radio button
  • In this sub, set a form-level variable of type of your class to the tag of the sender (you'll have to do ctypeing)
  • When your user clicks OK, return that variable, that is the object picked.

OK so it looks hard. So is either squeezing this out of management or doling out cash.

If you want fancier stuff, you can make a usercontrol with labels, checkboxes/radio buttons, etc. in it. You have to handle selecting/unselecting. Then add the usercontrol to a scrollable panel instead of the radiobutton. This provides almost unlimited flexibility.

FastAl
The complexity of doing this was the reason I was looking for a third party solution. It looks like I'm going to have to bite the bullet, though.
JChristian
+2  A: 

Control vendors can't make any money with controls like that. Here's some code to get your started:

using System;
using System.Drawing;
using System.Windows.Forms;

class RadioList : ListBox {
    public event EventHandler SelectedOptionChanged;

    public RadioList() {
        this.DrawMode = DrawMode.OwnerDrawFixed;
        this.ItemHeight += 2;
    }
    public int SelectedOption {
        // Current item with the selected radio button
        get { return mSelectedOption; }
        set { 
            if (value != mSelectedOption) {
                Invalidate(GetItemRectangle(mSelectedOption));
                mSelectedOption = value; 
                OnSelectedOptionChanged(EventArgs.Empty);
                Invalidate(GetItemRectangle(value));
            }
        }
    }
    protected virtual void OnSelectedOptionChanged(EventArgs e) {
        // Raise SelectOptionChanged event
        EventHandler handler = this.SelectedOptionChanged;
        if (handler != null) handler(this, e);
    }
    protected override void OnDrawItem(DrawItemEventArgs e) {
        // Draw item with radio button
        using (var br = new SolidBrush(this.BackColor))
            e.Graphics.FillRectangle(br, e.Bounds);
        if (e.Index < this.Items.Count) {
            Rectangle rc = new Rectangle(e.Bounds.Left, e.Bounds.Top, e.Bounds.Height, e.Bounds.Height);
            ControlPaint.DrawRadioButton(e.Graphics, rc,
                e.Index == SelectedOption ? ButtonState.Checked : ButtonState.Normal);
            rc = new Rectangle(rc.Right, e.Bounds.Top, e.Bounds.Width - rc.Right, e.Bounds.Height);
            TextRenderer.DrawText(e.Graphics, this.Items[e.Index].ToString(), this.Font, rc, this.ForeColor, TextFormatFlags.Left);
        }
        if ((e.State & DrawItemState.Focus) != DrawItemState.None) e.DrawFocusRectangle();
    }
    protected override void OnMouseUp(MouseEventArgs e) {
        // Detect clicks on the radio button
        int index = this.IndexFromPoint(e.Location);
        if (index >= 0 && e.X < this.ItemHeight) SelectedOption = index;
        base.OnMouseUp(e);
    }
    protected override void OnKeyDown(KeyEventArgs e) {
        // Turn on option with space bar
        if (e.KeyData == Keys.Space && this.SelectedIndex >= 0) SelectedOption = this.SelectedIndex;
        base.OnKeyDown(e);
    }
    private int mSelectedOption;
}
Hans Passant
I would have thought it was a common enough problem for there to be solutions for it. Oh well. Thanks for the code; it is indeed a good starting point.
JChristian
Nah, good UI design demands never asking the user to pick a selection from more than, say, 7 options. Once you need to scroll the available options you've passed the easy-to-use threshold and you should start categorizing them. It is a bit different for a CheckedListBox because deciding that things belong to a set is always easier than being forced to pick the *only* option.
Hans Passant