I have one requirement to add three check box in a group box, and check-box must react as radio button i.e.,
1. one can check only one check-box at a time.
2. when user goes to check another check box, previous one automatically should be unchecked.
I also know that this is not a good approach to do this, and it's really confusing to non-technical or technical user but I also want to learn something new in this.
I went through lots of sites, but I didn't get any satisfactory solution. I hope I will get good one from you.
Moreover, There are simple three check box to indicate three things for selection respectively and one text box. If one checks anyone things then texbox shows the selected thing.
For example:- Check-box show C#,ASP.NET and MVC respectively.
If one select C# then text-box must show C#.
Thanks...
With Thanks and Regards,
Ravindra.
You can use radio button for this functionality.
I'd do it like this, assuming you use winforms:
public partial class Form1 : Form
{
private bool ignoreEvents = false;
public Form1()
{
InitializeComponent();
//set this handler to the events of all 3 checkboxes
checkBox1.CheckedChanged += radioCheckboxes_CheckedChanged;
checkBox2.CheckedChanged += radioCheckboxes_CheckedChanged;
checkBox3.CheckedChanged += radioCheckboxes_CheckedChanged;
}
private void radioCheckboxes_CheckedChanged(object sender, EventArgs e)
{
if (!ignoreEvents)
{
ignoreEvents = true; // otherwise the other checkboxes would react when i set the state programmatically
CheckBox current = sender as CheckBox;
if (current == checkBox1)
{
checkBox2.Checked = false;
checkBox3.Checked = false;
}
else if (current == checkBox2)
{
checkBox1.Checked = false;
checkBox3.Checked = false;
}
else
{
checkBox1.Checked = false;
checkBox2.Checked = false;
}
ignoreEvents = false;
}
}
}
With WPF I'd just use a style for a radiobutton so that it looks like a checkbox...
you can simply use another task like timer that updates every 50ms ,
so as to disable the check boxes you want to disable there ,
in your "checked changed event" only flag the controls ,
like for checkbox1 = flagdcheckbx = 1 ; and so on.
private async Task writeTimer()
{
if(ignoreEvents == true)
{
ignoreEvents = false;
switch(flagdcheckbx)
{
case 1:
checkBox2.Checked = false;
checkBox3.Checked = false;
break;
case 2:
checkBox1.Checked = false;
checkBox3.Checked = false;
break;
case 3:
checkBox1.Checked = false;
checkBox2.Checked = false;
break;
}
}
}
private void radioCheckboxes_CheckedChanged(object sender, EventArgs e)
{
CheckBox current = sender as CheckBox;
if (current == checkBox1)
{
if (current.checked)
{
ignoreEvents = true;
flagdcheckbx=1;
}
}
else if (current == checkBox2)
{
if (current.checked) // it should happen only when checked
{
ignoreEvents = true;
flagdcheckbx=2;
}
}
else
{
if (current.checked)
{
ignoreEvents = true;
flagdcheckbx=3;
}
}
}
I assume you know how to group RadioButton controls inside a container and you just look for the CheckBox appearance for your radio button groups.
As an option you can modify the RadioButtonList that I've shared in [this post] and add the CheckBox appearance to it. In the following picture you can see two RadioButtonList control, one with CheckBox appearance and the other with RadioButton appearance:
radioButtonList1.DataSource = Enum.GetValues(typeof(DayOfWeek));
radioButtonList1.Appearance = RadioButtonListAppearance.CheckBox;
radioButtonList2.DataSource = Enum.GetValues(typeof(DayOfWeek));
Here is the modified version of code to add Appearance property:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
public class RadioButtonList : ListBox
{
RadioButtonListAppearance appearance = RadioButtonListAppearance.RadioButton;
public RadioButtonListAppearance Appearance
{
get { return appearance; }
set { appearance = value; Invalidate(); }
}
Size s;
public RadioButtonList()
{
this.DrawMode = DrawMode.OwnerDrawFixed;
using (var g = Graphics.FromHwnd(IntPtr.Zero))
s = RadioButtonRenderer.GetGlyphSize(
Graphics.FromHwnd(IntPtr.Zero), RadioButtonState.CheckedNormal);
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
var text = (Items.Count > 0) ? GetItemText(Items[e.Index]) : Name;
Rectangle r = e.Bounds; Point p;
var flags = TextFormatFlags.Default | TextFormatFlags.NoPrefix;
var selected = (e.State & DrawItemState.Selected) == DrawItemState.Selected;
var state = selected ?
(Enabled ? RadioButtonState.CheckedNormal :
RadioButtonState.CheckedDisabled) :
(Enabled ? RadioButtonState.UncheckedNormal :
RadioButtonState.UncheckedDisabled);
if (RightToLeft == System.Windows.Forms.RightToLeft.Yes)
{
p = new Point(r.Right - r.Height + (ItemHeight - s.Width) / 2,
r.Top + (ItemHeight - s.Height) / 2);
r = new Rectangle(r.Left, r.Top, r.Width - r.Height, r.Height);
flags |= TextFormatFlags.RightToLeft | TextFormatFlags.Right;
}
else
{
p = new Point(r.Left + (ItemHeight - s.Width) / 2,
r.Top + (ItemHeight - s.Height) / 2);
r = new Rectangle(r.Left + r.Height, r.Top, r.Width - r.Height, r.Height);
}
var bc = selected ? (Enabled ? SystemColors.Highlight :
SystemColors.InactiveBorder) : BackColor;
var fc = selected ? (Enabled ? SystemColors.HighlightText :
SystemColors.GrayText) : ForeColor;
using (var b = new SolidBrush(bc))
e.Graphics.FillRectangle(b, e.Bounds);
if (appearance == RadioButtonListAppearance.RadioButton)
{
RadioButtonRenderer.DrawRadioButton(e.Graphics, p, state);
}
else if (appearance == RadioButtonListAppearance.CheckBox)
{
CheckBoxRenderer.DrawCheckBox(e.Graphics, p, (CheckBoxState)state);
}
else
{
throw new InvalidOperationException("Appearance is invalid.");
}
TextRenderer.DrawText(e.Graphics, text, Font, r, fc, bc, flags);
e.DrawFocusRectangle();
base.OnDrawItem(e);
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override SelectionMode SelectionMode
{
get { return System.Windows.Forms.SelectionMode.One; }
set { }
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override int ItemHeight
{
get { return (this.Font.Height + 2); }
set { }
}
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override DrawMode DrawMode
{
get { return base.DrawMode; }
set { base.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; }
}
}
public enum RadioButtonListAppearance
{
RadioButton,
CheckBox
}
Assuming you have a few CheckBox controls in a container like a GroupBox, In the load event of your form:
Find all CheckBox controls of the container
Set AutoCheck property of the CheckBox controls to false
Handle Click event of the CheckBox controls and in the event handler, set Checked property of clicked element to true and for rest, set it to false
Here is the code:
private void Form1_Load(object sender, EventArgs e)
{
var all = groupBox1.Controls.OfType<CheckBox>().ToList();
foreach (CheckBox c in all)
{
c.AutoCheck = false;
c.Click += (obj, args) =>
{
var ThisCheckBox = (CheckBox)obj;
foreach( CheckBox cc in all.Except(new[] { ThisCheckBox }))
cc.Checked = false;
ThisCheckBox.Checked = true;
};
}
}
Related
Created a custom intellisense textbox (textbox with listbox a child).
As shown in below image, the listbox pops up when i enter a char which all works fine and good but when i am at the end of textbox the listbox is partially visible, is there anyway i can show the whole listbox content?
Tried this "Show control inside user control outside the boundaries of its parent
But when the popup window opens the text box looses focus and i cannot type anything further, my intellisense textbox keeps giving better results based on what they type but in this situation i am not able to type anymore.
FYI tried to add pParentControl.Focus() into show method defined in other article as shown below, missing something?
public void Show(Control pParentControl)
{
if (pParentControl == null) return;
// position the popup window
var loc = pParentControl.PointToScreen(new Point(0, pParentControl.Height));
pParentControl.Focus();
m_tsdd.Show(loc);
}
Here is the complete code
class TextBox_AutoComplete : TextBox
{
#region Class Members
List<string> dictionary;
ListBox listbox = new ListBox();
#endregion
private PopupHelper m_popup;
#region Extern functions
[DllImport("user32")]
private extern static int GetCaretPos(out Point p);
#endregion
#region Constructors
public TextBox_AutoComplete() : base()
{
this.Margin = new Padding(0, 0, 0, 0);
this.Multiline = true;
this.Dock = DockStyle.Fill;
this.KeyDown += Textbox_KeyDown;
this.KeyUp += Textbox_KeyUp;
listbox.Parent = this;
listbox.KeyUp += List_OnKeyUp;
listbox.Visible = false;
this.dictionary = new List<string>();
}
#endregion
#region Properties
public List<string> Dictionary
{
get { return this.dictionary; }
set { this.dictionary = value; }
}
#endregion
#region Methods
private static string GetLastString(string s)
{
Regex rgx = new Regex("[^a-zA-Z0-9_.\\[\\]]");
s = rgx.Replace(s, " ");
string[] strArray = s.Split(' ');
return strArray[strArray.Length - 1];
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
Point cp;
GetCaretPos(out cp);
List<string> lstTemp = new List<string>();
List<string> TempFilteredList = new List<string>();
string LastString = GetLastString(this.Text.Substring(0, SelectionStart));
//MessageBox.Show(LastString);
/*seperated them so that column name matches are found first*/
TempFilteredList.AddRange(dictionary.Where(n => n.Replace("[", "").ToUpper().Substring(n.IndexOf(".") > 0 ? n.IndexOf(".") : 0).StartsWith(LastString.ToUpper())
).Select(r => r)
.ToList());
TempFilteredList.AddRange(dictionary.Where(n => n.Replace("[", "").ToUpper().StartsWith(LastString.ToUpper())
|| n.ToUpper().StartsWith(LastString.ToUpper()))
.Select(r => r)
.ToList());
lstTemp = TempFilteredList.Distinct().Select(r => r).ToList();
/*Getting max width*/
int maxWidth = 0, temp = 0;
foreach (var obj in lstTemp)
{
temp = TextRenderer.MeasureText(obj.ToString(), new Font("Arial", 10, FontStyle.Regular)).Width;
if (temp > maxWidth)
{
maxWidth = temp;
}
}
listbox.SetBounds(cp.X + 20, cp.Y + 20, maxWidth, 60);
if (lstTemp.Count != 0 && LastString != "")
{
listbox.DataSource = lstTemp;
// listbox.Show();
if (m_popup == null)
m_popup = new PopupHelper(listbox);
m_popup.Show(this);
}
else if (m_popup != null)
{
//listbox.Hide();
m_popup.Hide();
}
}
protected void Textbox_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Down)
{
if (listbox.Visible == true)
{
listbox.Focus();
}
e.Handled = true;
}
else if (e.KeyCode == Keys.Escape)
{
listbox.Visible = false;
e.Handled = true;
}
}
protected void Textbox_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space && listbox.Visible == true)
{
listbox.Focus();
List_OnKeyUp(listbox, new KeyEventArgs(Keys.Space));
e.Handled = true;
}
if (e.KeyCode == Keys.Down && listbox.Visible == true)
{
listbox.Focus();
e.Handled = true;
}
}
private void List_OnKeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space || e.KeyCode == Keys.Enter)
{
int Selection_Start = this.SelectionStart;
string StrLS = GetLastString(this.Text.Substring(0, Selection_Start));
this.Select(Selection_Start - StrLS.Length, StrLS.Length);
// MessageBox.Show(this.Selection_Start.ToString() + " Last string" + StrLS);
this.SelectedText=((ListBox)sender).SelectedItem.ToString();
listbox.Hide();
this.Focus();
}
}
#endregion
}
public sealed class PopupHelper : IDisposable
{
private readonly Control m_control;
private readonly ToolStripDropDown m_tsdd;
private readonly Panel m_hostPanel; // workarround - some controls don't display correctly if they are hosted directly in ToolStripControlHost
public PopupHelper(Control pControl)
{
m_hostPanel = new Panel();
m_hostPanel.Padding = Padding.Empty;
m_hostPanel.Margin = Padding.Empty;
m_hostPanel.TabStop = false;
m_hostPanel.BorderStyle = BorderStyle.None;
m_hostPanel.BackColor = Color.Transparent;
m_tsdd = new ToolStripDropDown();
m_tsdd.CausesValidation = false;
m_tsdd.Padding = Padding.Empty;
m_tsdd.Margin = Padding.Empty;
m_tsdd.Opacity = 0.9;
m_control = pControl;
m_control.CausesValidation = false;
m_control.Resize += MControlResize;
//m_hostPanel.Controls.Add(m_control);
m_tsdd.Padding = Padding.Empty;
m_tsdd.Margin = Padding.Empty;
m_tsdd.MinimumSize = m_tsdd.MaximumSize = m_tsdd.Size = pControl.Size;
m_tsdd.Items.Add(new ToolStripControlHost(m_control));
}
private void ResizeWindow()
{
m_tsdd.MinimumSize = m_tsdd.MaximumSize = m_tsdd.Size = m_control.Size;
m_hostPanel.MinimumSize = m_hostPanel.MaximumSize = m_hostPanel.Size = m_control.Size;
}
private void MControlResize(object sender, EventArgs e)
{
ResizeWindow();
}
/// <summary>
/// Display the popup and keep the focus
/// </summary>
/// <param name="pParentControl"></param>
public void Show(Control pParentControl)
{
if (pParentControl == null) return;
// position the popup window
var loc = pParentControl.PointToScreen(new Point(0, pParentControl.Height));
pParentControl.Focus();
m_tsdd.Show(loc);
}
public void Hide()
{
m_tsdd.Hide();
}
public void Close()
{
m_tsdd.Close();
}
public void Dispose()
{
m_control.Resize -= MControlResize;
m_tsdd.Dispose();
m_hostPanel.Dispose();
}
}
Firstly, I personally don't see any benefit in having a control inside another. Yes, the child control is locked inside its parent's boundaries automatically for you, but this benefit is negated by the issue that you're facing, and solving that issue requires the same work as when the two controls have no relation. In both cases, you'll have to do the calculations manually to keep the child visible inside its parent. In the second case the parent is the app's window.
Secondly, I don't recommend using hacks like the one mentioned in the comments to show the child outside its parent's boundaries. The hack creates more issues than it solves, as you found out. And what's the point of that hack anyway? If you want to show the child outside the parent, then don't make it a child control in the first place, and you don't need any hack.
The best solution is the one that you find in any well designed app, and in Windows itself. Open any app, let's say Notepad, and right-click near the upper-left corner. You'll see the context menu pulling to lower-right direction. Now right-click near the other three corners and you'll see the context menu pulling in different direction each time, so it will always be visible inside the app. Now if you resize the app window too small and right-click, the context menu will choose the best direction but some of it will be outside the app because the window is too small. That's why you need your list not to be a child, but it's up to you, and it's only about these edge cases. The solution will be similar in both cases.
You're displaying the list in this line:
listbox.SetBounds(cp.X + 20, cp.Y + 20, maxWidth, 60);
The key is cp.X and cp.Y. This is what decides where the list will appear. You need to make this point dynamic and responsive to the boundaries of the parent. You fixed the width to maxWidth and height to 60, so I will use those values in the calculation.
To make sure the list will not go beyond the bottom:
var y = this.Height < cp.Y + 60 ? this.Height - 60 : cp.Y;
To make sure the list will not go beyond the right:
var x = this.Width < cp.X + maxWidth ? this.Width - maxWidth : cp.X;
Now you can show your list at the calculated point:
listbox.SetBounds(x, y, maxWidth, 60);
Notes:
I didn't include the 20 gap that you used. I think it looks better without the gap and I haven't seen any app that has a gap. If you prefer the gap, add it to the calculation of x and y. Don't add it in the SetBounds() or that will screw up the calculation.
The calculation above doesn't take into account when the parent size is too small to show the child inside. If you want to support that edge case, you need to make the child a separate control and add some checks to the calculation.
I'm working on a utility that helps me construct long, repetitive HTML tables. I have data imported from an XML file, a listBox that shows the names, buttons that insert text into a richTextBox that contains certain data from the XML, but there's info I have to enter manually for each name.
I have a treeView that lists committees ( parent ) and subcommittees ( 1 level of children ). I can check the appropriate boxes, click a button, and all the corresponding HTML is created. However, each committee ( and sub ) has a chairperson and a ranking member. Instead of adding those two extra nodes under each parent and child - I have 71 parents and children. I'd rather not add 142 more nodes - I'm hoping there's a way to have "four-click" checkboxes...? 1st click = checkmark; 2nd = green; 3rd = red; 4th = cleared. Or similar. That way I could "check" for being a member, "double-check" for Chair, "triple-check" for Ranking, and a fourth would just start over.
I'm open to suggestions for another approach, as well. This is the last bit I need to get working to save me typing 2-3K lines of HTML by hand, so I don't care how I get it done. Thanks.
Okay, sorry it's been a while. You know, life and such. Anyway, I ended up my own TreeNode and TreeView classes. This is my first adventure with WinForms, so a lot of this seemed quite finicky. Ended up be much simpler than I had imagined. If anybody wants to use this, knock yourself out.
The Check*.bmp images are just a checked box that I colored in. I'll probably make my own four images for this and neaten up the populateStateImageList() method. Also, I would think this would work for as many states as you need, up to the 14 image limit of StateImageList.
using System.Drawing;
using System.Windows.Forms;
namespace jkHTMLBuilder
{
public class QuadCheckTreeNode : TreeNode
{
public enum CheckState : int { UnChecked, Checked, Chair, Rank } // The four possible states
private CheckState _cs = CheckState.UnChecked; // The node's current state
public CheckState checkState
{
get
{
return _cs;
}
set
{
_cs = value;
}
}
public QuadCheckTreeNode(string initString) : base()
{
this.Text = initString;
this.checkState = CheckState.UnChecked;
this.Checked = false;
this.StateImageIndex = 0;
}
public void checkAdvance() // This is called from onAfterCheck to set the next consecutive state
{
switch (checkState)
{
case CheckState.UnChecked:
checkState = CheckState.Checked;
break;
case CheckState.Checked:
checkState = CheckState.Chair;
break;
case CheckState.Chair:
checkState = CheckState.Rank;
break;
case CheckState.Rank:
checkState = CheckState.UnChecked;
break;
}
this.Checked = (this.checkState == CheckState.UnChecked ? false : true);
}
}
class QuadCheckTreeView : TreeView
{
private bool clickStop = false;
private bool shouldAdvance = false;
public QuadCheckTreeView() : base()
{
StateImageList = new ImageList();
}
public void populateStateImageList() // I made this a separate method and call it from my Load_Senators method so the images are
{ // set up when the XML file is loaded. Apparently, loading the files ( Check*.bmmp )
for (int i = 0; i < 2; i++) // from the ctor causes problems...? Whatever. This works.
{
Bitmap bmp = new Bitmap(16, 16);
Graphics chkGraphics = Graphics.FromImage(bmp);
switch (i)
{
case 0:
CheckBoxRenderer.DrawCheckBox(chkGraphics, new Point(0, 1), System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal);
break;
case 1:
CheckBoxRenderer.DrawCheckBox(chkGraphics, new Point(0, 1), System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal);
break;
}
StateImageList.Images.Add(bmp);
}
Bitmap myBitmap = new Bitmap("..\\..\\CheckBlue.bmp");
StateImageList.Images.Add(myBitmap);
myBitmap = new Bitmap("..\\..\\CheckRed.bmp");
StateImageList.Images.Add(myBitmap);
}
public void ClearNodes(TreeNodeCollection nodes) // This is for when I move on to the next record. Unchecks everything.
{
clickStop = true;
foreach (QuadCheckTreeNode qctn in nodes)
{
qctn.Checked = false;
qctn.checkState = QuadCheckTreeNode.CheckState.UnChecked;
if (qctn.Nodes.Count > 0)
{
foreach (QuadCheckTreeNode cqctn in qctn.Nodes)
{
cqctn.Checked = false;
cqctn.checkState = QuadCheckTreeNode.CheckState.UnChecked;
}
}
}
clickStop = false;
}
protected override void OnCreateControl()
{
base.OnCreateControl();
CheckBoxes = false; // Checkboxes off to use my images
ClearNodes(this.Nodes); // Probably not needed.
}
protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
{
base.OnNodeMouseClick(e);
TreeViewHitTestInfo tvhtInfo = HitTest(e.X, e.Y); // If you didn't click on it, ignore.
if (tvhtInfo == null || tvhtInfo.Location !=
TreeViewHitTestLocations.StateImage)
{
return;
}
QuadCheckTreeNode qctn = (QuadCheckTreeNode)e.Node; // If you right-clicked, set to UnChecked
if (e.Button == MouseButtons.Right)
{
if (qctn.checkState != QuadCheckTreeNode.CheckState.UnChecked)
{
qctn.checkState = QuadCheckTreeNode.CheckState.UnChecked;
qctn.Checked = false;
shouldAdvance = false;
}
}
else
shouldAdvance = true; // Left click sets this var==true so the node's Advance() method is called
qctn.Checked = qctn.Checked; // This fires the onAfterCheck event
}
protected override void OnAfterCheck(TreeViewEventArgs e)
{
base.OnAfterCheck(e);
if (clickStop) // This keeps the event from running if it's called inappropriately
{
return;
}
clickStop = true;
QuadCheckTreeNode qctn = (QuadCheckTreeNode)e.Node;
if (shouldAdvance)
{
qctn.checkAdvance();
shouldAdvance = false;
qctn.StateImageIndex = (int)qctn.checkState;
}
checkParent(qctn); // Calling this method, if it actually checks a parent node, won't call onAfterCheck because of clickStop
clickStop = false;
}
protected void checkParent(QuadCheckTreeNode qctn)
{
QuadCheckTreeNode pqctn = (QuadCheckTreeNode)qctn.Parent;
if (pqctn == null)
return;
if (pqctn.checkState == QuadCheckTreeNode.CheckState.UnChecked) // This checks a parent if it has a checked child
{
bool chkParent = false;
foreach (QuadCheckTreeNode n in pqctn.Nodes)
{
if (n.checkState != QuadCheckTreeNode.CheckState.UnChecked) // Checks all the children. If even one is checked, the parent gets checked
chkParent = true;
}
if (chkParent)
{
pqctn.Checked = true;
pqctn.checkState = QuadCheckTreeNode.CheckState.Checked;
}
}
}
}
}
I have a form on which are "listBox1" and "button1". I have two function. The second function adds checkboxes to listbox1 and the first function displays message box. But I don´t know how to write the first function.
Here I want to check which checkbox is checked and write a message:
private void button1_click(object sender, EventArgs e)
{
MessageBox.Show("radiobutton: " + rb[i].Text);
}
Here I create checkboxes: //it´s working
internal void loadSurveys()
{
WebClient client2 = new WebClient();
var json = client2.DownloadString("http://www.test.net/api/surveys/?api_key=123");
JObject data = JObject.Parse(json);
var example = JsonConvert.DeserializeObject<Example>(json);
int y = 5;
int i = 0;
RadioButton[] rb = new RadioButton[example.surveys.Length];
String chkBox_name = "";
String chkBox_text = "";
foreach (var survey in data["surveys"].Children())
{
rb[i] = new RadioButton();
rb[i].Location = new Point(5, y);
rb[i].Name = chkBox_name + survey["id"];
rb[i].Text = chkBox_text + survey["title"];
rb[i].AutoSize = true;
this.listBox1.Controls.Add(rb[i]);
y += 20;
i++;
}
}
You can go through listBox1.Controls and pick checked RadioButton
private void button1_click(object sender, EventArgs e)
{
var rb = this.listBox1.Controls.OfType<RadioButton>().SingleOrDefault(n => n.Checked);
if (rb != null)
MessageBox.Show("radiobutton: " + rb.Text);
}
since this is RadioButton there shouldn't be more then one checked
The first step, is to make the radiobutton array a variable on form level:
RadioButton[] rb
which is assigned inside loadSurveys
rb = new RadioButton[example.surveys.Length];
Then the array is accessible inside your button click
var rb = rb.FirstOrDefault(r=>r.Checked);
if(rb==null)
MessageBox.Show("No radiobutton was selected");
else
MessageBox.Show("radiobutton: " + rb[i].Text);
edit Just noticed you add the radiobuttons to a listbox. Is the listbox1 variable an actual listbox? The above will still work, but if the goal is to display a listbox of radiobuttons, you can custom paint the listbox and else use a normal panel instead of a listbox.
Either way, you can also do a firstordefault on the controls on the controls of the listbox1 variable (with OfType), but if you'd use a listbox, and fill its items, you could simply use SelectedIndexChanged
edit 2
Since I already had it, wanted to show a way to make your listbox a radiobutton box. You can make any existing listbox a radiobutton box with the following class:
public class RadioButtonBoxPainter:IDisposable
{
public readonly ListBox ListBox;
public RadioButtonBoxPainter(ListBox ListBox)
{
this.ListBox = ListBox;
ListBox.DrawMode = DrawMode.OwnerDrawFixed;
ListBox.DrawItem += ListBox_DrawItem;
}
void ListBox_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index == -1) return;
Rectangle r = e.Bounds;
r.Width=r.Height;
bool selected= (e.State & DrawItemState.Selected) > 0;
e.DrawBackground();
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
ControlPaint.DrawRadioButton(e.Graphics, r, selected ? ButtonState.Checked : ButtonState.Normal);
r.X = r.Right + 2;
r.Width = e.Bounds.Width - r.X;
string txt;
if (ListBox.Site != null && ListBox.Site.DesignMode && e.Index >= ListBox.Items.Count)
txt = ListBox.Name;
else
txt = ListBox.GetItemText(ListBox.Items[e.Index]);
using (var b = new SolidBrush(e.ForeColor))
e.Graphics.DrawString(txt, e.Font, b, r);
if (selected)
{
r = e.Bounds;
r.Width--; r.Height--;
e.Graphics.DrawRectangle(Pens.DarkBlue, r);
}
}
public void Dispose()
{
ListBox.DrawItem -= ListBox_DrawItem;
}
}
Example of a standard implementation:
public class RadioButtonBox:ListBox
{
public readonly RadioButtonBoxPainter Painter;
public RadioButtonBox()
{
Painter = new RadioButtonBoxPainter(this);
}
[DefaultValue(DrawMode.OwnerDrawFixed)]
public override DrawMode DrawMode
{
get{return base.DrawMode;}
set{base.DrawMode = value;}
}
}
The RadioButtonBox is a control I actually use a lot. Personally I find it a lot quicker in implementing then a load of separate radiobuttons.
In case you want to use it, and want an example how to implement it in your current code, leave a comment and I'll add one.
I'd like to have a toolstrip with some buttons on it (WinFroms/c#/.net4). I'd like to have the clicked button be checked and all others unchecked, so I want to have only the clicked button checked, all the others unchecked.
I know toolstrip button has checkedonlclick property, but when I click one button, others may be also checked. In good old VB6 there was an automatic solution for this problem: buttongroup on toolbar. Is there any similar in Winfoms?
Or should I handle it from code?! Switch all other buttons to unchecked state when one button is checked? If so, then it would not be my favourite solution...
Call this code on the toolStripButton_Click events and you should get the desired result.
foreach (ToolStripButton item in ((ToolStripButton)sender).GetCurrentParent().Items)
{
if (item == sender) item.Checked = true;
if ((item != null) && (item != sender))
{
item.Checked = false;
}
}
I guess this is an old question, however I was looking for a solution to this problem as well and ended up making a kind of ToolStripRadioButton by extending ToolStripButton. The behaviour should be the same as a normal radio button, as far as I can see. I have however added a group id to make it possible to have multiple radio button groups within the same toolstrip.
One can add the radio button to a toolstrip like a normal ToolStripButton:
To make the button stand more out when checked I have given it a gradient background (CheckedColor1 to CheckedColor2 from top to bottom):
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
public class ToolStripRadioButton : ToolStripButton
{
private int radioButtonGroupId = 0;
private bool updateButtonGroup = true;
private Color checkedColor1 = Color.FromArgb(71, 113, 179);
private Color checkedColor2 = Color.FromArgb(98, 139, 205);
public ToolStripRadioButton()
{
this.CheckOnClick = true;
}
[Category("Behavior")]
public int RadioButtonGroupId
{
get
{
return radioButtonGroupId;
}
set
{
radioButtonGroupId = value;
// Make sure no two radio buttons are checked at the same time
UpdateGroup();
}
}
[Category("Appearance")]
public Color CheckedColor1
{
get { return checkedColor1; }
set { checkedColor1 = value; }
}
[Category("Appearance")]
public Color CheckedColor2
{
get { return checkedColor2; }
set { checkedColor2 = value; }
}
// Set check value without updating (disabling) other radio buttons in the group
private void SetCheckValue(bool checkValue)
{
updateButtonGroup = false;
this.Checked = checkValue;
updateButtonGroup = true;
}
// To make sure no two radio buttons are checked at the same time
private void UpdateGroup()
{
if (this.Parent != null)
{
// Get number of checked radio buttons in group
int checkedCount = this.Parent.Items.OfType<ToolStripRadioButton>().Count(x => x.RadioButtonGroupId == RadioButtonGroupId && x.Checked);
if (checkedCount > 1)
{
this.Checked = false;
}
}
}
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
this.Checked = true;
}
protected override void OnCheckedChanged(EventArgs e)
{
if (this.Parent != null && updateButtonGroup)
{
foreach (ToolStripRadioButton radioButton in this.Parent.Items.OfType<ToolStripRadioButton>())
{
// Disable all other radio buttons with same group id
if (radioButton != this && radioButton.RadioButtonGroupId == this.RadioButtonGroupId)
{
radioButton.SetCheckValue(false);
}
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
if (this.Checked)
{
var checkedBackgroundBrush = new LinearGradientBrush(new Point(0, 0), new Point(0, this.Height), CheckedColor1, CheckedColor2);
e.Graphics.FillRectangle(checkedBackgroundBrush, new Rectangle(new Point(0, 0), this.Size));
}
base.OnPaint(e);
}
}
Perhaps useful to others as well.
I'm not aware of a way to do this in the designer, but it's fairly simple to do in code:
readonly Dictionary<string, HashSet<ToolStripButton>> mButtonGroups;
...
ToolStripButton AddGroupedButton(string pText, string pGroupName) {
var newButton = new ToolStripButton(pText) {
CheckOnClick = true
};
mSomeToolStrip.Items.Add(newButton);
HashSet<ToolStripButton> buttonGroup;
if (!mButtonGroups.TryGetValue(pGroupName, out buttonGroup)) {
buttonGroup = new HashSet<ToolStripButton>();
mButtonGroups.Add(pGroupName, buttonGroup);
}
newButton.Click += (s, e) => {
foreach (var button in buttonGroup)
button.Checked = button == newButton;
};
return newButton;
}
I haven't done it in a little but if you use a group box around radio buttons it will only allow one to be checked at a time.
I wrote a custom Control (an auto-complete TextBox (below)) in which a ContextMenuStrip is programmatically added to the form.
My problem is that when the control generates a list longer than the height of it's parent container (Panel, GroupBox, etc) the bottom section of ContextMenuStrip is hidden.
I have tried calling .BringToFront() but can't find any way to overcome this behaviour.
Any help would be greatly appriciated, also feel free to steal the control :)
fig 1.
/// <summary>
/// TextBox which can auto complete words found in a table column
/// Just set DataSource and DataListField and start typing - WD
/// </summary>
public class AutoComplete : TextBox
{
public DataTable DataSource { get; set; }
public string DataListField { get; set; }
private ContextMenuStrip SuggestionList = new ContextMenuStrip();
public AutoComplete()
{
this.LostFocus += new EventHandler(AutoComplete_LostFocus);
KeyUp += new KeyEventHandler(AutoComplete_KeyUp);
SuggestionList.ItemClicked += new ToolStripItemClickedEventHandler(SuggestionList_ItemClicked);
}
void AutoComplete_LostFocus(object sender, EventArgs e)
{
if (!SuggestionList.Focused)
{
SuggestionList.Visible = false;
}
}
void SuggestionList_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
this.Text = e.ClickedItem.Text;
SuggestionList.Visible = false;
this.Focus();
SuggestionList.Visible = false;
}
void AutoComplete_KeyUp(object sender, KeyEventArgs e)
{
if (null != DataSource && DataSource.Rows.Count > 0 && null != DataListField)
{
if (e.KeyCode != Keys.Enter)
{
if (SuggestionList.Items.Count > 0 && e.KeyCode == Keys.Down)
{
SuggestionList.Focus();
SuggestionList.Items[0].Select();
SuggestionList.BringToFront();
}
else if (this.Text.Length > 0)
{
SuggestionList.Items.Clear();
DataRow[] drSuggestionList = DataSource.Select("[" + DataListField + "] LIKE '" + this.Text + "%'");
foreach (DataRow dr in drSuggestionList)
{
SuggestionList.Items.Add(dr[DataListField].ToString());
}
SuggestionList.TopLevel = false;
SuggestionList.Visible = true;
SuggestionList.Top = (this.Top + this.Height);
SuggestionList.Left = this.Left;
this.Parent.Controls.Add(SuggestionList);
SuggestionList.BringToFront();
}
}
}
}
}
It's because you turned it into a child control by setting its TopLevel property to false and adding it to the parent's Control collection. Replace this:
SuggestionList.TopLevel = false;
SuggestionList.Visible = true;
SuggestionList.Top = (this.Top + this.Height);
SuggestionList.Left = this.Left;
this.Parent.Controls.Add(SuggestionList);
SuggestionList.BringToFront();
with this:
SuggestionList.Show(this.Parent.PointToScreen(new Point(this.Left, this.Bottom)));
Beware that the CMS will overlap the text box if it is too tall.