我目前有一个包含TabControl
TabPage
的表单。每个TabPage
都有几个带有验证逻辑和适当ErrorProvider
的控件。在我的OK_Button_Clicked
活动中,我致电Form.ValidateChildren()
以确定是否保存并关闭表单。现在,假设我在选项卡1中有一个未通过验证的控件,但当前可见的选项卡是选项卡2.当用户按下OK时,他将无法看到表单未关闭的原因。我希望能够自动切换到验证失败的选项卡,这样用户就会看到ErrorProvider
的错误指示。
一种方法是订阅所有适当控件的Validated
和validating
事件,并知道每个控件都在哪个选项卡中,每当验证失败时,验证失败的选项卡列表都可以建成。因为据我所知没有生成ValidationFailed
事件,所以这可能很麻烦(例如,为每个控件定义一个布尔值,在验证之前将其设置为false,并在其Validated
事件中将其设置为true)。即使我有这样的事件,我也会被迫听取许多验证事件,每个验证事件对应一个可能无法验证的控件,并维护代码中未经验证的选项卡列表。我应该注意,直接订阅TabPage
的验证事件不起作用,因为即使其中包含的控件未通过验证,它们也会通过验证。
另一种方法可以利用我TabPage
中的控件恰好是自定义控件的事实。然后我可以让它们实现一个接口,例如:
interface ILastValidationInfoProvider
{
public bool LastValidationSuccessful {get; set;}
}
例如:
public MyControl : UserControl, ILastValidationInfoProvider
{
MyControl_Validing(object sender, object sender, CancelEventArgs e)
{
if (this.PassesValidation())
this.ErrorProvider.SetError(sender, null);
LastValidationSuccessful = true;
else
e.Cancel = true;
this.ErrorProvider.SetError("Validation failed!", null);
LastValidationSuccessful = false;
}
}
然后,在致电ValidateChildren
之后,我可以使用以下代码:
public void OK_Button_Click
{
if (form.ValidateChildren())
this.Close()
else
foreach (TabPage tab in this.TabControl)
foreach (Control control in tab.Controls)
{
ValidationInfo = control as ILastValidationInfoProvider
if (ValidationInfo != null && !ValidationInfo.LastValidationSuccessful)
{
this.TabControl.SelectTab(tab);
return;
}
}
}
我更喜欢这种方法,但它不能满足被验证的控件不是自定义的情况。
我很乐意使用更好的方法。有什么想法吗?
编辑我正在使用Form.AutoValidate = EnableAllowFocusChange
(正如Chris Sells在他的WinForms书中所推荐的那样),因此焦点确实可以从验证失败的控件(包括移动到其他选项卡)中改变。我还更新了自定义控件的示例代码,以强调ErrorProvider
内部驻留在其中的事实。
答案 0 :(得分:3)
好的,所以我终于明白了。
我保留了一个字典,其密钥为TabPages
,值为相应标签中未经验证的控件的HashSet
个。通过订阅每个选项卡中控件的所有验证和验证事件,可以轻松完成此操作。最后,在OK_BUtton_Click
中,如果ValidateChildren
失败,我知道其中一个散列集将为空,我只是跳转到第一个未经验证的选项卡(仅当当前选中的选项卡本身没有任何错误时) )。
Dictionary<TabPage, HashSet<Control>> _tabControls
= new Dictionary<TabPage, HashSet<Control>>();
public OptionsForm()
{
InitializeComponent();
RegisterToValidationEvents();
}
private void RegisterToValidationEvents()
{
foreach (TabPage tab in this.OptionTabs.TabPages)
{
var tabControlList = new HashSet<Control>();
_tabControls[tab] = tabControlList;
foreach (Control control in tab.Controls)
{
var capturedControl = control; //this is necessary
control.Validating += (sender, e) =>
tabControlList.Add(capturedControl);
control.Validated += (sender, e) =>
tabControlList.Remove(capturedControl);
}
}
}
private void Ok_Button_Click(object sender, EventArgs e)
{
if (this.ValidateChildren())
{
_settings.Save();
this.Close();
}
else
{
var unvalidatedTabs = _tabControls.Where(kvp => kvp.Value.Count != 0)
.Select(kvp => kvp.Key);
TabPage firstUnvalidated = unvalidatedTabs.FirstOrDefault();
if (firstUnvalidated != null &&
!unvalidatedTabs.Contains(OptionTabs.SelectedTab))
OptionTabs.SelectedTab = firstUnvalidated;
}
}
我认为这很可爱!