我想在更改状态之前验证单选按钮,但当焦点离开控件时它真的会触发
I want to validate a radiobutton before it changes state, but it really fires when the focus leaves the control
我有一个简单的 Winform 示例来演示会发生什么。
有一个带有两个 RadioButton 的 GroupBox。这两个按钮共享一个验证事件处理程序。窗体上还有一个不执行任何操作的按钮。没有事件关联。最后有一个 CheckBox 控制通过验证的状态。检查它通过并取消检查它失败。
当程序启动并且用户单击任一 RadioButton 时,不会触发验证事件。然后,当我单击当前按钮以外的任何控件时,将触发验证事件。 NOP 按钮让我可以在 CheckBox 之外点击一些东西。
本次测试中CheckBox表示通过验证的状态。
这将不起作用,因为一旦我取消选中 CheckBox 然后单击单选按钮,焦点就会永远卡住。您无法获得 CheckBox 的焦点以更改其状态。
原因是 Validating 事件总是在焦点离开时调用,而不是在单击 RadioButton 时调用。它看到 "valdating" 失败并取消了事件。
这显然是错误的做法。在初始点击时我应该做什么来测试?单击事件在状态更改后发生。
我应该使用什么事件才能在更改 RadioButton 状态之前测试验证?然后我可以离开按钮并在重试之前解决问题。
这个例子是一个简化的测试,显示了我的死胡同。我的真实示例是 DataGridView 中两个相似 table 之一的两个 RadioButtons select。这两个 table 是相关的,我希望它们在同一个 TabPage 上。当用户 select 是备用 table 时,我想在切换之前做一个 validation/confirmation。如果确认失败我想取消单选按钮。
// Validate is called when the radio button is clicked and when it leaves the box
using System.ComponentModel;
using System.Windows.Forms;
namespace ValidateRadioButton
{
public partial class Form1 : Form
{
int count;
public Form1()
{
InitializeComponent();
}
private void radiobutton_Validating(object sender, CancelEventArgs e)
{
if (sender is RadioButton) {
// If box not checked, cancel radioButton change
// This becomes a Hotel California test, once unchecked you
// can never leave the control
e.Cancel = !chkAllowChange.Checked;
}
count++;
//Display the number of times validating is called in the title bar
//Demonstrates when the event is called
Text = count.ToString();
}
}
}
我已经在测试项目中复制了您的表单,我想我知道发生了什么。
将 CancelEventArgs
的 Checked
属性 设置为 true 将防止任何控件设置失去焦点,直到输入 "corrected"。如您所知,只要控件失去焦点,就会触发 Validating
事件。正如您所说,用户变成了 "stuck",因为他们可以 "correct" 其输入的唯一方法是修改复选框,由于收音机上触发的 Validating
事件,他们无法访问该复选框按钮。
我想出的一个解决方案是将 Validated
事件移动到 GroupBox
,而不是单选按钮:
private void groupBox_Validating(object sender, CancelEventArgs e)
{
if (sender is RadioButton)
{
e.Cancel = !checkBox1.Checked;
}
count++;
//Display the number of times validating is called in the title bar
//Demonstrates when the event is called
Text = count.ToString();
}
一定要从单选按钮中删除Validating
事件处理程序。
为清楚起见,我以这种方式设置了表单:
结果是现在我无法单击 'OK' ,直到 我勾选了复选框。我保留了您的 Text = count.ToString()
分配,以便您可以看到该表单现在仅按应有的方式调用 Validating
。
我最终为 RadioButtons 使用了 CheckChanged
和 Click
事件。
我跟踪当前活动的 RadioButton
并在 CheckChanged
事件中进行验证。然后在 Click
事件中,如果验证失败,我将恢复之前的活动 RadioButton。否则我们可以继续RadioButton的目的。
此解决方案适用于一组中的两个或多个 RadioButton,并且在使用键盘时也适用。
using System.ComponentModel;
using System.Windows.Forms;
namespace ValidateRadioButton
{
public partial class Form1 : Form
{
// RadioButton that is currently active
RadioButton ActiveRadioButton;
bool cancelingChange;
public Form1()
{
InitializeComponent();
activeRadioButton = this.radioButton1;
}
private void radioButton_CheckedChanged(object sender, System.EventArgs e)
{
// Each click fires two events
// One for the RadioButton loosing its check and the other that is gaining it
// We are interested in the one gaining it
if (sender is RadioButton) {
RadioButton radioButton = (RadioButton)sender;
if (radioButton.Checked) {
// If this button is changing because of a canceled check
// do not validate
if (!cancelingChange) {
cancelingChange = !ValidateData();
if (!cancelingChange) {
// Mark this as the active value
activeRadioButton = radioButton;
}
}
}
}
}
private void radioButton_Click(object sender, System.EventArgs e)
{
// This is called after the RadioButton has changed
if (cancelingChange) {
// Check theRadioButton that was previously checked
activeRadioButton.Checked = true;
cancelingChange = false;
}
else {
// Do the thing the RadioButton should do
// If using separate events they all must start with the condition above.
this.Text = ((RadioButton)sender).Name;
}
}
/// <summary>
/// Validate state of data
/// </summary>
/// <returns></returns>
private bool ValidateData()
{
bool result = chkAllowChange.Checked;
if (!result) {
result = MessageBox.Show("Do you want to save your data?", "CheckBox unchecked", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) == DialogResult.OK;
if (result) {
// This is where I would save the data
chkAllowChange.Checked = true;
}
}
return result;
}
}
}
我有一个简单的 Winform 示例来演示会发生什么。
有一个带有两个 RadioButton 的 GroupBox。这两个按钮共享一个验证事件处理程序。窗体上还有一个不执行任何操作的按钮。没有事件关联。最后有一个 CheckBox 控制通过验证的状态。检查它通过并取消检查它失败。
当程序启动并且用户单击任一 RadioButton 时,不会触发验证事件。然后,当我单击当前按钮以外的任何控件时,将触发验证事件。 NOP 按钮让我可以在 CheckBox 之外点击一些东西。
本次测试中CheckBox表示通过验证的状态。
这将不起作用,因为一旦我取消选中 CheckBox 然后单击单选按钮,焦点就会永远卡住。您无法获得 CheckBox 的焦点以更改其状态。
原因是 Validating 事件总是在焦点离开时调用,而不是在单击 RadioButton 时调用。它看到 "valdating" 失败并取消了事件。
这显然是错误的做法。在初始点击时我应该做什么来测试?单击事件在状态更改后发生。
我应该使用什么事件才能在更改 RadioButton 状态之前测试验证?然后我可以离开按钮并在重试之前解决问题。
这个例子是一个简化的测试,显示了我的死胡同。我的真实示例是 DataGridView 中两个相似 table 之一的两个 RadioButtons select。这两个 table 是相关的,我希望它们在同一个 TabPage 上。当用户 select 是备用 table 时,我想在切换之前做一个 validation/confirmation。如果确认失败我想取消单选按钮。
// Validate is called when the radio button is clicked and when it leaves the box
using System.ComponentModel;
using System.Windows.Forms;
namespace ValidateRadioButton
{
public partial class Form1 : Form
{
int count;
public Form1()
{
InitializeComponent();
}
private void radiobutton_Validating(object sender, CancelEventArgs e)
{
if (sender is RadioButton) {
// If box not checked, cancel radioButton change
// This becomes a Hotel California test, once unchecked you
// can never leave the control
e.Cancel = !chkAllowChange.Checked;
}
count++;
//Display the number of times validating is called in the title bar
//Demonstrates when the event is called
Text = count.ToString();
}
}
}
我已经在测试项目中复制了您的表单,我想我知道发生了什么。
将 CancelEventArgs
的 Checked
属性 设置为 true 将防止任何控件设置失去焦点,直到输入 "corrected"。如您所知,只要控件失去焦点,就会触发 Validating
事件。正如您所说,用户变成了 "stuck",因为他们可以 "correct" 其输入的唯一方法是修改复选框,由于收音机上触发的 Validating
事件,他们无法访问该复选框按钮。
我想出的一个解决方案是将 Validated
事件移动到 GroupBox
,而不是单选按钮:
private void groupBox_Validating(object sender, CancelEventArgs e)
{
if (sender is RadioButton)
{
e.Cancel = !checkBox1.Checked;
}
count++;
//Display the number of times validating is called in the title bar
//Demonstrates when the event is called
Text = count.ToString();
}
一定要从单选按钮中删除Validating
事件处理程序。
为清楚起见,我以这种方式设置了表单:
结果是现在我无法单击 'OK' ,直到 我勾选了复选框。我保留了您的 Text = count.ToString()
分配,以便您可以看到该表单现在仅按应有的方式调用 Validating
。
我最终为 RadioButtons 使用了 CheckChanged
和 Click
事件。
我跟踪当前活动的 RadioButton
并在 CheckChanged
事件中进行验证。然后在 Click
事件中,如果验证失败,我将恢复之前的活动 RadioButton。否则我们可以继续RadioButton的目的。
此解决方案适用于一组中的两个或多个 RadioButton,并且在使用键盘时也适用。
using System.ComponentModel;
using System.Windows.Forms;
namespace ValidateRadioButton
{
public partial class Form1 : Form
{
// RadioButton that is currently active
RadioButton ActiveRadioButton;
bool cancelingChange;
public Form1()
{
InitializeComponent();
activeRadioButton = this.radioButton1;
}
private void radioButton_CheckedChanged(object sender, System.EventArgs e)
{
// Each click fires two events
// One for the RadioButton loosing its check and the other that is gaining it
// We are interested in the one gaining it
if (sender is RadioButton) {
RadioButton radioButton = (RadioButton)sender;
if (radioButton.Checked) {
// If this button is changing because of a canceled check
// do not validate
if (!cancelingChange) {
cancelingChange = !ValidateData();
if (!cancelingChange) {
// Mark this as the active value
activeRadioButton = radioButton;
}
}
}
}
}
private void radioButton_Click(object sender, System.EventArgs e)
{
// This is called after the RadioButton has changed
if (cancelingChange) {
// Check theRadioButton that was previously checked
activeRadioButton.Checked = true;
cancelingChange = false;
}
else {
// Do the thing the RadioButton should do
// If using separate events they all must start with the condition above.
this.Text = ((RadioButton)sender).Name;
}
}
/// <summary>
/// Validate state of data
/// </summary>
/// <returns></returns>
private bool ValidateData()
{
bool result = chkAllowChange.Checked;
if (!result) {
result = MessageBox.Show("Do you want to save your data?", "CheckBox unchecked", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) == DialogResult.OK;
if (result) {
// This is where I would save the data
chkAllowChange.Checked = true;
}
}
return result;
}
}
}