How do you control the position of a custom hint? (Or, possibly, how do you use a custom hint window class for one specific control?)

How do you control the position of a custom hint?  (Or, possibly, how do you use a custom hint window class for one specific control?)

I've written a TCustomControl descendant for which I want hints to:
a) Appear at a specific place - not at the mouse cursor
b) Render differently to normal hints

(b) is fine. I have a descendant of TCustomHint which overrides SetHintSize and PaintHint. In my custom control's constructor, I tell it to use this hint via SetCustomHint(new TCustomHint-descendant instance).  Now, I mouse over my control and get a lovely custom-painted hint. Fantastic.

(a) however is giving me trouble. Hint position seems to be set through the hint window - that is, a TCustomHintWindow. However, I can't override TCustomHintWindow.PositionAt since it's not virtual, nor have I figured out how to get my hint to use my custom hint window class without using Application.OnShowHint, which seems the wrong way for a custom control. I'd prefer to have the control itself control the hint.

I would have liked to override TCustomHint.ShowHint, for example, but it too is not virtual, and it's hard-coded to use TCustomHintWindow! No way to specify a TCustomHintWindow descendant... A lot of things seem like this: so far this afternoon each time I've thought of a different way to achieve it, I come up against hard-coded limitations (like non-virtual methods, or private fields.) There must be a way to do it, however.

Oddly, the moment I called SetCustomHint, I also no longer get CM_SHOWHINT messages in my custom control. This might be a hint (heh) about a different code flow but I haven't traced it, if so.

So, basically: how do I cause my hint to show at a specific position? If this includes using a custom hint window class, how do I get it to use that? (FWIW, the goal is a "tracking hint", ie where you move the mouse and the hint stays visible and moves relative to the mouse, where that movement position is wholly controlled by me.)

Comments

  1. Sorry for the delay. Holidays. I immediately found a hack from 2010 and can confirm that i reached the same conclusion then as you describe above. The need was similar to what you describe above. I.e. not a "hint hint" but a functional one (used to clarify typing long stings into very narrow edits and combos).

    Since this is an application (albeit with its own custom components) i did not have to fiddle to much with windows messages, but that will not apply for writing a real component. Paint is overridden and "inherited" not called. ActivateHint is called when the hint needs to be shown or updated and the hint is hidden using ShowWindow(Handle, SW_HIDE).

    I'd say there are similar problems with a lot of "owner drawing" in the VCL :) dive deep into for example DevExpress code and sometimes you get the feeling they had to rewrite the VCL.

    ReplyDelete
  2. Dany Marmur Thanks for the info!
    I haven't got DevExpress components - what is it about owner-drawing they have redone/changed/etc? I know with many VCL controls, owner-drawing is buggy - the list view is one example. Over time I've just learned how to get around most of the problems - or, learned to use other controls :) But you sound like there are some problems very deep in the VCL architecture itself?

    ReplyDelete
  3. Well, problems/bugs/deep or not. I do not think i'm the one to make such statements.

    What i'm trying to say is that i find some things (and owner-drawing comes to mind first) extremely un-flexible. However, as some concluded in a recent post about serialization, this might well be in the nature of the thing. It's complexity and it's need for detailed control.

    The TStringGrid for example. I have a watchdog keeping track of some servers. The watchdog sends me e-mails when something is amiss, but it feels nice to have a GUI potion on my second screen. It has 15 columns and i would like to color 3 of them differently because those values should be 0. This is just for me and i would like to spend maximum 15 minutes on it.

    Phew. Either i check "Manual Draw". In this case i have to re-create all the visual functionality in the component. All of it. I would expect to have access to the base class drawing functions "atomized" so that i can call them and get the exact same result but intersperse these calls with Canvs.Font.Color := clRed. OR uncheck "ManulaDraw" and have (and i guess it would be about 12 different) several OnDrawXxxx events so that i can override only OnDrawCellText. I would like to change some aspect but keep as much of the base functions, so that updates to VCL, windows et. al. will still work.

    Today i can have ManualDraw set to false, and in OnDrawCell do something like:

    procedure TWebClientForm.StatusGridDrawCell(Sender: TObject; ACol,
      ARow: Integer; Rect: TRect; State: TGridDrawState);
    begin
      if (not (gdFixed in State)) and (not (gdRowSelected in State)) and (not (gdSelected in State)) and (ACol in [8, 11, 14])
      then begin
        TStringGrid(Sender).Canvas.Font.Color := clRed;
        TStringGrid(Sender).Canvas.TextRect(Rect, Rect.Left+2, Rect.Top+2, TStringGrid(Sender).Cells[ACol, ARow]);
      end;
    end;

    But since the event is called after the built-in drawing this draws the red text on to of the black. So i end up with unnecessary painting. I would not allow that piece of code into production.

    The second line inside the block is bad. the "+2" pixel operation is copied from the base class after looking at the code. That will probably change in the future.

    All in all it's a grand hazzle. I know that we have to take windows default painting, VCL add-ons/fixes for windows, skins et. al into account. But i still get the feeling a lot of times that many things i want to do could be a lot easier.

    DevExpress combines many different ways of doing this from different "eras" of their components. All backwards compatible. However, the different techniques used often clash. Indication of a DB-bound control being read-only does not work for some skins. But works fine for others or when using the older skinless "styles" they have. It's not only the skins, the layout control can control some control painting aspects but not others.

    I guess that they brought functionality to VCL before CG/EMB and thus had to add their own stuff that look "redundant" to me. 

    If you deliver components and have the time or revenue you would test you component on for each supported version of the VCL (ie Delphi). But managing a huge application i need to minimize all the spots where i needed to copy code (like the pixel operation above).

    You do not need the DevExpress components to get inspiration, their KB is open and searchable

    https://www.devexpress.com/Support/Center

    Filter on VCL (the right hand side) and start searching for OwnerDraw or owner draw, painting or such.

    ReplyDelete

Post a Comment