使用ErrorProvider改善用户体验

1. ErrorProvider概述        

         我们经常使用Windows Forms下的ErrorProvider,这是一个.NET组件,可以在VS IDE的工具栏中找到它。Errorprovider通常用于窗体或控件的用户输入;同时它也是典型的关联型(绑定)的验证控件,用以验证并显示控件关联的数据源如DataSet中存在的错误。ErrorProvider比使用消息框(MessageBox)显示错误信息的效果好,除了摘要中提到的情况外,还因为一旦消息框关闭了错误信息也随之消失,用户可能无法记忆所有的错误内容因而也就无法方便的纠正所有错误从而要多次弹出错误消息框。而ErrorProvider会记录所有的错误并能够准确定位在错误发生的窗体或控件上,显示一个红色图标,当鼠标悬停在该图标上方时还能够自动弹出ToolTip提示以显示错误内容。这样操作人员可以根据错误发生的位置以及具体内容从容的修改错误,直到这些小图标全部消失。

2. ErrorProvider属性     

    ErrorProvider组件有三个关键的属性:DataSource、ContainerControl和Icon。

  • DataSource通常与ContainerControl相关,也就是控件绑定的数据内容,可以是DataTable,DataSet或者自定义的实体等。ErrorProvider会根据DataSource的结构进行其内部的数据验证,当然验证的规则需要开发人员定义。
  • ContainerControl正如其名字所言,正是ErrorProvider所依存的容器,通过为其设置适当的容器,通常是Windows窗体,就是为了Errorprovider能够在窗体上的适当位置显示错误图标。(不要忘了,ErrorProvider是一个组件,凡是组件都具备相应的特征,其中很重要的一点就是容器的概念,如果你不清楚可以从MSDN上查阅有关组件的概念,请记住组件式的开发是微软大力推荐的一种开发规范,其实J2EE的规范中也处处体现这样的思想。)
  • Icon属性允许用户自定义图标。如果你对默认的这个小红圆圈不满意的话,没关系,你可以通过该属性用你自己的图标替换。

3. ErrorProvider常规用法

        ErrorProvider的一种最简单使用方法就是当用户向某控件比如TextBox中输入无效数据时显示错误提示,这种情况下甚至不需要设置DataSource,只需要设置ContainerControl属性值为当前Form并使用SetError方法进行简单编码即可,如下所示。

protected void textBox1_Validating (object sender,
   System.ComponentModel.CancelEventArgs e)
{
   try
   {
      int x = Int32.Parse(textBox1.Text);
      errorProvider1.SetError(textBox1, "");
   }
   catch (Exception e)
   {
      errorProvider1.SetError(textBox1, "Not an integer value.");
   }
}

        在窗体上一个TextBox控件的验证事件处理方法中我们检查用户填入内容是否是数值,如果不是则在该TextBox控件旁显示错误图标,相应的ToolTip文本就是“Not an integer value”。从另外一个角度,我们也可以猜到如果要手工消除错误信息,也是通过SetError方法将提示文本设为空字符串即可。

        实际上,ErrorProvider组件也可以捕获到DataSet或其他数据源的列上出现的错误,也就是说一旦将ErrorProvider绑定到数据源,不必直接关联到控件就可以在绑定相同数据源的控件上显示错误图标,代码如下。

  • 将ErrorProvider组件绑定到一个DataTable的某一列上(使用DataSource和DataMember属性)

         textBox1.DataBindings.Add("Text", DataSet1, "Customers.Name");
         errorProvider1.DataSource = DataSet1;
         errorProvider1.DataMember = "Customers";

  •  设置Errorprovider的ContainerControl属性

         errorProvider1.ContainerControl = this;

  • 设置包含一个列错误的某一行的位置

         DataTable1.Rows[5].SetColumnError("Name", "Bad data in this row.");//Name是列名,后面是错误提示
         this.BindingContext [DataTable1].Position = 5;//发生错误的行是Table的第五行,ErrorProvider绑定这一行

 4. ErrorProvider的实体绑定机制

        上面列举的ErrorProvider的用法都是最基本的,不用编码或者只需少量编码或者设置一些相关的属性就能够实现一定的功能,然而这些实现方式对于一个相当规模的系统而言扩展性非常有限,不够灵活,而且没有与系统框架紧密结合起来。事实上,大多数情况下我们的系统具有严格的分层,其中不乏有O/RMapping。当在前台界面上作数据保存、更新时往往会涉及到对底层实体的操作。实体装载的数据是否满足业务规则,是否满足数据库定义的要求应当能够反映在前台的界面上。这样一个从上层(UI层)提交数据给下层(业务层、实体层)到从下层反馈错误信息给上层的过程也可以利用ErrorProvider提供的便利。我们完全可以构建一套完整的数据绑定机制,当实体层发生数据错误时在界面上的ErrorProvider可以及时感知到并显示出来。这也就是所谓的智能感知,当然除了ErrorProvider还需要bindingsource组件的支持。如下图所示。

使用ErrorProvider改善用户体验_第1张图片

在系统界面上我们可以看到接受用户输入信息的控件都有一个DataBindings属性目录,其中又包含三个具体的控件属性项分别是Advanced、Tag、Text,如下图所示。

点击"高级"(Advanced)项,弹出一个数据绑定对话框,右边的条目分别用以设置该控件绑定数据源的字段,数据源发生更新的模式以及该控件录入信息必须满足的格式类型,如下图所示。

使用ErrorProvider改善用户体验_第2张图片

        对于智能感知而言,数据源更新模式项非常重要,Never条目表明不提示数据源更新;OnPropertyChanged表明当数据源属性发生改变时提示数据源更新,用户可以看到窗体界面的左上角窗体标题的后面多出一个"*"号,操作保存或放弃后该星号消失;OnValidation表明当数据验证时提示数据源更新。(关于智能感知的部分不是本文介绍的重点,请参见MSDN相关文档。)同样对于错误提示而言也有两种情况,一种是当用户在界面控件上输入不符合规则的数据后立刻提示错误,另一种是用户点击保存或更新按钮动作后再进行数据验证并在界面上显示错误信息,两种方式都是通过ErrorProvider实现的,这里重点讨论第二种方式。

     为了让ErrorProvider能够感知到数据源内部发生的错误,数据实体应至少继承两个接口INotifyPropertyChanged和IDataErrorInfo。这两个接口都位于System.ComponentModel命名空间下,前者提供了一个属性改变事件PropertyChanged,继承的实体内每个简单属性都应该引发该事件以便外部捕获到属性值的改变;后者提供了一个错误信息列表Error用以自定义错误信息,也就是ToolTip要显示的内容,还有一个索引器用以根据列名获取对应的错误信息。通常情况下,我们都会为所有的实体定义一个基类,该基类继承上述接口,实现相应的属性,另外为方便起见还会新增一个IsValid属性以表明派生的实体类当前属性是否通过验证。

      需要注意的是,这里关于Error属性的设置也存在两种方式,一种是在实体内部定义业务验证规则,将每个属性必须满足的规则定义好以及将发生错误时生成的错误信息定义好以便填充到Error属性中。但是这种方法存在一个很严重的缺陷就是业务规则与实体实现紧耦合不方便移植和扩展。另一种办法就是在实体基类中定义一个公共方法专门用以为每个属性设置错误信息,这样我们就可以在业务层进行数据校验时将对应错误信息写入到该实体的错误列表中。

      在完成了实体层的实现和业务层实现后,ErrorProvider就可以捕获到绑定数据源内产生的错误并在用户界面上绑定相同数据源相应字段的控件上显示错误图标和提示了。

5. 使用ErrorProvider的局限性

         ErrorProvider的功能非常强大,正如其名称所言对于处理错误消息是再合适不过了,然而对于警告信息(不属于错误的那些)甚至普通消息提醒的处理就显得力不从心。遗憾的是,微软也并没有为开发人员提供WarningProvider或者NotifyProvider,这些功能目前仍需要使用MessageBox来实现。我曾试图利用ErrorProvider的数据绑定机制实现警告提醒和普通消息提醒,能够智能感知,根据不同的消息类型显示不同的提示图标。但是,如果你仔细研究了上述的数据绑定机制,就会发现真的要做到这些并不是件容易的事情。^_^

你可能感兴趣的:(开发心得)