Search code examples
delphihint

Programatically show Balloon Hint only for invalid text edit input


I'm following this example: http://lawrencebarsanti.wordpress.com/2009/12/16/display-error-messages-with-tballoonhint/

I'm trying to only show a balloon hint when the current value in the edit box is not acceptable. The check is triggered when OnExit. The balloon should still be allowed to display until the value is determined to be OK. I'm also trying to programmatically show the balloon as soon as the user leaves the edit to show the initial error.

The code works, but not the first time. I have to leave once with an invalid value, change to an acceptable one, then come back and use an invalid one again. I think this is because I can't enable or disable the ShowHint property right before I try to show the balloon.

Here is my code:

procedure TForm1.Edit1Exit(Sender: TObject);
var
  R: TRect;
  Bad : Boolean;
begin
  //Check if edit has only numbers
  if StrIsReal(Edit1.Text) then
  begin
    if(StrToFloat(Edit1.Text) >= 0.5) then
    begin
      //Value is ok
      SpeedButton1.Visible := false;
      Edit1.ShowHint := false;
      BalloonHint1.HideHint;
      Edit1.Text := FloatToStrF(StrToFloat(Edit1.Text), ffFixed, 8, 2);
    end
    else
    begin
      //Is decimal, but not at least 0.5
      Bad := true;
    end;
  end
  else
  begin
    Bad := true;
  end;

  if Bad then
  begin
    //Invalid number
    Edit1.ShowHint := true;

    Edit1.Text := '0.00';
    SpeedButton1.Visible := true;

    R := Edit1.BoundsRect;
    R.TopLeft := ClientToScreen(R.TopLeft);
    R.BottomRight := ClientToScreen(R.BottomRight);
    BalloonHint1.ShowHint(R);   //!!! Issue: No hint the first time around
  end;
end;

How can I show the balloon conditionally right when I check for a valid value (leaving the edit)?


Solution

  • Actually, the thing that's odd is that you get anything the second time, not that you don't get anything the first.

    Your code works for me (in XE4 & XE6) first time (and second) with this change:

      R := Edit1.BoundsRect;
      R.TopLeft := ClientToScreen(R.TopLeft);
      R.BottomRight := ClientToScreen(R.BottomRight);
      BalloonHint1.Description := 'bad input';   <---- this was missing
      BalloonHint1.ShowHint(R);   //!!! Issue: No hint the first time around
    

    So, if it did work for you second time around, I think it's because of code you're not showing, and equally something you're not showing is probably why it doesn't work the first. So you can "spot the difference", the code of my project is below. I couldn't find your StrIsReal function so rolled my own.

    Btw, I added a second TEdit to my form, so that there is something else on it to lose focus to, and commented out your two Edit1.ShowHint assignments as they don't make any difference but shouldn't be necessary anyway.

      TForm1 = Class(TForm)
      [...]
        FBalloonHint : TBalloonHint;
        procedure WMActivateApp(var AMessage: TMessage); message WM_ActivateApp;
        procedure WMWindowPosChanged(var AMessage: TMessage);  message WM_WindowPosChanged;
      [...]
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      FBalloonHint := TBalloonHint.Create(Self);
      FBalloonHint.HideAfter := 5000;
      FBalloonHint.Delay := 0;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      R: TRect;
    begin
      FBalloonHint.Description := 'You pressed ' + Button1.Caption;
      R := Button1.BoundsRect;
      R.TopLeft := ClientToScreen(R.TopLeft);
      R.BottomRight := ClientToScreen(R.BottomRight);
      FBalloonHint.ShowHint(R);
    end;
    
    procedure TForm1.Edit1Exit(Sender: TObject);
    var
      R: TRect;
      Bad : Boolean;
      F : Double;
    
      function StrIsReal(S : String) : Boolean;
      begin
        Result := True;
      end;
    
    begin
      //Check if edit has only numbers
      if StrIsReal(Edit1.Text) then
      begin
        if(StrToFloat(Edit1.Text) >= 0.5) then
        begin
          //Value is ok
          SpeedButton1.Visible := false;
          //Edit1.ShowHint := false;
          FBalloonHint.HideHint;
          Edit1.Text := FloatToStrF(StrToFloat(Edit1.Text), ffFixed, 8, 2);
        end
        else
        begin
          //Is decimal, but not at least 0.5
          Bad := true;
        end;
      end
      else
      begin
        Bad := true;
      end;
    
      if Bad then
      begin
        //Invalid number
        //Edit1.ShowHint := true;
    
        Edit1.Text := '0.00';
        SpeedButton1.Visible := true;
    
        R := Edit1.BoundsRect;
        R.TopLeft := ClientToScreen(R.TopLeft);
        R.BottomRight := ClientToScreen(R.BottomRight);
        FBalloonHint.Description := 'bad input';
        FBalloonHint.ShowHint(R);   //!!! Issue: No hint the first time around
      end;
    end;
    
    procedure TForm1.SpeedButton1Click(Sender: TObject);
    begin
      Edit1Exit(Sender);
    end;
    
    procedure TForm1.WMActivateApp(var AMessage: TMessage);
    begin
      if Assigned(FBalloonHint) then FBalloonHint.HideHint;
      inherited;
    end;
    
    procedure TForm1.WMWindowPosChanged(var AMessage: TMessage);
    begin
      if Assigned(FBalloonHint) then FBalloonHint.HideHint;
      inherited;
    end;