|
Introduction
This is the .NET version of my previous MFC article, CRadioListBox: A ListBox with Radio Buttons. A couple of years ago, I discussed in a Visual C++ forum about a member's request to implement a custom ListBox control similar to MFC's CCheckListBox, but with radio buttons. Initially it appeared to be trivial, since the ListBox control's unique selection version complies with the requirements, but I have concluded that this control has some advantages:
- It is clearer that options are mutually exclusive with radio buttons.
- It is a good alternative to a group of radio buttons because you have to maintain just one control, less memory use.
- It inherits some useful features like scrolling, sorting, data binding and multi-column.
- It will be easier to change options dynamically, as shown in the demo application.
- It will be easier to manage selection events, also shown in the demo application.
Using the Code
To implement RadioListBox into your project, you just need to do a few steps:
- Include RadioListBox.cs into your project.
- Drop a
RadioListBox object into your form.
- Change the standard properties of the control, just like a
ListBox.
- Countersense to standard
ListBox, transparent BackColor property is allowed.
That's all! Now you can use the radio button collection as a regular ListBox. You can add items with the Items.Add() method and query for user selection with the SelectedIndex property.
Fake Transparency
Some .NET controls accept a transparent color as a BackColor property, but ListBox is not one of them. So, transparency requires lots of non-managed tricks. However, transparency is a key feature needed for this control to be useful. It allows the control to acquire a real radio button look and feel, as you can see in the screenshot above. I decided to stay in the managed world by providing fake transparency to the control by overriding the BackColor property to accept it, and saving its own background color brush. When setting the background color to transparent, the control will mimic the parent form or control, even if the form has a non-standard background color.
RadioListBox Internals
The RadioListBox class is derived from Windows Forms' ListBox class with the owner-draw feature. The resumed class definition is the following:
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms.VisualStyles;
namespace System.Windows.Forms
{
public class RadioListBox : ListBox
{
private StringFormat Align;
private bool IsTransparent = false;
private Brush BackBrush;
public override Color BackColor ...
[Browsable(false)]
public override DrawMode DrawMode ...
[Browsable(false)]
public override SelectionMode SelectionMode ...
public RadioListBox() ...
protected override void OnDrawItem(DrawItemEventArgs e) ...
protected override void DefWndProc(ref Message m) ...
protected override void OnHandleCreated(EventArgs e) ...
protected override void OnFontChanged(EventArgs e) ...
protected override void OnParentChanged(EventArgs e) ...
protected override void OnParentBackColorChanged(EventArgs e) ...
}
}
The core enhancement is at the OnDrawItem() method. The method does not highlight the selected item as in a standard ListBox control, but draws a radio button instead. It also manages the focus state to draw the focus rectangle properly and the background color according to the transparency attribute. Here is the C# source code:
protected override void OnDrawItem(DrawItemEventArgs e)
{
int maxItem = this.Items.Count - 1;
if (e.Index < 0 || e.Index > maxItem)
{
e.Graphics.FillRectangle(BackBrush, this.ClientRectangle);
return;
}
int size = e.Font.Height;
Rectangle backRect = e.Bounds;
if (e.Index == maxItem)
backRect.Height = this.ClientRectangle.Top +
this.ClientRectangle.Height - e.Bounds.Top;
e.Graphics.FillRectangle(BackBrush, backRect);
Brush textBrush;
bool isChecked = (e.State & DrawItemState.Selected) == DrawItemState.Selected;
RadioButtonState state = isChecked ?
RadioButtonState.CheckedNormal : RadioButtonState.UncheckedNormal;
if ((e.State & DrawItemState.Disabled) == DrawItemState.Disabled)
{
textBrush = SystemBrushes.GrayText;
state = isChecked ? RadioButtonState.CheckedDisabled :
RadioButtonState.UncheckedDisabled;
}
else if ((e.State & DrawItemState.Grayed) == DrawItemState.Grayed)
{
textBrush = SystemBrushes.GrayText;
state = isChecked ? RadioButtonState.CheckedDisabled :
RadioButtonState.UncheckedDisabled;
}
else
{
textBrush = SystemBrushes.FromSystemColor(this.ForeColor);
}
Size glyphSize = RadioButtonRenderer.GetGlyphSize(e.Graphics, state);
Point glyphLocation = e.Bounds.Location;
glyphLocation.Y += (e.Bounds.Height - glyphSize.Height) / 2;
Rectangle bounds = new Rectangle(e.Bounds.X + glyphSize.Width, e.Bounds.Y,
e.Bounds.Width - glyphSize.Width, e.Bounds.Height);
RadioButtonRenderer.DrawRadioButton(e.Graphics, glyphLocation, state);
if (!string.IsNullOrEmpty(DisplayMember))
e.Graphics.DrawString(((System.Data.DataRowView)this.Items[e.Index])
[this.DisplayMember].ToString(),
e.Font, textBrush, bounds, this.Align);
else
e.Graphics.DrawString(this.Items[e.Index].ToString(),
e.Font, textBrush, bounds, this.Align);
e.DrawFocusRectangle();
}
History
- 23rd April, 2007: First version
- 14th September, 2007: Refinements in control rendering (thanks to stephpms); support for bounded data (thanks to PeterDP)
- 1st September, 2008: Improved background painting; support for large fonts (thanks to rkousha)
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 27 (Total in Forum: 27) (Refresh) | FirstPrevNext |
|
 |
|
|
Hi Please tell me how can i bind a generic list which has EntityState onject. I tried to bind it using radiobuttonlist1.DataSource = list;
But in radiobuttonlist only class name like that
MyProductExtensions.IM.ClientProxy.ImsServer.EntityState
If i use DisplayMember = "Text"; then it gives error that Cannot cast object MyProductExtensions.IM.ClientProxy.ImsServer.EntityState to System.Data.DataRowView
Please also tell how can i bind datavalue to radiobuttonlist.
public class EntityState { private string _text; public string Text { get { return _text; } set { _text = value; } } private int _value; public int Value { get { return _value; } set { _value = value; } } }
Regards Imran
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi, I think your best chance is not to use the DisplayMember, and implement a ToString() overriden method inside EntityState:
public class EntityState { private string _text;
public override string ToString() { return _text; }
// etcetera
Best regards, Jaime.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
 |
|
|
The requirement is, there are 6 radio buttons and a listbox. when i click on the radio buttons, some set of values gets populated into the listbox from the database, but when i click on 1 of the radio button among the six, the second item from the listbox must be removed. someone plese give me some idea and help me finish with the code.
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
|
 |
|
|
Hi Jamie,
In your implementation the bound for the radio button depends on the bounds for the list item. However the list item bound does not change with change in the font size. You have to set the ItemHeight the same as the FontHeight. I added the OnFontChange event handler to achieve this.
protected override void OnFontChanged(EventArgs e) { base.OnFontChanged(e); this.ItemHeight = FontHeight; }
Also the gap between the radio button and text can be set using the Glyph size from
RadioButtonRenderer::GetGlyphSize(e->Graphics,state).Width RadioButtonRenderer::GetGlyphSize(e->Graphics,state).Height
Using the font height is rather inappropriate since the glyph size is constant. If you know of a way to change the size of the radio button I like to know. Standard radio button stays the same size no matter what the font size is and there is no property for changing the button size.
Overall you've done a great job. Keep it up. Thanks.
Ramin
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Thanks for the information Ramin. In fact I have not tried to change font. I will review all concerning this and publish it in next update. Cheers, Jaime.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
If you bind a datatable to the control you only will see strings "System.Data.DataRowView" instead of the needed column items.
I suggest this:
bounds = new Rectangle(e.Bounds.X+size+2, e.Bounds.Y, e.Bounds.Width-size-2, e.Bounds.Height); if (this.DisplayMember != null) // Bound Datatable? Then show the column written in Displaymember e.Graphics.DrawString(((System.Data.DataRowView)this.Items[e.Index])[this.DisplayMember].ToString(), e.Font, textBrush, bounds, this.Align); else e.Graphics.DrawString(this.Items[e.Index].ToString(), e.Font, textBrush, bounds, this.Align);
instead of
bounds = new Rectangle(e.Bounds.X+size+2, e.Bounds.Y, e.Bounds.Width-size-2, e.Bounds.Height); e.Graphics.DrawString(this.Items[e.Index].ToString(), e.Font, textBrush, bounds, this.Align);
Best regards,
Peter
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I forgot: if you bind a dataset instead of a datatable you could split the displaymember by dot, and so on.
Regards,
Peter
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
As you use ControlPaint.DrawRadioButton to draw the radio button, the radioButtons doesn't have the good style on WindowsXP or Vista. You can use the RadioButtonRenderer class, and your radio buttons will be nice. The two static methods GetGlyphSize and DrawRadioButton of the RadioButtonRenderer class will be useful for your RadioListBox class.
|
| Sign In·View Thread·PermaLink | 4.20/5 (2 votes) |
|
|
|
 |
|
|
How did you changed the look of your sample windows form?
http://www.FocusOnPanama.com/ http://www.coralys.com/ http://flightsim.coralys.com/
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I am not sure what are you refering to. The screenshot was taken from my PC running Windows Vista. It will look different with Windows XP or other OSs. Cheers, Jaime.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
That is exactly whatI meant. I don't have Vista yet and probably won't for a long time.
http://www.FocusOnPanama.com/ http://www.coralys.com/ http://flightsim.coralys.com/
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Well, first of all I included the .vb into the program, and got some errors, but I can't figure out how to add the RadioListBox into my prodject, wich seem to be the way to solve the errors?
If I change the Build Action to Content for example I get no errors, but I still cant add it to my prodject, so I wonder how I add it.
Thanks
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I suggest to make the RadioListBox class public (C# version). It toook me quite some time to find out that for this reason it will not appear in the toolbox.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
I have used this list in my project. But since there are too many options in my list, a long tower is generated when I show this list. What I need is a flexible list which could support multi columns. The web control (RadioButtonList) provides this feature. I have tried myself to set multi columns but not yet succeeded. Regards Hamid
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Hamid, I have tested the multicolumn property without troubles. Even if you set MultiColumn to true and width is not enough, a horizontal scrollbar appears. Also you can control the ColumnWidth property. Greetings, Jaime.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
There is no source code for this, just go to the form/control designer, select the RadioListBox control you have included in your form, go to properties, and set MultiColumn property to true. Use the source code I have provided as a starting point.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|