How to get toolStripMenuItem from a Form variable? - c#

I want to access a toolStripMenuItem from a Form variable pass to a function.
public void foo(Form frm)
{
frm.controls["toolstripitem1"].checked= true;
}
I know that toolStripMenuItem is not control. my question is how to access this?
"Sorry for my bad english."

To access the MenuStripItem iterate over the MenuStrip control Items and check the name matches desired item, this is a simple example with 1 level of items, see below for a recursive example with nested menus, to iterate over 1st level only items:
static void CheckMenuItem(Form form)
{
var menu = form.Controls["menuStrip1"] as MenuStrip;
foreach (ToolStripMenuItem item in menu.Items)
{
if (item.Name == "toolStripMenuItem1")
{
item.Checked = true;
}
}
}
Recursive example for nested Menu to check every item:
static void CheckMenuItem(Form form)
{
var menu = form.Controls["menuStrip1"] as MenuStrip;
foreach (ToolStripMenuItem item in menu.Items)
{
if (item.Name == "toolStripMenuItem3")
{
item.Checked = true;
}
else if (item.HasDropDownItems)
{
CheckMenuItemRecursively(item);
}
}
}
static void CheckMenuItemRecursively(ToolStripMenuItem menuItem)
{
foreach (ToolStripItem subItem in menuItem.DropDown.Items)
{
if (subItem is ToolStripMenuItem)
{
var subMenuItem = subItem as ToolStripMenuItem;
if (subItem.Name == "toolStripMenuItem3")
{
subMenuItem.Checked = true;
}
else if (subMenuItem.HasDropDownItems)
{
CheckMenuItemRecursively(subMenuItem);
}
}
}
}
Arguably the recursive method could be adjusted to also account for a simple 1 level Menu but it would be more complicated.

Related

Remove the line below the selected item in a listbox

When I add an item into a listbox I also add a new line because I want there to be a blank line between each item added. When I remove a selected item I also want to remove the blank line I added otherwise I will end up getting 2 blank lines between each item this is the problem I am having so I thought if I could delete the selected item as well as the blank line above and below the selected item this would work. Is there a better approach to this?
ListBox1.Items.Remove(ListBox1.SelectedItem);
I have typed the items and differentiate what is the blank item and what is the value item. At the time of deleting I have the reference of both. It worked fine, see if it helps.
Here's an example:
Form:
private void Form1_Load(object sender, EventArgs e)
{
Data data = new Data { description = "Test1" };
listBox1.Items.Add(data);
data.BlankLine = new BlankItem();
listBox1.Items.Add(data.BlankLine);
data = new Data { description = "Test2" };
listBox1.Items.Add(data);
data.BlankLine = new BlankItem();
listBox1.Items.Add(data.BlankLine);
data = new Data { description = "Test3" };
listBox1.Items.Add(data);
data.BlankLine = new BlankItem();
listBox1.Items.Add(data.BlankLine);
data = new Data { description = "Test4" };
listBox1.Items.Add(data);
data.BlankLine = new BlankItem();
listBox1.Items.Add(data.BlankLine);
}
Event to delete the item on click:
private void listBox1_Click(object sender, EventArgs e)
{
if((listBox1.SelectedItem != null && listBox1.SelectedItem.GetType() != typeof(BlankItem)))
{
Data item = (Data)listBox1.SelectedItem;
listBox1.Items.Remove(item);
listBox1.Items.Remove(item.BlankLine);
}
}
Object Data
public class Data
{
public string description { get; set; }
public BlankItem BlankLine { get; set; }
public override string ToString()
{
return description;
}
}
Object BlankItem
public class BlankItem
{
public override string ToString()
{
return Environment.NewLine;
}
}
I wanted to try to implement the above functionality, but using a data-bound Listbox such that I make changes to the underlying list instead of the Listbox. If possible, use BindingList<T> instead of List<T> because it implements additional functionality specific to data binding.
The core is still the same, as each item added must also be followed by adding a string.Empty item. The same for removal, when an item is removed
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
var list = new BindingList<string>();
list.Add("ABC");
list.Add(string.Empty);
list.Add("GHK");
list.Add(string.Empty);
list.Add("OPQ");
listBox1.DataSource = list;
var binding = listBox1.BindingContext[list] as CurrencyManager;
listBox1.KeyDown += (s, ev) =>
{
if (ev.KeyData == Keys.Delete)
{
if (listBox1.SelectedItem != null && !listBox1.SelectedItem.Equals(string.Empty))
{
int index = listBox1.SelectedIndex;
if (index >= 0)
{
list.RemoveAt(index);
if (index < list.Count && list[index].Equals(string.Empty))
{
list.RemoveAt(index);
}
binding.Refresh();
}
}
}
if (ev.KeyData == Keys.Insert)
{
int index = listBox1.SelectedIndex;
if (index==-1 || list[index] == string.Empty)
{
index++;
}
list.Insert(index, "NEW " + (index + 1).ToString());
list.Insert(index+1, string.Empty);
}
};
}
}
press the [DEL] key to remove an item, and the [INS] key to add an item.
But I am not happy with this solution. I think there is a way to create a class that implements IListSource that you directly add/remove items and it creates a list with blanks in between automatically for binding.

Find Radio Group in Nested Controls in c# windows form

I wrote this recursive function to find name of radio button in each tab page:
private static DevExpress.XtraEditors.RadioGroup FindRadioGroupInTabPage(System.Windows.Forms.Control parentControl)
{
if (!parentControl.HasChildren)
{
return null;
}
foreach (var ct in parentControl.Controls.OfType<System.Windows.Forms.Control>())
{
if (ct is DevExpress.XtraEditors.RadioGroup rdg)
{
return rdg;
}
else
{
FindRadioGroupInTabPage(ct);
return null;
}
}
return null;
}
But it always returns Null. What's wrong with my code?
Use the following and in RadioButtonList change from RadioButton to DevExpress.XtraEditors.RadioGroup.
public static class ControlExtensions
{
public static IEnumerable<T> Descendants<T>(this Control control) where T : class
{
foreach (Control child in control.Controls)
{
if (child is T thisControl)
{
yield return (T)thisControl;
}
if (child.HasChildren)
{
foreach (T descendant in Descendants<T>(child))
{
yield return descendant;
}
}
}
}
public static List<RadioButton> RadioButtonList(this Control control)
=> control.Descendants<RadioButton>().ToList();
}
Usage where this is the current form
List<RadioButton> radioButtons = this.RadioButtonList();
Can be used on say a panel
List<RadioButton> radioButtonInPanel = panel1.RadioButtonList();

How to update ToolStripStatusLabel Text from a method passing all the controls on the form

I have a method to reset some controls on my form. I'm able to get the method to work except for the ToolStripStatusLabel named "tsStatusLabelInfo". This control is not passed to the resetForm() method.
I believe that it is part of the StatusStrip control but I haven't figure out how to get access to the ToolStripStatusLabel control to update the text.
private void resetButton_Click(object sender, EventArgs e)
{
Utilities.resetForm(this);
}
public static void resetForm(Control form)
{
foreach(Control c in GetOffSprings(form))
{
if (c.Name == "folderTextBox")
{
((TextBox)c).Clear();
}
else if (c.Name == "mfrListTextBox")
{
((RichTextBox)c).Clear();
}
else if (c.Name == "mfrListDataGridView")
{
((DataGridView)c).DataSource = null;
}
else if (c.Name == "onlyRadioButton")
{
((RadioButton)c).Checked = true;
}
else if (c.Name == "usRadioButton")
{
((RadioButton)c).Checked = true;
}
else if (c.Name == "otherYearsCheckedListBox")
{
((CheckedListBox)c).SetItemCheckState(0, CheckState.Unchecked);
((CheckedListBox)c).SetItemCheckState(1, CheckState.Unchecked);
}
else if (c.Name == "yearComboBox")
{
((ComboBox)c).Text = string.Empty;
}
else if (c.Name == "tsStatusLabelInfo")
{
//Control never pass
}
else if (c.Name == "statusStrip1")
{
// Exception:Object reference not set to an instance of an object
((StatusStrip)c).Controls["tsStatusLabelInfo"].Text = string.Empty;
}
}
}
//Loop through the control recursively getting all child controls
public static IEnumerable<Control> GetOffSprings(this Control #this)
{
foreach(Control child in #this.Controls)
{
yield return child;
//MessageBox.Show(child.Name);
foreach (var offspring in GetOffSprings(child))
{
yield return offspring;
}
}
}
It is because of this: https://msdn.microsoft.com/en-us/library/system.windows.forms.toolstrip.controls(v=vs.110).aspx
The Controls collection is not used on the ToolStrip and ToolStripContainer. You should use Items instead: https://msdn.microsoft.com/en-us/library/system.windows.forms.toolstrip.items(v=vs.110).aspx
The items on the tool strip are not Control objects. They are ToolStripItem objects. Looping recursively through the Controls collection will not give you access to these items.

Creating a trouble shooting web page with a series of ?s in asp.net. Directed Graph

I've been working on my first web page which will be used as a trouble shooting guide based on a series of questions. The questions are determined by the answer of the previous question, so it turns into a "choose your own adventure". Luckily I was given a decision flow chart showing all the possible paths, but it looks like the Tokyo subway map. I first created a series of panels, each containing the question and a dropdown list control containing the answer as the text and the id of the panel it should preceed. Next I created a class to organize all the panels into a graph structure. With this class I'm able to know the order of which panels are selected, which panels have not been selected, and is easy to make changes to the decision flow.
Below is an example of how I am using the class in the pageload event and below that is the actual class.
Since I'm not really familiar with asp.net, I'm just wondering if there are better ways of tackling this problem and if there are flaws within my code. My code is working, but I'm sure there could be improvements.
protected void Page_Load(object sender, EventArgs e)
{
DecisionGraph g = new DecisionGraph(this);
// Pass is root panel
g.BuildGraph("pnl1");
// Refreshes graph and returns an obj containing 2 iterable objects
DecisionGraphEnumeration dgEnum = g.RefreshGraph();
// Clear panel which will hold active panels
pnlMASTER.Controls.Clear();
IEnumerator<Panel> iEnumOnPath = dgEnum.GetOnPathPanelEnumerator();
while (iEnumOnPath.MoveNext())
{
Panel p = (Panel)iEnumOnPath.Current;
p.Visible = true;
pnlMASTER.Controls.Add(p);
}
IEnumerator<Panel> iEnumOffPath = dgEnum.GetOffPathPanelEnumerator();
while (iEnumOffPath.MoveNext())
{
iEnumOffPath.Current.Visible = false;
}
}
Here is the class
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Collections;
using System.Collections.Generic;
public class DecisionGraph
{
const String NULL_POINTER_NAME = "null";
private Node root;
private Page webPage;
//constructor
public DecisionGraph(Page pg)
{
this.webPage = pg;
this.root = null;
}
// method creates a graph structure recursively starting
// with the parameter as root
public void BuildGraph(String pnlName)
{
// return the reference of the supplied panel
Panel p = GetPanel(pnlName);
// create root
root = new Node(p, GetDropDownList(p));
// add children nodes to root
insert(root);
return;
}
// adds all child nodes to passed node
private void insert(Node n)
{
// if Panel has a DropDownList
if (n.dropDownList != null)// not leaf
{
// create an array of Nodes to hold references to children
n.children = new Node[n.dropDownList.Items.Count];
int i = 0;
foreach (ListItem it in n.dropDownList.Items)
{
Panel childPanel = GetPanel(it.Value);
if (childPanel == null)
{
// Panel does not exit
n.children[i] = null;
}
else
{
Node childNode = GetNode(childPanel);
if (childNode == null)// Node doesn't exist in structure for chilePanel
{
// create child node
childNode = new Node(childPanel, GetDropDownList(childPanel));
// add child node to arryay of children
n.children[i] = childNode;
// add the childs nodes of childNode to childNode
insert(n.children[i]);
}
else
{
// node had already been created, just add reference
n.children[i] = childNode;
}
}
i++;
}
}
return;
}
// returns an iterator of all active panels set as visible
// and sets all other panels as hidden
public DecisionGraphEnumeration RefreshGraph()
{
LinkedList<Panel> pathedPanels = new LinkedList<Panel>();
LinkedList<Panel> nonPathedPanels = new LinkedList<Panel>();
FindCurrentPath(root, pathedPanels);
HideNonPathedNodes(root, nonPathedPanels);
UnMarkAllNodes(root);
return new DecisionGraphEnumeration(pathedPanels.GetEnumerator(), nonPathedPanels.GetEnumerator());
}
// marks each node as valid and saves reference inorder
private void FindCurrentPath(Node n, LinkedList<Panel> list)
{
if (n == null)
return;
n.visitedFlag = true;
list.AddLast(n.panel);
if (n.dropDownList == null)
return;
int index = n.dropDownList.SelectedIndex;
FindCurrentPath(n.children[index],list);
return;
}
// finds all non pathed nodes and saves reference in no order
private void HideNonPathedNodes(Node n, LinkedList<Panel> list)
{
if (n == null)
return;
if (!n.visitedFlag)
{
n.ResetDropDownList();
// add panel if not already added.
if (!list.Contains(n.panel))
list.AddLast(n.panel);
}
if(n.children == null)
return;
foreach (Node childNode in n.children)
HideNonPathedNodes(childNode, list);
}
// unmarks n and sets all children of n as unmarked
private void UnMarkAllNodes(Node n)
{
if (n == null)
return;
n.visitedFlag = false;
if (n.children == null)
{
return;
}
else
{
foreach (Node childNode in n.children)
UnMarkAllNodes(childNode);
}
}
// returns node if a node already exists for p in structure, else returns null
private Node GetNode(Panel p)
{
Node n = getNode(root, p);
return n;
}
// recursive method call for GetNode
private Node getNode(Node n, Panel p)
{
if (n == null || p == null)
return null;
if (n.panel.Equals(p))
{
return n;
}
else if (n.children != null)
{
Node x = null;
int i = 0;
while (x == null && i < n.children.Length)
{
x = getNode(n.children[i], p);
i++;
}
return x;
}
else
{
return null;
}
}
// returns a DropDownList from p
private DropDownList GetDropDownList(Panel p)
{
foreach (Control ctrl in p.Controls)
{
if (ctrl is DropDownList)
{
return (DropDownList) ctrl;
}
}
return null;
}
// returns a panel from webpage of contructor using the FindControl method
private Panel GetPanel(String panelName)
{
if(panelName.Equals(NULL_POINTER_NAME))
return null;
Control ctrl = webPage.FindControl(panelName);
if (ctrl is Panel)
{
return (Panel)ctrl;
}
else
{
throw new ArgumentException(String.Format("{0} is not a Panel inside page {1}",panelName,webPage.Title.ToString()));
}
}
private class Node
{
public Panel panel;
public DropDownList dropDownList;
public Node[] children;
public bool pathedNode;
public bool visitedFlag;
#region Constructors
public Node() : this(null, null) { }
public Node(Panel p) : this(p, null) { }
public Node(Panel pnl, DropDownList d)
{
this.panel = pnl;
this.dropDownList = d;
this.children = null;
this.pathedNode = false;
this.visitedFlag = false;
}
#endregion
public void ResetDropDownList()
{
if (dropDownList == null)
return;
else
dropDownList.SelectedIndex = 0;
}
}
}
public class DecisionGraphEnumeration
{
private IEnumerator<Panel> onPathPanels;
private IEnumerator<Panel> offPathPanels;
internal DecisionGraphEnumeration(IEnumerator<Panel> active, IEnumerator<Panel> nonActive)
{
onPathPanels = active;
offPathPanels = nonActive;
}
public IEnumerator<Panel> GetOnPathPanelEnumerator()
{
return onPathPanels;
}
public IEnumerator<Panel> GetOffPathPanelEnumerator()
{
return offPathPanels;
}
}
Not gone through your code but based on the idea of what you want to do I think you might want to investigate using the WizardControl http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.wizard.aspx which allows for highly customisable Wizards to be created
As a first step you could use the ActiveStepChanged event to decide which Step to go to depending on what the user answers in each step.
I strongly recommend against referencing any instance of System.Web.Page from within your DecisionGraph class. It doesn't look like you're using the reference for anything right now and you'll just be tempted to use it unnecesarrily for as long as you leave it in there.
It seems like you are building the entire graph for each page_load--are you navigating through the structure after postbacks?
Keep in mind that when you set a panel's visibility property to false you are not getting any output on the client side. However, if you set the CSS style attribute "display" to "none" then you will be rendering the entire graph to the client and can actually perform all of the navigation on the client-side with javascript.

Best way to databind a group of radiobuttons in WinForms

I'm currently working on databinding some of my existing Windows Forms, and I've ran into an issue figuring out the proper way of databinding a group of radiobutton controls within a group box.
My business object has an integer property which I want to databind against 4 radiobuttons (where each of them represents the values 0 - 3).
I'm currently binding against a presenter object which works as the binder between the form and the business object, and the way I've done it now is to have 4 separate properties which each binds against each of these values (I do use INotifyPropertyChanged, but not including that here):
Private int _propValue;
Public bool PropIsValue0
{
get { return _propValue == 0; }
set
{
if (value)
_propValue = 0;
}
}
Public bool PropIsValue1 { // As above, but with value == 1 }
Public bool PropIsValue2 { // As above, but with value == 2 }
Public bool PropIsValue3 { // As above, but with value == 3 }
And I then bind each of the radiobuttons to their respective property as above.
This does not seem right to me, so any advice are highly appreciated.
Following is a generic RadioGroupBox implementation in the spirit of ArielBH's suggestion (some code borrowed from Jay Andrew Allen's RadioPanel). Just add RadioButtons to it, set their tags to different integers and bind to the 'Selected' property.
public class RadioGroupBox : GroupBox
{
public event EventHandler SelectedChanged = delegate { };
int _selected;
public int Selected
{
get
{
return _selected;
}
set
{
int val = 0;
var radioButton = this.Controls.OfType<RadioButton>()
.FirstOrDefault(radio =>
radio.Tag != null
&& int.TryParse(radio.Tag.ToString(), out val) && val == value);
if (radioButton != null)
{
radioButton.Checked = true;
_selected = val;
}
}
}
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
var radioButton = e.Control as RadioButton;
if (radioButton != null)
radioButton.CheckedChanged += radioButton_CheckedChanged;
}
void radioButton_CheckedChanged(object sender, EventArgs e)
{
var radio = (RadioButton)sender;
int val = 0;
if (radio.Checked && radio.Tag != null
&& int.TryParse(radio.Tag.ToString(), out val))
{
_selected = val;
SelectedChanged(this, new EventArgs());
}
}
}
Note that you can't bind to the 'Selected' property via the designer due to initialization order problems in InitializeComponent (the binding is performed before the radio buttons are initialized, so their tag is null in the first assignment). So just bind yourself like so:
public Form1()
{
InitializeComponent();
//Assuming selected1 and selected2 are defined as integer application settings
radioGroup1.DataBindings.Add("Selected", Properties.Settings.Default, "selected1");
radioGroup2.DataBindings.Add("Selected", Properties.Settings.Default, "selected2");
}
I know this post is old but in my search for an answer for this same problem I came across this post and it didn't solve my problem. I ended up having a lightbulb go off randomly just a minute ago and wanted to share my solution.
I have three radio buttons in a group box. I'm using a List<> of a custom class object as the data source.
Class Object:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BAL
{
class ProductItem
{
// Global Variable to store the value of which radio button should be checked
private int glbTaxStatus;
// Public variable to set initial value passed from
// database query and get value to save to database
public int TaxStatus
{
get { return glbTaxStatus; }
set { glbTaxStatus = value; }
}
// Get/Set for 1st Radio button
public bool Resale
{
// If the Global Variable = 1 return true, else return false
get
{
if (glbTaxStatus.Equals(1))
{
return true;
}
else
{
return false;
}
}
// If the value being passed in = 1 set the Global Variable = 1, else do nothing
set
{
if (value.Equals(true))
{
glbTaxStatus = 1;
}
}
}
// Get/Set for 2nd Radio button
public bool NeverTax
{
// If the Global Variable = 2 return true, else return false
get
{
if (glbTaxStatus.Equals(2))
{
return true;
}
else
{
return false;
}
}
// If the value being passed in = 2 set the Global Variable = 2, else do nothing
set
{
if (value.Equals(true))
{
glbTaxStatus = 2;
}
}
}
// Get/Set for 3rd Radio button
public bool AlwaysTax
{
// If the Global Variable = 3 return true, else return false
get
{
if (glbTaxStatus.Equals(3))
{
return true;
}
else
{
return false;
}
}
// If the value being passed in = 3 set the Global Variable = 3, else do nothing
set
{
if (value.Equals(true))
{
glbTaxStatus = 3;
}
}
}
// More code ...
Three seperate public variables with get/set accessing the same one global variable.
In the code behind, I have a function called during the Page_Load() setting all the controls databindings. For each radio button I add its own databinging.
radResale.DataBindings.Add("Checked", glbProductList, "Resale", true, DataSourceUpdateMode.OnPropertyChanged, false);
radNeverTax.DataBindings.Add("Checked", glbProductList, "NeverTax", true, DataSourceUpdateMode.OnPropertyChanged, false);
radAlwaysTax.DataBindings.Add("Checked", glbProductList, "Always", true, DataSourceUpdateMode.OnPropertyChanged, false);
I hope this helps someone!!
I think I would use my own GroupBox. I would bind the CustomGroupBox to your Model and set the correct RadioButton (using the tag or name properties) from the binded value.
This is my approach for binding a list of radio buttons to an enum.
Using the Enum as a string in the button's Tag property, I use the Binding.Format and Binding.Parse event to decide which button should be checked.
public enum OptionEnum
{
Option1 = 0,
Option2
}
OptionEnum _rbEnum = OptionEnum.Option1;
OptionEnum PropertyRBEnum
{
get { return _rbEnum; }
set
{
_rbEnum = value;
RaisePropertyChanged("PropertyRBEnum");
}
}
public static void FormatSelectedEnum<T>(object sender, ConvertEventArgs args) where T : struct
{
Binding binding = (sender as Binding);
if (binding == null) return;
Control button = binding.Control;
if (button == null || args.DesiredType != typeof(Boolean)) return;
T value = (T)args.Value;
T controlValue;
if (Enum.TryParse(button.Tag.ToString(), out controlValue))
{
args.Value = value.Equals(controlValue);
}
else
{
Exception ex = new Exception("String not found in Enum");
ex.Data.Add("Tag", button.Tag);
throw ex;
}
}
public static void ParseSelectedEnum<T>(object sender, ConvertEventArgs args) where T : struct
{
Binding binding = (sender as Binding);
if (binding == null) return;
Control button = binding.Control;
bool value = (bool)args.Value;
if (button == null || value != true) return;
T controlValue;
if (Enum.TryParse(button.Tag.ToString(), out controlValue))
{
args.Value = controlValue;
}
else
{
Exception ex = new Exception("String not found in Enum");
ex.Data.Add("Tag", button.Tag);
throw ex;
}
}
Then setup your data binding like this:
radioButton1.Tag = "Option1";
radioButton2.Tag = "Option2";
foreach (RadioButtonUx rb in new RadioButtonUx[] { radioButton1, radioButton2 })
{
Binding b = new Binding("Checked", this, "PropertyRBEnum");
b.Format += FormatSelectedRadioButton<OptionEnum>;
b.Parse += ParseSelectedRadioButton<OptionEnum>;
rb.DataBindings.Add(b);
}
I started to resolve the same problematic.
I used a RadioButtonBinding class which encapsulates all radiobuttons about an enum in the data source.
This following class keeps all radio buttons in a list and makes a lookup for the enum :
class RadioButtonBinding : ILookup<System.Enum, System.Windows.Forms.RadioButton>
{
private Type enumType;
private List<System.Windows.Forms.RadioButton> radioButtons;
private System.Windows.Forms.BindingSource bindingSource;
private string propertyName;
public RadioButtonBinding(Type myEnum, System.Windows.Forms.BindingSource bs, string propertyName)
{
this.enumType = myEnum;
this.radioButtons = new List<System.Windows.Forms.RadioButton>();
foreach (string name in System.Enum.GetNames(this.enumType))
{
System.Windows.Forms.RadioButton rb = new System.Windows.Forms.RadioButton();
rb.Text = name;
this.radioButtons.Add(rb);
rb.CheckedChanged += new EventHandler(rb_CheckedChanged);
}
this.bindingSource = bs;
this.propertyName = propertyName;
this.bindingSource.DataSourceChanged += new EventHandler(bindingSource_DataSourceChanged);
}
void bindingSource_DataSourceChanged(object sender, EventArgs e)
{
object obj = this.bindingSource.Current;
System.Enum item = obj.GetType().GetProperty(propertyName).GetValue(obj, new object[] { }) as System.Enum;
foreach (System.Enum value in System.Enum.GetValues(this.enumType))
{
if (this.Contains(value))
{
System.Windows.Forms.RadioButton rb = this[value].First();
if (value.Equals(item))
{
rb.Checked = true;
}
else
{
rb.Checked = false;
}
}
}
}
void rb_CheckedChanged(object sender, EventArgs e)
{
System.Windows.Forms.RadioButton rb = sender as System.Windows.Forms.RadioButton;
System.Enum val = null;
try
{
val = System.Enum.Parse(this.enumType, rb.Text) as System.Enum;
}
catch(Exception ex)
{
// cannot occurred if code is safe
System.Windows.Forms.MessageBox.Show("No enum value for this radio button : " + ex.ToString());
}
object obj = this.bindingSource.Current;
obj.GetType().GetProperty(propertyName).SetValue(obj, val, new object[] { });
this.bindingSource.CurrencyManager.Refresh();
}
public int Count
{
get
{
return System.Enum.GetNames(this.enumType).Count();
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.radioButtons.GetEnumerator();
}
public bool Contains(Enum key)
{
return System.Enum.GetNames(this.enumType).Contains(key.ToString());
}
public IEnumerable<System.Windows.Forms.RadioButton> this[Enum key]
{
get
{
return this.radioButtons.FindAll(a => { return a.Text == key.ToString(); });
}
}
IEnumerator<IGrouping<Enum, System.Windows.Forms.RadioButton>> IEnumerable<IGrouping<Enum, System.Windows.Forms.RadioButton>>.GetEnumerator()
{
throw new NotImplementedException();
}
public void AddControlsIntoGroupBox(System.Windows.Forms.GroupBox gb)
{
System.Windows.Forms.FlowLayoutPanel panel = new System.Windows.Forms.FlowLayoutPanel();
panel.Dock = System.Windows.Forms.DockStyle.Fill;
panel.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft;
foreach (System.Windows.Forms.RadioButton rb in this.radioButtons)
{
panel.Controls.Add(rb);
}
gb.Controls.Add(panel);
}
}
You are using the class into a form by adding that code in the constructor of the form:
public PageView()
{
InitializeComponent();
RadioButtonBinding rbWidth = new RadioButtonBinding(typeof(Library.EnumConstraint), this.pageBindingSource, "ConstraintWidth");
rbWidth.AddControlsIntoGroupBox(this.groupBox1);
RadioButtonBinding rbHeight = new RadioButtonBinding(typeof(Library.EnumConstraint), this.pageBindingSource, "ConstraintHeight");
rbHeight.AddControlsIntoGroupBox(this.groupBox3);
this.pageBindingSource.CurrentItemChanged += new EventHandler(pageBindingSource_CurrentItemChanged);
}
Set the tag name of your radio buttons to something that represents the value.
Create a string setting, for example, OptionDuplicateFiles, and give it the default value of the tag name for your default radio button.
To save your checked radio button:
Settings.Default.OptionDuplicateFiles = gbxDuplicateFiles.Controls
.OfType<RadioButton>()
.Where(b => b.Checked)
.Select(b => b.Tag)
.First()
.ToString();
To load your checked radio button:
(gbxDuplicateFiles.Controls
.OfType<RadioButton>()
.Where(b => b.Tag.ToString() == Settings.Default.OptionDuplicateFiles)
.First())
.Checked = true;
Tada!
I liked the idea of a RadioButtonGroupBox but I decided to create a version that is self supporting.
There is no reason to add value to Tag attribute or to introduce new value attributes.
Any assigned radio button is still a member of the RadioButtonGroupBox and the sequence of radiobuttons is defined during development.
Soo, I modified the code.
Now I can get and set the selected radiobutton by index position, By Control Name and by Text.
BTW Text is only useable if your asssigned Text is different for each radiobutton.
public class RadioButtonGroupBox : GroupBox
{
public event EventHandler SelectedChanged = delegate { };
int _nIndexPosCheckRadioButton = -1;
int _selected;
public int Selected
{
get
{
return _selected;
}
}
public int CheckedRadioButtonIndexPos
{
set
{
int nPosInList = -1;
foreach (RadioButton item in this.Controls.OfType<RadioButton>())
{
// There are RadioButtonItems in the list...
nPosInList++;
// Set the RB that should be checked
if (nPosInList == value)
{
item.Checked = true;
// We can stop with the loop
break;
}
}
_nIndexPosCheckRadioButton = nPosInList;
}
get
{
int nPosInList = -1;
int nPosCheckeItemInList = -1;
foreach (RadioButton item in this.Controls.OfType<RadioButton>())
{
// There are RadioButtonItems in the list...
nPosInList++;
// Find the RB that is checked
if (item.Checked)
{
nPosCheckeItemInList = nPosInList;
// We can stop with the loop
break;
}
}
_nIndexPosCheckRadioButton = nPosCheckeItemInList;
return _nIndexPosCheckRadioButton;
}
}
public string CheckedRadioButtonByText
{
set
{
int nPosInList = -1;
foreach (RadioButton item in this.Controls.OfType<RadioButton>())
{
// There are RadioButtonItems in the list...
nPosInList++;
// Set the RB that should be checked
if (item.Text == value)
{
item.Checked = true;
// We can stop with the loop
break;
}
}
_nIndexPosCheckRadioButton = nPosInList;
}
get
{
string cByTextValue = "__UNDEFINED__";
int nPosInList = -1;
int nPosCheckeItemInList = -1;
foreach (RadioButton item in this.Controls.OfType<RadioButton>())
{
// There are RadioButtonItems in the list...
nPosInList++;
// Find the RB that is checked
if (item.Checked)
{
cByTextValue = item.Text;
nPosCheckeItemInList = nPosInList;
// We can stop with the loop
break;
}
}
_nIndexPosCheckRadioButton = nPosCheckeItemInList;
return cByTextValue;
}
}
public string CheckedRadioButtonByName
{
set
{
int nPosInList = -1;
foreach (RadioButton item in this.Controls.OfType<RadioButton>())
{
// There are RadioButtonItems in the list...
nPosInList++;
// Set the RB that should be checked
if (item.Name == value)
{
item.Checked = true;
// We can stop with the loop
break;
}
}
_nIndexPosCheckRadioButton = nPosInList;
}
get
{
String cByNameValue = "__UNDEFINED__";
int nPosInList = -1;
int nPosCheckeItemInList = -1;
foreach (RadioButton item in this.Controls.OfType<RadioButton>())
{
// There are RadioButtonItems in the list...
nPosInList++;
// Find the RB that is checked
if (item.Checked)
{
cByNameValue = item.Name;
nPosCheckeItemInList = nPosInList;
// We can stop with the loop
break;
}
}
_nIndexPosCheckRadioButton = nPosCheckeItemInList;
return cByNameValue;
}
}
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
var radioButton = e.Control as RadioButton;
if (radioButton != null)
radioButton.CheckedChanged += radioButton_CheckedChanged;
}
void radioButton_CheckedChanged(object sender, EventArgs e)
{
_selected = CheckedRadioButtonIndexPos;
SelectedChanged(this, new EventArgs());
}
}
My approach is to put each radio button into its own panel before binding them to a boolean property:
public static Binding Bind<TObject>(this RadioButton control, object dataSource, string dataMember)
{
// Put the radio button into its own panel
Panel panel = new Panel();
control.Parent.Controls.Add(panel);
panel.Location = control.Location;
panel.Size = control.Size;
panel.Controls.Add(control);
control.Location = new Point(0, 0);
// Do the actual data binding
return control.DataBindings.Add("Checked", dataSource, dataMember);
}
I would like to make an observation about the code block that might be helpful to people reading these posts. The following code may not always work as expected due to it's structure.
try
{
val = System.Enum.Parse(this.enumType, rb.Text) as System.Enum;
}
catch(Exception ex)
{
// cannot occurred if code is safe
System.Windows.Forms.MessageBox.Show("No enum value for this radio button : " + ex.ToString());
}
object obj = this.bindingSource.Current;
obj.GetType().GetProperty(propertyName).SetValue(obj, val, new object[] {
}
);
this.bindingSource.CurrencyManager.Refresh();
If an error occurs in the try block, the catch block will be executed. The code will continue to execute after the catch block. Since there was no handling of binding source, the variables following the catch could end up in a indeterminate state and may throw another exception that may or may not be handled.
A better approach is as follows
try
{
val = System.Enum.Parse(this.enumType, rb.Text) as System.Enum;
object obj = this.bindingSource.Current;
obj.GetType().GetProperty(propertyName).SetValue(obj, val, new object[] { });
this.bindingSource.CurrencyManager.Refresh();
}
catch(EntityException ex)
{
// handle error
}
catch(Exception ex)
{
// cannot occurred if code is safe
System.Windows.Forms.MessageBox.Show("No enum value for this radio button : " + ex.ToString());
}
This allows the enum value error to be handled as well as other errors that may occur. However use the EntityException or variations of it before the Exception block (all decendents of Exception have to come first). One can get specific entity state information for a entity framework error by using the entity framework classes instead of the Exception base class. This can be helpful for debugging or providing clearer run time messages for the user.
When I setup try-catch blocks I like to view it as a "layer" on top of the code. I make decisions about the flow of the exceptions through out the program, their display to the user, and what ever cleanup is required to allow the program to continue working properly without objects in a indeterminate state that can cascade to other errors.
I liked Jan Hoogma GroupBox binding but preferred more control over values you could bind to started by creating RadioButtonBIndable which can have values of different types assigned to it which are strongly typed.
public class RadioButtonBindable : RadioButton
{
public Int32 ValueInt { get; set; }
public Decimal ValueDecimal { get; set; }
public String ValueString { get; set; }
public Boolean ValueBoolean { get; set; }
}
Then I can work on the GroupBox and create GroupBoxBindable that can be bound to any of the values in the RadioButtonBindable (you could bind to multiple values e.g. ValueInt = 23 and ValueString = "Example Text"
You can also set a default value in case of teh unlikeley event where there is no RadioButton selected.
public class RadioButtonGroupBoxBindable : GroupBox, INotifyPropertyChanged
{
//public event EventHandler SelectedChanged = delegate { };
public event EventHandler ValueIntChanged = delegate { };
public event EventHandler ValueDecimalChanged = delegate { };
public event EventHandler ValueStringChanged = delegate { };
public event EventHandler ValueBooleanChanged = delegate { };
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public Int32 DefaultValueInt { get; set; }
public Decimal DefaultValueDecimal { get; set; }
public String DefaultValueString { get; set; }
public Boolean DefaultValueBoolean { get; set; }
public Boolean ValueBoolean
{
set
{
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.ValueBoolean == value)
{
item.Checked = true;
break;
}
}
}
}
get
{
Boolean retVal = DefaultValueBoolean;
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.Checked)
{
retVal = item.ValueBoolean;
break;
}
}
}
return retVal;
}
}
public int ValueInt
{
set
{
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.ValueInt == value)
{
item.Checked = true;
break;
}
}
}
}
get
{
int retVal = DefaultValueInt;
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.Checked)
{
retVal = item.ValueInt;
break;
}
}
}
return retVal;
}
}
public decimal ValueDecimal
{
set
{
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.ValueDecimal == value)
{
item.Checked = true;
break;
}
}
}
}
get
{
decimal retVal = DefaultValueDecimal;
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.Checked)
{
retVal = item.ValueDecimal;
break;
}
}
}
return retVal;
}
}
public string ValueString
{
set
{
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.ValueString == value)
{
item.Checked = true;
break;
}
}
}
}
get
{
string retVal = DefaultValueString;
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.Checked)
{
retVal = item.ValueString;
break;
}
}
}
return retVal;
}
}
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
var radioButton = e.Control as RadioButtonBindable;
if (radioButton != null)
radioButton.CheckedChanged += radioButton_CheckedChanged;
}
void radioButton_CheckedChanged(object sender, EventArgs e)
{
if (((RadioButtonBindable)sender).Checked)
{
OnPropertyChanged("ValueInt");
OnPropertyChanged("ValueDecimal");
OnPropertyChanged("ValueString");
OnPropertyChanged("ValueBoolean");
ValueIntChanged(this, new EventArgs());
ValueDecimalChanged(this, new EventArgs());
ValueStringChanged(this, new EventArgs());
ValueBooleanChanged(this, new EventArgs());
}
}
}

Categories