Detecting change source (user or program) in radio buttons - c#

I'm trying to set a flag saying whether the last change on the Checked property was caused by the user or the program.
I'm using a custom RadioButton:
public class MyRadioButton : RadioButton
{
ValueChanger valueChanger = ValueChanger.Program;
public MyRadioButton()
{
this.Click += OnButtonClickedByUser;
this.CheckedChanged += OnCheckChange;
}
public void setChecked(bool val)
{
this.valueChanger = ValueChanger.Program;
this.Checked = val;
}
void OnButtonClickedByUser(Object sender, EventArgs e)
{
this.valueChanger = ValueChanger.User;
}
void OnCheckChange(Object sender, EventArgs e)
{
// do stuff depending on 'this.valueChanger'
}
enum ValueChanger
{
User,
Program
};
}
I call setChecked whenever the value was changed because of a message received from a serial connection, and I expect OnButtonClickedByUser to be called by the Click event whenever the value is changed through the UI.
My problem is that the CheckedChanged event fires before the Click event, which makes OnCheckChange unreliable.
Is there any way to fix that ?

User can change the value of the RadioButton by click on the control or by moving the focus to the control (arrow key, tab, mnemonic key combination).
Both OnEnter and ProcessMnemonic try to call PerformClick which calls OnClick which is responsible to checking the control. So you can override OnClick method:
protected override void OnClick(EventArgs e)
{
// Here CheckedChanged event has not been raised yet
base.OnClick(e);
}
To find out more about how RadioButton works internally, take a look at its source code.

Related

Why is Click called before OnClick and how do I solve it?

I have a custom button (public partial class QButton : Control) that has the following code to change its own checked-state when a user clicks on it:
protected override void OnMouseClick(MouseEventArgs e)
{
if (e.Button != System.Windows.Forms.MouseButtons.Left)
{
base.OnMouseClick(e);
return;
}
Status tmp = m_status;
if (m_status.HasFlag(Status.Checked))
m_status &= ~Status.Checked;
else
m_status |= Status.Checked;
if (tmp != m_status)
Invalidate();
base.OnMouseClick(e);
}
That part is working well. When using this button in a form, I connect the events in the form like this:
public void attach(Control.ControlCollection c)
{
/* ... */
m_Button.Click += OnEnable;
}
protected void OnEnable(object sender, EventArgs e)
{
/* the following still returns the old state as buttons OnMouseClick hasnt yet been called */
m_enabled = m_Button.Checked;
updateEnableState();
}
So what I want is that my form gets notified of the click and can do some magic. The problem is that the form gets notified before my button gets the notification, so first the forms OnEnable-method gets called and then the Buttons OnMouseClick-method gets called.
How do I notify the button of a sucessfull click-event before the form gets involved?
As #PaulF pointed out, the problem was that in fact Click and MouseClick are different events (didnt knew that) and that Click gets called before MouseClick.
As MouseClick only gets called on an occuring mouseclick (and might raise problems with doubleclicks), the button should override OnClick
So the solution is:
protected override void OnClick(MouseEventArgs e)
{
Status tmp = m_status;
if (m_status.HasFlag(Status.Checked))
m_status &= ~Status.Checked;
else
m_status |= Status.Checked;
if (tmp != m_status)
Invalidate();
base.OnClick(e);
}
…which also works for keyboard issued clicks on the button.

Prevent Event Handler

I have a Gridview. I and populating two dynamic text box for each cell inside it. User will enter the arriving time in first textbox and the arriving + 9 hours will be added and display in second textbox. I have written event handler where i am calculating exit time. the event handler is working fine but I need to event handler will fire for first Cell only. How to prevent event handler for rest of the textbox.
You didn't provide code but this is a general example.
private bool _isFirst = true;
private void CellEventHandler(object sender, EventArgs e)
{
if (!_isFirst) return;
// code
_isFirst = false;
}
You could also unbind the event handler
private void CellEventHandler(object sender, EventArgs e)
{
// your code here
textBox.Click -= CellEventHandler;
}

Put focus back on previously focused control on a button click event C# winforms

I have made a custom Number Keypad control that I want to place in my winform application. All of the buttons have an OnClick event to send a value to the focused textbox in my form where I have placed my custom control. Like this:
private void btnNum1_Click(object sender, EventArgs e)
{
if (focusedCtrl != null && focusedCtrl is TextBox)
{
focusedCtrl.Focus();
SendKeys.Send("1");
}
}
focusedCtrl is supposed to be set on the MouseDown event of the button like this:
private void btnNum1_MouseDown(object sender, EventArgs e)
{
focusedCtrl = this.ActiveControl;
}
where this.ActiveControl represents the active control on the form.
My problem is that the button always receives the focus before the event detects what the focused control was previously. How can I detect which control had the focus before the button got the focus? Is there another event I should be using? Thanks in advance!
EDIT: Also, I would rather not use the GotFocus event on each textbox in the form to set focusedCtrl since that can be tedious and because I would like to have all the coding of my custom control be in the control itself and not on the form where it is placed. (I will do this, though, if there is no other practical way to do what I am asking)
Your requirement is fairly unwise, you'll want some kind of guarantee that your button isn't going to poke text into inappropriate places. You really do need to have the form co-operate, only it knows what places are appropriate.
But it is not impossible, you can sniff at input events before they are dispatched to the control with the focus. In other words, record which control has the focus before the focusing event is fired. That's possible in Winforms with the IMessageFilter interface.
Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto your form, replacing your existing buttons.
using System;
using System.Windows.Forms;
class CalculatorButton : Button, IMessageFilter {
public string Digit { get; set; }
protected override void OnClick(EventArgs e) {
var box = lastFocused as TextBoxBase;
if (box != null) {
box.AppendText(this.Digit);
box.SelectionStart = box.Text.Length;
box.Focus();
}
base.OnClick(e);
}
protected override void OnHandleCreated(EventArgs e) {
if (!this.DesignMode) Application.AddMessageFilter(this);
base.OnHandleCreated(e);
}
protected override void OnHandleDestroyed(EventArgs e) {
Application.RemoveMessageFilter(this);
base.OnHandleDestroyed(e);
}
bool IMessageFilter.PreFilterMessage(ref Message m) {
var focused = this.FindForm().ActiveControl;
if (focused != null && focused.GetType() != this.GetType()) lastFocused = focused;
return false;
}
private Control lastFocused;
}
Control focusedCtrl;
//Enter event handler for all your TextBoxes
private void TextBoxesEnter(object sender, EventArgs e){
focusedCtrl = sender as TextBox;
}
//Click event handler for your btnNum1
private void btnNum1_Click(object sender, EventArgs e)
{
if (focusedCtrl != null){
focusedCtrl.Focus();
SendKeys.Send("1");
}
}
you have an event called lostFocus you can use
button1.LostFocus +=new EventHandler(dataGridView1_LostFocus);
and in the event:
Control lastFocused;
void dataGridView1_LostFocus(object sender, EventArgs e)
{
lastFocused = sender as Control;
}
in that way you can always know what is the Control that was focused previously
now, correct me if i'm wrong, but you do it for the SendKeys.Send("1"); to know which textBox need to receive the number. for that you can use GotFocus event and register only the textBoxs to it.
you can also do what windows is doing and use just one textbox like here:
if it's fits your needs
What about using this with the parameter forward = false?
Control.SelectNextControl Method
You'd probably call it on your "custom Number Keypad control".

Radio button event executing two times

In my project, There are two radioButtons. To which I have given same CheckedChanged event by doing
something like this:
DoctorRadioButton.CheckedChanged += new EventHandler(RadioButton_CheckedChanged);
PatientRadioButton.CheckedChanged += new EventHandler(RadioButton_CheckedChanged);
I kept both the RadioButtons in a Panel to make them one true while other one is false.
Now the problem is that I am implementing a very big code in the RadioButton_CheckedChanged event.
Whenever the user is changing the state of any of the two RadioButtons, the event is raising two times.
After so many hours I got the answer, the event is raising two times because both the RadioButton states are being changed(Hence, the event will be raised two times). To solve this problem I am trying to unhook the event temporarily something like this:
RadioButton_CheckedChanged Event: (Not Working)
if (DoctorRadioButton.Checked)
{
PatientRadioButton.CheckedChanged -= RadioButton_CheckedChanged; //Un
//
//My functions
//
PatientRadioButton.CheckedChanged += new EventHandler(RadioButton_CheckedChanged);
}
else
{
DoctorRadioButton.CheckedChanged -= RadioButton_CheckedChanged;
//
//My functions
//
DoctorRadioButton.CheckedChanged += new EventHandler(RadioButton_CheckedChanged);
}
Eventhough the event is executing two times. I know I am doing something wrong in Hooking and Unhooking. Please Help.
You can check the sender RadioButton and place your code accordingly like this -
void RadioButton_CheckedChanged(object sender, EventArgs e)
{
RadioButton senderRadioButton = sender as RadioButton;
if (senderRadioButton.Equals(DoctorRadioButton))
// OR senderRadioButton.Name == "DoctorRadioButton"
{
// Place your code here for DoctorRadioButton.
}
else
{
// Place your code here for PatientRadioButton.
}
}
Update
If you can't use two different handlers for both radioButtons and want to execute code only in case checkbox is checked you can do this -
void RadioButton_CheckedChanged(object sender, EventArgs e)
{
RadioButton senderRadioButton = sender as RadioButton;
if (senderRadioButton.IsChecked)
{
// Place your code here for check event.
}
}
For an extremely simple (albeit crude) solution would be to not hook both the radio buttons, and hook only one of them to the handler: since checking one radio unchecks the other one, it would work as intended.
A more complicated way would be to use a backing property, like this:
class myForm
{
private bool radioStatus = false; // depends on the default status of the radios
private bool RadioStatus
{
get{return radioStatus;} set {radioStatus = value; Checked_Changed();}
}
public myForm()
{
// Lambdas as handlers to keep code short.
DoctorRadioButton.CheckedChanged += (s,args)=>
{ if((s as RadioButton).Checked) RadioStatus = true; };
PatientRadioButton.CheckedChanged += (s,args)=>
{ if((s as RadioButton).Checked) RadioStatus = false; };
}
void Checked_Changed()
{
if (RadioStatus) // = true --> DoctorRadioButton was checked
{
//code
}
else // = false --> PatientRadioButton was checked
{
//other code
}
}
}
This approach has the advantage of allowing you to abstract from the UI a bit.
Put both radio buttons in the same panel or groupbox and automatically they will be grouped so that only one can be selected at a time.
Its a late solution but i found there is no correct answer for your question so i am posting it may be it works For You
Create Click Event for both radio button and simple put your code beacuse on every click your radio button got checked and your code executes :):):)
private void DoctorRadioButtons_Click(object sender, EventArgs e)
{
//Your code on Doctor Radio Button
}
private void PatientRadioButton_Click(object sender, EventArgs e)
{
// Your code on Patient Radio Button
}

Prevent button getting focus

I have a solution with several forms, each may have TextBox's/controls and a button to show the SIP (the bottom bar is hidden).
When the user clicks my SIP button, the SIP is enabled but the focus is now the button. I want the user to click the button - the SIP to display but the focus to remain on the control that had the focus before the user clicked the button. Does anyone know how to do this? Thanks.
Instead of using an standard button, you can create a custom one by deriving from the Control class and overriding the OnPaint method. A control created this way will not claim the focus by default when treating the Click event (tested on VS2008 netcf 2.0).
public partial class MyCustomButton : Control
{
public MyCustomButton()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.DrawString("Show SIP", Font, new SolidBrush(ForeColor), 0, 0);
// Calling the base class OnPaint
base.OnPaint(pe);
}
}
The solution of nathan will work also for Compact Framework or native Windows Mobile applications. In the textbox GotFocus set a global var and use this in the buttons click event to set the focus back to the last active textbox:
//global var
TextBox currentTB = null;
private void button1_Click(object sender, EventArgs e)
{
inputPanel1.Enabled = !inputPanel1.Enabled;
if(currentTB!=null)
currentTB.Focus();
}
private void textBox1_GotFocus(object sender, EventArgs e)
{
currentTB = (TextBox)sender;
}
regards
Josef
Edit: Solution with subclass of TextBox:
class TextBoxIM: TextBox{
public static TextBox tb;
protected override void OnGotFocus (EventArgs e)
{
tb=this;
base.OnGotFocus (e);
}
}
...
private void btnOK_Click (object sender, System.EventArgs e)
{
string sName="";
foreach(Control c in this.Controls){
if (c.GetType()==typeof(TextBoxIM)){
sName=c.Name;
break; //we only need one instance to get the value
}
}
MessageBox.Show("Last textbox='"+sName+"'");
}
Then, instead of placing TextBox use TextBoxIM.

Categories