i Have color picker in my game to customize character clothes by user in game.
how can i save this changes ?
i've try this way but doesn't work and i don't know why :
public Material[] ShirtColorMat;
Renderer renderer;
int PantSaver;
void Start()
{
renderer = this.GetComponent<Renderer>();
PantSaver = PlayerPrefs.GetInt("Saver");
}
public void _000000() // i use this on button
{
renderer.material = ShirtColorMat[0];
Save();
}
void Save()
{
PlayerPrefs.SetInt("Saver", PantSaver);
}
thanks
You are not updating the PantSaver value.
It should probably rather be
public void _000000()
{
PantSaver = 0;
renderer.material = ShirtColorMat[PantSaver];
Save();
}
void Save()
{
PlayerPrefs.SetInt("Saver", PantSaver);
}
In general a property might be of good use here:
private int _pantSaver;
private int PantSaver
{
get => _pantSaver;
set
{
_pantSaver = value;
PlayerPrefs.SetInt("Saver", value);
PlayerPrefs.Save();
renderer.material = ShirtColorMat[value];
}
}
This way everytime you assign a new value to PantSaver it is automatically saved and the renderer updated.
So then you would only need to do
public void _000000()
{
PantSaver = 0;
}
Finally please rename _000000 to something more meaningful like e.g. ResetPant or something similar.
You might in Save() want to say PlayerPrefs.SetInt("Saver", ShirtColorMat[0]); instead. What you have in "PantSaver" is not a reference but a value. This means that with the above code you are currently first getting the value "Saver" and then setting the same value again.
The PlayerPrefs.SetInt's first string parameter is the key you save it to and later can get it from.
I have a bug in my Unity project and it doesn't make any sense to me why, simply putting it, I have a script with a Stack, which represents an Inventory slot, when you add a item to it, it pushes it into the correct inventory slot, as seem in this GIF: https://imgur.com/a/wxDpvsG
In the left is the inventory, in the right is just a Box with random items. My problem started with trying to make a hoveover tooltip to remove items from the inventory. Here is the script of the slots.
public class InvSlots : MonoBehaviour , IPointerEnterHandler, IPointerExitHandler
{
public Stack<Item> items = new Stack<Item>();
public Text stackTxt;
public Image imgItem;
public GameObject ToolTip, Exclude;
public bool IsEmpty
{
get { if (items == null) { return true; } return items.Count == 0; }
}
public int CountItem()
{
return this.items.Count;
}
public Item CurrentItem
{
get { return items.Peek(); }
}
public bool isAvailable
{
get { return CurrentItem.maxnumber > items.Count; }
}
public void addItem(Item item)
{
items.Push(item);
changeSprite(Resources.Load<Sprite>("imgs/ItemIcons/" + item.iconPath));
if (items.Count >= 1)
{
stackTxt.text = items.Count.ToString();
}
}
private void changeSprite(Sprite sprite)
{
imgItem.sprite = sprite;
imgItem.color = new Vector4(1, 1, 1, 1);
}
public void OnPointerEnter(PointerEventData eventData)
{
Debug.Log("pointer enter " + items.Peek().nome);
if (items.Count > 0)
{
Instantiate(ToolTip, gameObject.transform.position, new Quaternion(), GameObject.Find("BGInv").transform);
GameObject.Find("ToolTipNome").GetComponent<TMPro.TextMeshProUGUI>().text = CurrentItem.nome;
GameObject.Find("ToolTipDesc").GetComponent<TMPro.TextMeshProUGUI>().text = CurrentItem.desc;
}
}
public void OnPointerExit(PointerEventData eventData)
{
Debug.Log("pointer exit" + items.Peek().nome);
if (items.Count > 0)
{
Destroy(GameObject.Find("HoverOver Tooltip 1(Clone)"));
}
}
public void OpenExclude()
{
Debug.Log("Open Exclude " + items.Peek().nome);
if (items.Count > 0)
{
Destroy(GameObject.Find("HoverOver Tooltip 1(Clone)"));
Instantiate(Exclude, gameObject.transform.position, new Quaternion(), GameObject.Find("BGInv").transform);
}
}
public void CloseExclude()
{
Debug.Log("Close exclude " + items.Peek().nome);
if (items.Count > 0)
{
Destroy(GameObject.FindGameObjectWithTag("ExcludeItems"));
}
}
public void ExcludeOneItem()
{
Debug.Log("Exclude 1 " + items.Peek().nome);
if (items.Count > 0)
{
items.Pop();
stackTxt.text = items.Count.ToString();
Destroy(GameObject.FindGameObjectWithTag("ExcludeItems"));
}
}
public void ExcludeAllItem()
{
Debug.Log("Exclude all " + items.Peek().nome);
if (items.Count > 0)
{
imgItem.color = new Vector4(1, 1, 1, 1);
items.Clear();
stackTxt.text = "";
Destroy(GameObject.FindGameObjectWithTag("ExcludeItems"));
}
}
}
So basically, I have that Stack of items, BUT whenever I try to access the Stack through the methods CloseExclude(), ExcludeOneItem() and ExcludeAllItem() the Stack is empty, but in every other method of the script the Stack have items like it should. I have no idea why something like that is happening, i've never seen anything like this. Just to keep it clear, this methods are being called from the OnClick of the buttons, but so is the OpenExclude() so I don't think that's it.
As you can see in the script, I have a bunch of Logs, Here is a picture of the console:
Console
And a GIF to help you guys see it more clearly:
https://imgur.com/a/KxEsOUl
TLDR I have a stack that is not working on certain methods of my script, but works fine in others.
Thanks to elgozo I managed to get it right, the prefab of the exclude menu was getting the wrong InvSlots Instance indeed! What I did was simply make a script to set the InvSlots that the prefab is using when it gets instantiated, so it doesn't mess it up.
I'm doing a multiple choice quiz game, and im tryinng to highlight the correct answer in green if the wrong answer is clicked. for example if i have A , B , C and D and "A" is the correct answer. i want if i choose "B" to turn B to red color and display the correct answer to green which is in this case "A". Right now i have if i click the right answer it displays green, as i want it. and if i press wrong answer it displays red. what im missing is when i click wrong answer i want to also highlight the correct answer.
Here's my functions:
In the Game controller:
public bool theAnswerIsCorrect;
public bool IsCorrected()
{
return theAnswerIsCorrect;
}
public void AnswerButtonClick(bool isCorrect)
{
if (isCorrect)
{
Debug.Log("I'm Correct");
theAnswerIsCorrect = true;
playerScore += currentRoundData.pointAddedForCorrectAnswer;
scoreDisplayText.text = "Score: " + playerScore.ToString();
}
else
theAnswerIsCorrect = false;
// Do we still have questions?
if (questionPool.Length > questionIndex + 1)
{
//questionIndex++;
UpdateQuestionIndex();
StartCoroutine(DelayTime(3));
// ShowQuestion();
}
else
{
EndRound();
}
}
And this is an a class that just holdes my data
public class answerData {
public string answerTxt;
public bool isCorrect;
}
This is being used in the ButtonClick function (the code in the begging of the question) -> this line
gameController.AnswerButtonClick (AnswerData.isCorrect);
And in the inspector i specify by the bool what is the correct answer
*** NEW SCRIPT HERE "Picking up the Correct button"
// store reference to btn text
public Text answerText;
private answerData AnswerData;
private GameController gameController;
public static AnswerButton _instace;
private void Awake()
{
_instace = this;
}
// Use this for initialization
void Start ()
{
gameController = FindObjectOfType<GameController> ();
}
public void Setup(answerData data)
{
AnswerData = data;
answerText.text = AnswerData.answerTxt;
}
IEnumerator ReturnButtonColor()
{
yield return new WaitForSeconds(2.9f);
GetComponent<Button>().image.color = Color.white;
Debug.Log("HiB");
}
public void HandleClick()
{
gameController.AnswerButtonClick (AnswerData.isCorrect);
if (gameController.IsCorrected())
{
GetComponent<Button>().image.color = Color.green;
Debug.Log("im true");
StartCoroutine(ReturnButtonColor());
}
else
{
GetComponent<Button>().image.color = Color.red;
// gameController.IsCorrected().image.color = Color.green;
StartCoroutine(ReturnButtonColor());
}
}
Thank you
I am assuming that all option button have same script attached.
Create one delegate and register this in your script which is attached to your option button.
Like this
Create delegate and event
#region DELEGATES
public delegate void OnQuestionOptionClicked ();
public static event OnQuestionOptionClicked onQuestionOptionClicked;
#endregion
#region DELEGATE_CALLS
private void RaiseOnQuestionOptionClicked ()
{
if (onQuestionOptionClicked != null)
onQuestionOptionClicked ();
}
#endregion
Register it
void OnEnable ()
{
onQuestionOptionClicked += OnQuestionOptionClicked;
}
void OnDisable ()
{
onQuestionOptionClicked -= OnQuestionOptionClicked;
}
#region DELEGATE_EVENT_LISTENER
void OnQuestionOptionClicked ()
{
GetComponent<Button>().interactable = false;
if (AnswerData.isCorrect){
GetComponent<Button>().image.color = Color.green;
Debug.Log("im true");
}
}
#endregion
Your setup method
public void Setup(answerData data)
{
AnswerData = data;
answerText.text = AnswerData.answerTxt;
GetComponent<Button>().interactable = true;
GetComponent<Button>().image.color = Color.white;
}
And on button click event
#region BUTTON_CLICK_LISTENER
public void ButtonClick()
{
gameController.AnswerButtonClick (AnswerData.isCorrect);
if (!gameController.IsCorrected())
{
GetComponent<Button>().image.color = Color.red;
Debug.Log("im true");
// StartCoroutine(ReturnButtonColor());
}
RaiseOnQuestionOptionClicked ();
}
#endregion
Hope this solution helps you.;)
Like using GetComponent<Button>().image.color = Color.green; in the if statement , you could simply add in /*Button A*/.image.color = Color.green; in the else statement.
What #BadWolf in the comments is saying is the correct answer. If you need more coding help you have to include more information. How do you determine the Correct Button? Does it have a special name in the Scene? Does the gameController know which button is the correct? It seems gameController knows which Button is the correct one so you should create a method which will look something like:
public Button GetCorrectButton(){
return theCorrectButton;
}
I don't know the code for how you see if it is the correct button but I'm guessing you have some way where you use the Correct Button. Find the Correct Button and return it in the method GetCorrectButton.
which you will then use in your code like:
public void ButtonClick()
{
gameController.AnswerButtonClick (AnswerData.isCorrect);
if (gameController.IsCorrected())
{
GetComponent<Button>().image.color = Color.green;
Debug.Log("im true");
// StartCoroutine(ReturnButtonColor());
}
else
{
GetComponent<Button>().image.color = Color.red;
// Like this:
gameController.GetCorrectButton().image.color = Color.green;
}
}
I can be more specific if I get some more information/code to see!
How can I create a Button (Not GUI etc) in Unity 4.6 from code? I need to be able to specify the action when tapped.
I tried adding a delegate or something to onClick but I can't get it to work.
http://docs.unity3d.com/ScriptReference/UI.Button-onClick.html
This is the code I have so far:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class ButtontitleAndAction {
public string Title;
public delegate void Del();
public ButtontitleAndAction(string title, Del theaction) {
this.Title = title;
this.action = theaction;
}
public void Call() {
Debug.Log("Call " + this.Title);
this.action();
}
private Del action;
}
public class MjalertScript : MonoBehaviour {
public Text Text;
public GameObject gameobjectButtons;
public GameObject gameobjectMjalertbutton;
// Use this for initialization
void Start() {
}
public void Addbuttons(ButtontitleAndAction[] buttontitles) {
// public void Addbuttons(string[] buttontitles) {
GridLayoutGroup grid = this.gameobjectButtons.GetComponent<GridLayoutGroup>();
float newwidth = 400 / buttontitles.Length;
grid.cellSize = new Vector2(newwidth, grid.cellSize.y);
foreach (ButtontitleAndAction buttondata in buttontitles) {
GameObject gameobjectButton = (GameObject)Instantiate(this.gameobjectMjalertbutton);
Transform child = gameobjectButton.transform.GetChild(0); // There should just be one child, the gameobject for the Text label.
Text textofbutton = child.gameObject.GetComponent<Text>();
textofbutton.text = buttondata.Title;
gameobjectButton.transform.SetParent(gameobjectButtons.transform, false);
Button buttonofbutton = gameobjectButton.GetComponent<Button>();
Debug.Log("buttonofbutton: " + buttonofbutton + ", for label text: " + buttondata.Title);
buttonofbutton.onClick.AddListener(() => {
Debug.Log("DOO Something!");
//buttondata.Call();
});
}
}
}
The buttons don't ever react to any taps. I'd expected "DOO Something!" something to be output but nothing.
The code above has no problems actually. I figured out the real problem. Somehow, an EventSystem item was never added to the hierarchy. Just add one and buttons will work. Actually in my case, neither buttons through code or using the editor ever worked. The lack of the EventSystem was the problem.
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());
}
}
}