0

I have two edits boxes on a form, one for a min value and the other for a max value that the user needs to enter. I want to catch possible errors as the user is entering the values. One possible error is that the max value is less than the min value. I bring up a error message if this happens. However, even if the user wants to enter a 5 in the min box and a 100 in the max box, it brings up the error message even as the user is entering the "1" of the 100 in the max box if he has already entered a 5 in the min box. How to allow the user to enter the entire value before bringing up the error message?

Here is my code (i catch other errors too, but only max < min error seems to be affected):

procedure TfrmAnalysisOptions.lbleConstraintsMaxChange(Sender: TObject);
var
  I: integer;
  Val, ValidEntry: string;
  Chr: char;
  RangeMin, RangeMax: Double;
  const Allowed = ['0'..'9', '.'];
begin

  Val := lbleConstraintsMax.Text;

      //initialize values    
  ValidEntry := '';
  ConstraintsMaxChange := '';

  //value can contain only numerals, and "."
    for I := 1 to Length(Val) do
     begin
       Chr := Val[I];
       if not (Chr in Allowed) then
       begin
     MessageDlgPos('The value entered for the max value of the ' +
               'constraint must contain only a numeral, a decimal ' +
               'point or a negative sign.',
            mtError, [mbOK], 0, 300, 300);
     Exit;

       end
       else ValidEntry := 'OK'; //validity check for this part

     end;

     //max value cannot be zero or less than the min value
    if not TryStrToFloat(Val, RangeMax) then Exit
    else if RangeMax = 0 then
    begin
       MessageDlg('Max value cannot be zero.', mtError, [mbOK], 0);
       Exit;
    end
    else if not TryStrToFloat(lbleConstraintsMin.Text, RangeMin) then Exit
    else if RangeMax < RangeMin then
      begin
    MessageDlgPos('Max value cannot be less than Min value.',
           mtError, [mbOK], 0, 300, 300);
    Exit;
      end

    else if (RangeMax < 0) then
      begin
    MessageDlgPos('A constraint cannot be negative.',
              mtError, [mbOK], 0, 300, 300);
    Exit;
      end

    //final validity check
    else if ValidEntry = 'OK' then ConstraintsMaxChange := 'OK'
    else MessageDlgPos('There was an unexpected problem with the ' +
               'value entered in the max constraints box.',
            mtError, [mbOK], 0, 300, 300);

end;
4

2 回答 2

5

OnChange每次修改编辑控件的内容时都会触发该事件。虽然这是过滤无效字符的合适位置,但它不是验证数值的合适位置。没有办法预测用户打算输入什么数字,是否5, 50, 500, 5,000, 其中的每一个都可能是可接受的,也可能是不可接受的。

解决方案 1:在填写完这两个编辑控件并且用户尝试进入下一步(保存、继续、加载、启动等)后,执行此检查。

解决方案 2:您可以在这些控件旁边显示“无效条目”标签,而不是显示消息框,同时保持相同的逻辑。这样用户就不会面对大量的消息框。

解决方案 3:使用OnExit事件而不是OnChange. 请注意,这不会完全解决您的问题,而是将其最小化。当焦点离开控件时触发此事件。

解决方案 4与解决方案 2 类似,使用OnChangeOnExit来控制Enabled用户可能接下来按下的按钮的属性。

解决方案 5与解决方案 4 类似,使用操作并更改该Enabled特定操作的属性。然后,用户可能按下的任何按钮都不能被点击。


附带说明一下,当您过滤掉无效字符时,忽略这些字符并且首先不要让它们进入控件而不是弹出消息会更优雅一些。您可以改用OnKeyPress事件或使用TMaskEdit.

于 2013-10-09T20:46:38.883 回答
1

作为 OnExit 或 OnChange 的替代方法,我可以建议一种面向对象的模式方法:

  1. 定义模型对象。 TMyBusinessModelObject = class ....
  2. 在 View 对象中定义一个将数据复制到模型对象的函数。
  3. 在模型对象中进行验证。
  4. 如果有意义,请在需要时启动验证,即模式对话框关闭时。如果您需要在屏幕上显示颜色代码,例如图像或状态指示器,请使用OnExit事件。
  5. 编写单元测试,以便您了解模型对象、视图模型或控制器对象是否正常工作,以及您的业务逻辑是否正常工作。

其次,甚至比你编写代码来阻止无效字符TMaskEditTEdit地方更好,JVCL 中有专门为数字输入设计的控件,包括允许你在控件级别输入约束的控件。

Delphi 是一个面向组件的 RAD 工具,当你像使用它一样使用它时,当你从一开始就避免使用代码隐藏形式(Big Ball of Mud)时,你以后就不会有一个巨大的混乱需要清理. (一个相当可悲的事实是,标准的 Delphi 实践是做最简单的事情,并且构建一个巨大的泥球,没有 OOP。)

于 2013-10-10T21:33:14.913 回答