WinForm - Don't allow radio button to be tabbed into - c#

Since TabStop does not work on RadioButtons (see linked question), how can I prevent a (WinForm) RadioButton from being tabbed into, but also allow the user to click on the RadioButton, without the tab focus jumping somewhere else.
I've read this and so I thought the following would work:
rbFMV.Enter += (s, e) => focusFirstWorkflowButton();
rbFMV.MouseUp += (s, e) => rbFMV.Focus();
But it doesn't. When I click on the RB, the focus jumps away, and does not come back on Mouse Up.
Any dirty workarounds out there?

Try something like this:
Set TabStop property of the radiobuttons to "false" in the form's constructor. Then attach the following events handlers to the CheckedChanged events of the radiobuttons.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
radioButton1.TabStop = false;
radioButton2.TabStop = false;
}
private void radioButton1_CheckedChanged(object sender, EventArgs e)
{
radioButton1.TabStop = false;
radioButton2.TabStop = false;
}
private void radioButton2_CheckedChanged(object sender, EventArgs e)
{
radioButton1.TabStop = false;
radioButton2.TabStop = false;
}
}
You can attach these event handlers using lambda aswell, as you have shown in your question.
But the important point here is that whenever a radiobutton is checked/unchecked, it's tabstop property is also modified simultaneously. Hence you need to set it to false everytime that event occurs.

The underlying Win32 RadioButton does not automatically change the TabStop property. However, if you use .NET Reflector you can see that the .NET control runs code to update the TabStop property whenever OnEnter method is called because focus has entered the control or whenever the AutoCheck or Checked properties are modified.
Luckily there is a simple solution to your problem. Just derive a new class that overrides the OnTabStopChanged method and automatically set it back to false again. Here is the impl...
public class NonTabStopRadioButton : RadioButton
{
protected override void OnTabStopChanged(EventArgs e)
{
base.OnTabStopChanged(e);
if (TabStop)
TabStop = false;
}
}
Then always use the NonTabStopRadioButton in your application instead of the standard one.

only one control can have input focus at the time i think, so when they click the radio button it will get focus..
But what if you do something like this?
rbFMV.GotFocus += (s, e) => someothercontrol.Focus();
also, have you looked at the TabStop property?
-edit-
i see you have, sorry, missed that :/

Related

Why doesn't a bound value persist to the datasource the first time it changes when using EndCurrentEdit?

I have a CheckBox that has it's Checked property bound to a bool value. During the CheckedChanged event, some logic runs which uses the bool property on the data source.
My problem is the first time the CheckBox gets checked by the user, the bound data source does not get updated. Subsequent updates work fine though.
Here's some sample code for testing the problem. Just create a blank form and add a CheckBox to it.
public partial class Form1 : Form
{
private bool _testBool;
public bool TestBool
{
get { return _testBool; }
set { _testBool = value; }
}
public Form1()
{
InitializeComponent();
checkBox1.DataBindings.Add(new Binding("Checked", this, "TestBool"));
checkBox1.CheckedChanged += new EventHandler(checkBox1_CheckedChanged);
}
void checkBox1_CheckedChanged(object sender, EventArgs e)
{
checkBox1.BindingContext[this].EndCurrentEdit();
Debug.WriteLine(TestBool.ToString());
}
}
The first time I check the box, the TestBool property remains at false, even though checkBox1.Checked is set to true. Subsequent changes do correctly update the TestBool property to match checkBox1.Checked though.
If I add a breakpoint to the CheckedChanged event and check out checkBox1.BindingContext[this].Bindings[0] from the immediate window, I can see that modified = false the first time it runs, which is probably why EndCurrentEdit() is not correctly updating the data source.
The same thing also occurs with using a TextBox and the TextChanged event, so this isn't limited to just CheckBox.Checked.
Why is this? And is there a generic common way of fixing the problem?
Edit: I know of a few workarounds so far, although none are ideal since they are not generic and need to be remembered everytime we want to use a Changed event.
setting the property on the datasource directly from the CheckedChanged event
finding the binding and calling WriteValue()
hooking up the bindings after the control has been loaded
I am more concerned with learning why this is happening, although if someone knows of a standard generic solution to prevent it from happening that does not rely on any special coding in the Changed event, I'd be happy with that too.
Controls usually want to go through their validation first before writing to the data source, so the writing of the value usually won't happen until you try to leave the control.
You can force the writing of the value yourself:
void checkBox1_CheckedChanged(object sender, EventArgs e) {
Binding b = checkBox1.DataBindings["Checked"];
if (b != null) {
b.WriteValue();
}
Debug.WriteLine(TestBool.ToString());
}
Apparently, the CheckedChanged event is too early in the process.
But you can leverage BindingComplete:
public partial class Form1 : Form
{
private Boolean _testBool;
public Boolean TestBool
{
get { return _testBool; }
set { _testBool = value; }
}
public Form1()
{
InitializeComponent();
checkBox1.DataBindings.Add(new Binding("Checked", this, "TestBool", true, DataSourceUpdateMode.OnPropertyChanged));
checkBox1.DataBindings[0].BindingComplete += Form1_BindingComplete;
}
private void Form1_BindingComplete(Object sender, BindingCompleteEventArgs e)
{
Debug.WriteLine("BindingComplete: " + TestBool.ToString());
}
}
Note that the event will fire at startup as the initial bind linkage occurs. You will have to deal with that possible unintended consequence, but otherwise, it works on the first click and every click.
Also note that the true (format) is required in the Binding constructor to make the event fire.
The closest I can find for an explanation to this behavior is this 3rd party explanation
Basically, this is an issue of timing. The way binding works in DotNet
is actually very simple. There's no magic in the DotNet framework that
tells the BindingManager when something changes. What it does is, when
you bind to a property (such as CheckedValue) The BindingManager looks
for an event on the control called propertynameChanged (e.g.
"CheckedValueChanged"). This is the same event your code is hooking
into on your sample form.
When the control fires the event, the order in which the listeners
receive the event is arbitrary. There's no reliable way to tell
whether the BindingManager will get the event first or the Form will.
My CheckBox1_CheckChanged event is running before the BindingManager handles the changed event, so the data source hasn't been updated at this time.
My best guess as to why this only happens the first time is that the control isn't visible yet, so some code doesn't get run that should fix the order events get handled in. I've seen other posts about not being able to bind to non-visible items due to the handle not being created yet, and one answer states
Until the control is visible for the first time some back-end initialization never happens, and part of that initialization is enabling the data binding.
So I suspect that this is somehow related.
I can verify that if I attach the Changed handler later on such as during the Load event, it works as I would expect.
public partial class Form1 : Form
{
private bool _testBool;
public bool TestBool
{
get { return _testBool; }
set { _testBool = value; }
}
public Form1()
{
InitializeComponent();
checkBox1.DataBindings.Add(new Binding("Checked", this, "TestBool"));
Load += new EventHandler(Form1_Load);
}
void Form1_Load(object sender, EventArgs e)
{
checkBox1.CheckedChanged += new EventHandler(checkBox1_CheckedChanged);
}
void checkBox1_CheckedChanged(object sender, EventArgs e)
{
// Not needed anymore
//checkBox1.BindingContext[this].EndCurrentEdit();
Debug.WriteLine(TestBool.ToString());
}
}

Is there a way to set a control as a tooltip?

I'm wondering if it's possible to use ToolTip.SetToolTip or something similar to open a control as a tooltip instead of just a string (i.e. SetToolTip(controlToWhichToAdd, panelToDisplayAsToolTip) instead of passing a string as your second parameter).
If this isn't possible I'm guessing next best thing is displaying a panel on the mouse location on mouse_enter event on the control and removing it (or making it invisible) on mouse_leave.
Or are there other practices that make this possible in an easier way?
This is not possible out of the box. You have two choices. First option is to override the Draw Event, which will let you customize how the tooltip looks. Here is an example of this. Be sure you set the OwnerDraw property to true if you use this method!
Although the first method will work if you just need some simple customization, the second option will work best if you need more flexible options. The second option is to do what you already suggested and create your own sort of tooltip. Simply put, you would first create an event handler for the MouseEnter event. When that event fires, you'd enable a Timer. This timer would be the delay that occurs before the tooltip is show. Then finally, you'd just make your panel appear at the mouse coordinates.
Suppose you have a form with a button and timer on it and you want the button to have a tooltip that is a panel:
public partial class Form1 : Form
{
private Panel _myToolTipPanel;
private void Form1_Load(object sender, EventArgs e)
{
_myToolTipPanel = new Panel {Visible = false};
Controls.Add(_myToolTipPanel);
Label myLabel = new Label();
myLabel.Text = "Testing";
_myToolTipPanel.Controls.Add(myLabel);
}
private void button1_MouseEnter(object sender, EventArgs e)
{
timer1.Enabled = true;
}
private void button1_MouseLeave(object sender, EventArgs e)
{
timer1.Enabled = false;
_myToolTipPanel.Visible = false;
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Enabled = false;
Point position = Cursor.Position;
Point formPoisition = PointToClient(position);
_myToolTipPanel.Visible = true;
_myToolTipPanel.Location = formPoisition;
}
}
Now of course you will have to do some beautifying of the tooltip, but this is the general idea!
One Approach could be inheriting the ToolTip control and then override the SetToolTip and Show methods . Inside the SetToolTip the private method - SetToolTipInternal needs to be re-written , but most of the functionality could be reuse - it uses the Mouse Events ( leave , move) to bind region. but since tooltip uses internal's of windows to show the baloon window. you will have to override quite a bit of code.
but this could be time consuming and needs quite a bit of testing.
You could write a handler for the Tooltip.Popup event, and cancel the popup to display your own panel.
You'd need to clean it up at the appropriate time, though.
For example:
private void ToolTip1_Popup(Object sender, PopupEventArgs e)
{
e.Cancel = true;
//Do work here to display whatever control you'd like
}
If you're just looking for more formatting options in the tooltip display, an alternative is something like this CodeProject entry, which implements an HTML-enabled tooltip:

When you set Checked property in a RadioBox, can you suppress the CheckChanged event?

I'm new to C# and Windows Form but if I have a radiobutton and I call radiobutton1.Checked=true, is there a way for it to not fire the CheckedChange event? I want to distinguish between the user clicking on the radiobutton and me setting the radiobutton programmatically. Is this possible?
Stop trying to defeat the design of the CheckedChanged event. It's specifically supposed to include programmatic changes.
If you want user-triggered changes and not programmatic changes, use the Click event instead. (You may be thinking that you don't want to restrict yourself to mouse clicks, don't worry, there's a MouseClick event for that, Click includes keyboard changes as well.)
Here's a straightforward method of using the event when you feel like it.
private bool SuppressRadioButton1Event { get; set; }
private void radioButton1_CheckedChanged(object sender, EventArgs e)
{
if (!this.SuppressRadioButton1Event)
{
MessageBox.Show("Not suppressed!");
}
}
private void button1_Click(object sender, EventArgs e)
{
this.SetRadioButton1(false);
}
private void SetRadioButton1(bool checkedOn)
{
this.SuppressRadioButton1Event = true;
radioButton1.Checked = checkedOn;
this.SuppressRadioButton1Event = false;
}
A very easy way:
public void radio_OnCheckChanged(object sender, EventArgs e)
{
RadioButton r = sender as RadioButton;
bool isUserChange = r.Tag.Equals(1);
if (isUserChange) blabla
else blabla
r.Tag = null;
}
public void MyMethod()
{
radio1.Tag = 1;
radio.Checked = true;
}
You can use any kind of flag which users can't do by their clicking.But you can do via your code.
Why should your code care who checked the radiobutton?
EDIT: There are ways around this (subclass, flag), but don't. The only "legit" reason I can think of for wanting this is to prevent some side-effect from happening when the value is initially (programatically) displayed, and even that is suspect. Rethink the side-effect, does it really belong on the change-event, or the commit?
More info one why/what would help. On the surface, this looks like a design error.
One (hackish) way to do it would be to subclass RadioButton and override the OnCheckChanged virtual method, suppressing the event if the Checked property has been set programmatically.
However, since radio-buttons belong to a group, the event always fires in pairs (oen for the uncheck, one for the check). You will therefore want to suppress the event for the entire group when you choose the selected button programmatically. Here's an example implementation:
public class CustomRadioButton : RadioButton
{
private bool _suppressCheckedEvent;
public void SetChecked(bool value, bool suppressCheckedEvent)
{
if (!suppressCheckedEvent)
Checked = value;
else
{
SetSupressModeForGroup(true);
Checked = value;
SetSupressModeForGroup(false);
}
}
private void SetSupressModeForGroup(bool suppressCheckedEvent)
{
foreach (var crb in Parent.Controls.OfType<CustomRadioButton>())
crb._suppressCheckedEvent = suppressCheckedEvent;
}
protected override void OnCheckedChanged(EventArgs e)
{
if (!_suppressCheckedEvent)
base.OnCheckedChanged(e);
}
}
In this implementation, changing the checked-state through the Checked property will always fire the event. When you call the SetChecked method, you have the choice to suppress the event.
You could try to attach the event programmatically. Based on my application configuration I check several radio buttons but I don't want to fire events.
To attach an event programmatically:
chbOptionX.CheckedChanged += new System.EventHandler(this.chbShowStockBySizeAndColor_CheckedChanged);

How to remove the focus from a TextBox in WinForms?

I need to remove the focus from several TextBoxes. I tried using:
textBox1.Focused = false;
Its ReadOnly property value is true.
I then tried setting the focus on the form, so as to remove it from all the TextBoxes, but this also fails to work:
this.Focus();
and the function returns false when a textbox is selected.
So, how do I remove the focus from a TextBox?
You can add the following code:
this.ActiveControl = null; //this = form
Focusing on the label didn't work for me, doing something like label1.Focus() right?
the textbox still has focus when loading the form, however trying Velociraptors
answer, worked for me, setting the Form's Active control to the label like this:
private void Form1_Load(object sender, EventArgs e)
{
this.ActiveControl = label1;
}
Try disabling and enabling the textbox.
You can also set the forms activecontrol property to null like
ActiveControl = null;
Focus sets the input focus, so setting it to the form won't work because forms don't accept input. Try setting the form's ActiveControl property to a different control. You could also use Select to select a specific control or SelectNextControl to select the next control in the tab order.
Try this one:
First set up tab order.
Then in form load event we can send a tab key press programmatically to application. So that application will give focus to 1st contol in the tab order.
in form load even write this line.
SendKeys.Send("{TAB}");
This did work for me.
This post lead me to do this:
ActiveControl = null;
This allows me to capture all the keyboard input at the top level without other controls going nuts.
A simple solution would be to kill the focus, just create your own class:
public class ViewOnlyTextBox : System.Windows.Forms.TextBox {
// constants for the message sending
const int WM_SETFOCUS = 0x0007;
const int WM_KILLFOCUS = 0x0008;
protected override void WndProc(ref Message m) {
if(m.Msg == WM_SETFOCUS) m.Msg = WM_KILLFOCUS;
base.WndProc (ref m);
}
}
I've found a good alternative! It works best for me, without setting the focus on something else.
Try that:
private void richTextBox_KeyDown(object sender, KeyEventArgs e)
{
e.SuppressKeyPress = true;
}
I made this on my custom control, i done this onFocus()
this.Parent.Focus();
So if texbox focused - it instantly focus textbox parent (form, or panel...)
This is good option if you want to make this on custom control.
It seems that I don't have to set the focus to any other elements. On a Windows Phone 7 application, I've been using the Focus method to unset the Focus of a Textbox.
Giving the following command will set the focus to nothing:
void SearchBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
Focus();
}
}
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.focus.aspx
It worked for me, but I don't know why didn't it work for you :/
//using System;
//using System.Collections.Generic;
//using System.Linq;
private void Form1_Load(object sender, EventArgs e)
{
FocusOnOtherControl(Controls.Cast<Control>(), button1);
}
private void FocusOnOtherControl<T>(IEnumerable<T> controls, Control focusOnMe) where T : Control
{
foreach (var control in controls)
{
if (control.GetType().Equals(typeof(TextBox)))
{
control.TabStop = false;
control.LostFocus += new EventHandler((object sender, EventArgs e) =>
{
focusOnMe.Focus();
});
}
}
}
The way I get around it is to place all my winform controls. I make all labels and non-selecting winform controls as tab order 0, then my first control as tab order 2 and then increment each selectable control's order by 1, so 3, 4, 5 etc...
This way, when my Winforms start up, the first TextBox doesn't have focus!
you can do this by two method
just make the "TabStop" properties of desired textbox to false now it will not focus even if you have one text field
drag two text box
make one visible on which you don't want foucus which is textbox1
make the 2nd one invisible and go to properties of that text field and select
tabindex value to 0 of textbox2
and select the tabindex of your textbox1 to 1
now it will not focus on textbox1
If all you want is the optical effect that the textbox has no blue selection all over its contents, just select no text:
textBox_Log.SelectionStart = 0;
textBox_Log.SelectionLength = 0;
textBox_Log.Select();
After this, when adding content with .Text += "...", no blue selection will be shown.
Please try set TabStop to False for your view control which is not be focused.
For eg:
txtEmpID.TabStop = false;
You can try:
textBox1.Enable = false;
using System.Windows.Input
Keyboard.ClearFocus();
Kinda late to the party in 2022, however none of the solutions here worked for me (idk why) using .Net_6.0_windows, so I've come up with this solution:
Label focusoutLabel = new Label() {
Text = "",
Name = "somegenericplaceholdernamethatwillneverbeusedinmyprogram",
Visible = false,
};
this.Controls.Add(focusoutLabel);
this.ActiveControl = focusoutLabel;
^Place this code to your Form load handler^
In the constructor of the Form or UserControl holding the TextBox write
SetStyle(ControlStyles.Selectable, false);
After the InitializeComponent();
Source: https://stackoverflow.com/a/4811938/5750078
Example:
public partial class Main : UserControl
{
public Main()
{
InitializeComponent();
SetStyle(ControlStyles.Selectable, false);
}

How to skip Validating after clicking on a Form's Cancel button

I use C#.
I have a Windows Form with an edit box and a Cancel button. The edit box has code in validating event. The code is executed every time the edit box loses focus. When I click on the Cancel button I just want to close the form. I don't want any validation for the edit box to be executed. How can this be accomplished?
Here is an important detail: if the validation fails, then
e.Cancel = true;
prevents from leaving the control.
But when a user clicks Cancel button, then the form should be closed no matter what. how can this be implemented?
If the validation occurs when the edit box loses focus, nothing about the the cancel button is going to stop that from happening.
However, if the failing validation is preventing the cancel button from doing its thing, set the CausesValidation property of the button to false.
Reference: Button.CausesValidation property
Obviously CausesValidation property of the button has to be set to false and then the validating event will never happen on its click. But this can fail if the parent control of the button has its CausesValidation Property set to true. Most of the time developers misses/forgets to change the CausesValidation property of the container control (like the panel control). Set that also to False. And that should do the trick.
I was having problems getting my form to close, since the validation of certain controls was stopping it. I had set the control.CausesValidation = false for the cancel button and all the parents of the cancel button. But still was having problems.
It seemed that if the user was in the middle of editing a field that was using validation and just decided to give up (leaving the field with an invalid input), the cancel button event was being fired but the window would not close down.
This was fixed by the following in the cancel button click event:
private void btnCancel_Click(object sender, EventArgs e)
{
// Stop the validation of any controls so the form can close.
AutoValidate = AutoValidate.Disable;
Close();
}
Set the CausesValidation property of the Cancel button to false.
Set the CausesValidation property to false.
None of these answers quite did the job, but the last answer from this thread does. Basically, you need to:
Insure that the Cancel button (if any) has .CausesValidation set to false
Override this virtual method.
protected override bool ProcessDialogKey(Keys keyData) {
if (keyData == Keys.Escape) {
this.AutoValidate = AutoValidate.Disable;
CancelButton.PerformClick();
this.AutoValidate = AutoValidate.Inherit;
return true;
}
return base.ProcessDialogKey(keyData);
}
I didn't really answer this, just pointing to the two guys who actually did.
Setting CausesValidation to false is the key, however this alone is not enough. If the buttons parent has CausesValidation set to true, the validating event will still get called. In one of my cases I had a cancel button on a panel on a form, so I had to set CausesValidation = false on the panel as well as the form. In the end I did this programatically as it was simpler than going through all the forms...
Control control = cancelButton;
while(control != null)
{
control.CausesValidation = false;
control = control.Parent;
}
In my case, in the form I set the property AutoValidate to EnableAllowFocusChange
By using Visual Studio wizard you can do it like that:
Judicious use of the Control.CausesValidation property will help you achieve what you want.
Just above the validation code on the edit box add:
if (btnCancel.focused)
{
return;
}
That should do it.
In complement of the answer of Daniel Schaffer: if the validation occurs when the edit box loses focus, you can forbid the button to activate to bypass local validation and exit anyway.
public class UnselectableButton : Button
{
public UnselectableButton()
{
this.SetStyle(ControlStyles.Selectable, false);
}
}
or if you use DevExpress:
this.simpleButtonCancel.AllowFocus = false;
Note that doing so will change the keyboard experience: the tab will focus anymore on the cancel button.
Maybe you want to use BackgroundWorker to give little bit delay, so you can decide whether validation should run or not. Here's the example of avoiding validation on form closing.
// The flag
private bool _isClosing = false;
// Action that avoids validation
protected override void OnClosing(CancelEventArgs e) {
_isClosing = true;
base.OnClosing(e);
}
// Validated event handler
private void txtControlToValidate_Validated(object sender, EventArgs e) {
_isClosing = false;
var worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerAsync();
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
// Do validation on complete so you'll remain on same thread
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (!_isClosing)
DoValidationHere();
}
// Give a delay, I'm not sure this is necessary cause I tried to remove the Thread.Sleep and it was still working fine.
void worker_DoWork(object sender, DoWorkEventArgs e) {
Thread.Sleep(100);
}
This is an old question however I recently ran into this issue and solved it this way:
1st, we are loading a UserControl into a 'shell' Form that has a save and cancel button. The UserControl inherit an interface (like IEditView) that has functions for Save, Cancel, Validate and ToggleValidate.
In the shell form we used the mouse enter and mouse leave like so:
private void utbCancel_MouseEnter(object sender, EventArgs e)
{
((Interface.IEdit)tlpMain.Controls[1]).ToggleValidate();
}
private void utbCancel_MouseLeave(object sender, EventArgs e)
{
((Interface.IEdit)tlpMain.Controls[1]).ToggleValidate();
}
Then in ToggleValidate (Say a simple form with two controls...you can always just loop through a list if you want) we set the CausesValidation
public bool ToggleValidate()
{
uneCalcValue.CausesValidation = !uneCalcValue.CausesValidation;
txtDescription.CausesValidation = !txtDescription.CausesValidation;
return txtDescription.CausesValidation;
}
Hope this helps.
I found this thread today while investigating why my form would not close when a validation error occurred.
I tried the CausesValidation = false on the close button and on the form itself (X to close).
Nothing was working with this complex form.
While reading through the comments I spotted one that appears to work perfectly
on the form close event , not the close button (so it will fire when X is clicked also)
This did the trick.
AutoValidate = AutoValidate.Disable;
Create a bool:
bool doOnce;
Set it to false in your function and then:
if (doOnce == false)
{
e.cancel = true;
doOnce = true;
}
This means it will only run once and you should be able to cancel it. This worked for me anyways.
This work for me.
private void btnCancelar_MouseMove(object sender, MouseEventArgs e)
{
foreach (Control item in Form.ActiveForm.Controls)
{
item.CausesValidation = false;
}
}

Categories

Resources