Question

I'm trying to use TCustomHint to show a message to my user that fades in and out nicely, to not be too distracting. However when I call ShowHint on my object with a point, the hint box appears to center itself around the point I give. What I would like is to have my box appear such that its top-left coordinate is the point given.

Here's the code I'm using so show the hint:

procedure ShowNotification(ATitle: UnicodeString; AMsg: UnicodeString);
var
  Box: TCustomHint;
  P: TPoint;
begin
    Box := TCustomHint.Create(MyForm);
    Box.Title := ATitle;
    Box.Description := AMsg;
    Box.Delay := 0;
    Box.HideAfter := 5000;
    Box.Style := bhsStandard;

    P.X := 0;
    P.Y := 0;

    Box.ShowHint(P);
end;

I know that my point's X/Y coordinates are not relative to the form, and that's not the issue.

I've traced through what happens when I call ShowHint and it appears that if I can somehow control the final width of the underlying TCustomHintWindow inside of TCustomHint.ShowHint(Rect: TRect) then I may be in business.

So my question is: is there an obvious way to stop a TCustomHint from centering itself at my point? Or will I have to go through the process of inheriting, overriding the draw method, etc etc? I hope I'm just missing something simple.

Était-ce utile?

La solution

There's no particularly easy way to do what you want. The TCustomHint class is designed to serve a very specific purpose. It was designed to be used by the TControl.CustomHint property. You can see how it is called by looking at the code for TCustomHint.ShowHint. The pertinent excerpts are:

if Control.CustomHint = Self then
begin
  ....
  GetCursorPos(Pos);
end
else
  Pos := Control.ClientToScreen(Point(Control.Width div 2, Control.Height));
ShowHint(Pos);

So, either the control is shown centred horizontally around the current cursor position, or centred horizontally around the middle of the associated control.

I think the bottom line here is that TCustomHint is not designed to be used the way you are using it.

Anyway, there is a rather gruesome way to make your code do what you want. You can create a temporary TCustomHintWindow that you never show and use it to work out the width of the hint window that you want to show. And then use that to shift the point that you pass to the real hint window. In order to make it fly you need to crack the private members of TCustomHintWindow.

type
  TCustomHintWindowCracker = class helper for TCustomHintWindow
  private
    procedure SetTitleDescription(const Title, Description: string);
  end;

procedure TCustomHintWindowCracker.SetTitleDescription(const Title, Description: string);
begin
  Self.FTitle := Title;
  Self.FDescription := Description;
end;

procedure ShowNotification(ATitle: UnicodeString; AMsg: UnicodeString);
var
  Box: TCustomHint;
  SizingWindow: TCustomHintWindow;
  P: TPoint;
begin
  Box := TCustomHint.Create(Form5);
  Box.Title := ATitle;
  Box.Description := AMsg;
  Box.Delay := 0;
  Box.HideAfter := 5000;
  Box.Style := bhsStandard;

  P := Point(0, 0);
  SizingWindow := TCustomHintWindow.Create(nil);
  try
    SizingWindow.HintParent := Box;
    SizingWindow.HandleNeeded;
    SizingWindow.SetTitleDescription(ATitle, AMsg);
    SizingWindow.AutoSize;
    inc(P.X, SizingWindow.Width div 2);
  finally
    SizingWindow.Free;
  end;
  Box.ShowHint(P);
end;

This does what you asked, but honestly, it makes me feel rather queasy.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top