WinForms TabControl验证:切换到验证失败的选项卡

时间:2010-02-16 11:16:13

标签: winforms validation tabcontrol tabpage

我目前有一个包含TabControl TabPage的表单。每个TabPage都有几个带有验证逻辑和适当ErrorProvider的控件。在我的OK_Button_Clicked活动中,我致电Form.ValidateChildren()以确定是否保存并关闭表单。现在,假设我在选项卡1中有一个未通过验证的控件,但当前可见的选项卡是选项卡2.当用户按下OK时,他将无法看到表单未关闭的原因。我希望能够自动切换到验证失败的选项卡,这样用户就会看到ErrorProvider的错误指示。

一种方法是订阅所有适当控件的Validatedvalidating事件,并知道每个控件都在哪个选项卡中,每当验证失败时,验证失败的选项卡列表都可以建成。因为据我所知没有生成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内部驻留在其中的事实。

1 个答案:

答案 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;
        }
    }

我认为这很可爱!