Strange wm_keydown behaviour with THtmlEdit - delphi

This is a strange one: This is the situation
The application builds an edit form at run-time from an XML document. The UI/UX design is such that there are three levels of nested TTabControls and for the final (lowest) level the TTabItem is created at run-time. This has a TVertScrollBox which itself contains a column-grid control of mine and that (at last!) contains the THtmlEditor.
The problem is that when the controls are built and the content loaded, the THtmlEdit initially ignores keydown. It will respond to mouse events, the caret can be positioned. Switching to a different top-level tab, or away from the whole application and back, then cures the issue and the editor responds to keydown messages.
I have tried putting a breakpoint in procedure THtmlEditor.KeyDown() ... and then tracing back up the call-chain. There doesn't seem to be anything behaving differently. I've paid particular attention to input focus, explicitly calling Editor.SetFocus even though that is apparently already called ...
I have tried putting a conditional breakpoint in function TPlatformWin.HandleMessage: Boolean; ... and my only observation is that when in non-working mode, the call to DispatchMessage(Msg) doesn't arrive at the editor, or its parent form.
I have tried to build a MDC for this, replicating the structure outlined above but ... that always works!
What can I try next?
Has anyone seen this behaviour (and fixed it)?
EDIT:
The detail I didn't mention -- didn't think about it -- is that for the error condition to show, the control focused before the THmtlEditor is a TWebBrowser. If I set focus on a TEdit after TWebBrowser and then to THtmlEditor it seems to work.
Grrr!

This is only half an answer, but for future reference, this is what I've done:
It's a bodge :-(
I've put a TEdit on the main form and set it invisible. I've then added a handler to the MainForm OnFocusChanged which sets a boolean trap-flag to track if TWebBrowser has previously been focused.
For the instances of THtmlEditor I've added an OnClick handler which checks the trap-flag and makes the TEdit visible, calls SetFocus on it, re-hide the TEdit and return True so that the THtmlEditor can re-set focus to itself. The trap-flag is to avoid unnecessarily losing focus if the user is just clicking in the THtmlEditor.
The next step -- which I may never get to -- would be to trace through the focus code for TEdit. My suspicion is that it all relates ITextInput which TEdit supports and THtmlEditor doesn't.

Related

Remove TChart from form programmatically

I'm looking for the correct way in order to remove a TChart and deallocate all the memory.
I am using Delphi2007 with the standard TeeChart 7
I create the TChart programmatically:
var parentform: TForm;
begin
newchart:= TChart.Create(parentform);
newchart.Parent:= parentform;
...
Then, I want to remove only the chart from the form (not closing the form itself), but I get only that the chart becomes blank and stays on the form:
newChart.FreeAllSeries;
FreeAndNil(newChart);
if I use
NewChart.Parent := nil,
I don't see the chart anymore, but I think the TChart object still exists (until the parentform is destroyed). Is there a specific method for doing this?
Thank you
The most straightforward way to get rid of a TChart control, or just about any control, for that matter, is to call Free on it:
newChart.Free;
You can call FreeAndNil instead if you wish. That has the same effect of calling Free, but also sets the variable's value to nil. That's useful if you later test the variable's value to detect whether you still have access to the control. If you never reference the variable again, then FreeAndNil doesn't get you much.
The control will automatically free the other things it owns, such as the series you manually freed with FreeAllSeries. You don't need to free them yourself.
Merely clearing the control's Parent property does not free the control. You can prove that by re-assigning the Parent property and watching as the control re-appears on the screen. That wouldn't happen if the control had ceased to exist.
If the control remains visible on the screen after you free it, then you have other problems. Maybe the parent control hasn't repainted itself properly. You might try calling Refresh on the parent control. You might also have multiple controls visible. After all, the question's code creates two chart controls, so maybe one of them is still visible, and you've mistaken it for the control you destroyed.

How determine that a dragging operation has ended in FireMonkey?

I want to find out if a dragged control has been released outside any target.
One would think that OnDragEnd should be used, but that event doesn't work (they forgot to call the DragEnd procedure in the FMX.Types unit).
As an alternative, I tried OnMouseUp for the dragged control. Doesn't work. Doesn't get triggered when DragMode=dmAutomatic.
As a last resort, I tried to override the MouseUp procedure of the form itself (since all mouse events go through the form before being handed down to the respective control). Surprise: the MouseUp procedure does not get called when the control's DragMode=dmAutomatic.
Amazing how something this very simple is this extremely hard to achieve, but I'm hoping someone might have found a working solution.
It is possible to override the form's DragLeave method. This method is called for all objects that get dropped outside of a target area.

Delphi 6 TListBox OnMeasureItem() and OnDrawItem() never called for lbOwnerDrawVariable() list box

I have a Delphi 6 app with a TListBox control set to lbOwnerDrawVariable. At run-time I add a single string to the list box. I have event handlers for OnMeasureItem() and OnDrawItem() and I set breakpoints on the very first line of code in each event handler. However neither of them are ever called. Not once. Not even if I make an Explicit Refresh or Repaint call on the list box.
This is really basic stuff so what is it that I am doing wrong that could inhibit the calling of those event handlers and subsequently disrupting my owner draw code? The single string does show in the list box properly. I threw in an OnClick() event handler just to see if it worked and did.
The OnMeasureItem and OnDrawItem events are (indirectly) triggered in response to the WM_MEASUREITEM and WM_DRAWITEM messages from Windows. Make sure you do not have any message handlers in your app that are filtering out that message, or the VCL's internal CN_MEASUREITEM and CN_DRAWITEM messages.
It turns out the problem was due to a non-zero value in the Columns property of the TListBox I was using. I had been experimenting with using columns earlier before I converted over to owner-draw and had left the TListBox.Columns property with a non-zero value. Apparently a non-zero Columns property value inhibits the triggering of owner-draw related event triggering. Once I set that property back to zero OnMeasureItem() and OnDrawItem() started firing.
There's really very little that can go wrong here. If you set up a test app to try this out then it functions just as you would expect and the event handlers are called.
The most likely cause of the behaviour you report is if the items are added before the event handlers are assigned. This typically happens if the items are added at design time in the .dfm file. You say you are adding the items at runtime. Perhaps you are adding them too soon, before the event handlers are assigned. What happens if you add items in response to an event, e.g. a button click. Try that out because you can be sure then then the event handlers are assigned by that point.
If that doesn't help then clearly you have some code in your app that is interfering with the VCL code.
I had this same issue: my OnDrawItem event handler was not being called if the Columns property was non-zero. It turned out this was because the Style property was set to lbOwnerDrawVariable. Variable item height is not allowed in conjunction with multiple columns, presumably because the rows would not line up across the columns if the item heights were allowed to be different. Once the Style property was set to lbOwnerDrawFixed the OnDrawItem event handler was called as expected.
I had a similar problem with a csOwnerDrawVariable-style combobox not triggering the OnMeasureItem event. As David Heffernan suggested, the issue was that the items had been added to the list at design time. The work-around I ended up using was to add code to the FormCreate event handler to copy the design-time list to a temporary variable, then clear the list and add the items back. Kludgy but effective.

TTreeView drawing error when deactivating a form

I have found what appears to be a bug related to TTreeView.
Take a form containing a TTreeView with HideSelection set to True.
Make the tree view multi-select and select multiple items in the tree view.
Show another form so that your app has two forms.
Give the tree view the focus and then click in the other form.
The result looks like this:
But in fact there should be no items highlighted. Interestingly, the last item is selected and it is no longer highlighted, as indeed should all the other items. It appears that the most recently clicked item is the one that gets the special treatment.
If instead you click in the edit box (or indeed any other control that takes focus) then all items are correctly hidden. So it's fine for the focus to transfer to another control on the form—the problem seems to be limited to deactivating the form.
I have discovered by trial and error that I can fix this by calling Invalidate on the tree view whenever the form is deactivated and activated (need to prevent mirror image of the bug). However, I'm looking for a better understanding of what the bug is and how to fix it in a less invasive manner, i.e. at the tree view level rather than the containing form level.
So, to summarise, my questions are:
What exactly is causing the problem?
How can I fix it without writing code that hooks TForm events?
Submitted the issue as QC#94908.
The solution seems to be to respond to NM_SETFOCUS and NM_KILLFOCUS notifications by invalidating selected nodes. You can modify TCustomTreeView.CNNotify directly or you can write a new TCustomTreeView descendant. Here is a quick hack only to show the missing code:
type
TTreeView = class(ComCtrls.TTreeView)
private
procedure CNNotify(var Message: TWMNotifyTV); message CN_NOTIFY;
end;
procedure TTreeView.CNNotify(var Message: TWMNotifyTV);
begin
case Message.NMHdr^.code of
NM_KILLFOCUS, NM_SETFOCUS:
InvalidateSelectionsRects;
end;
inherited;
end;
Edit: David's QC report.

Modify a Delphi DFM resource to close upon showing?

Is it possible to edit a DFM (Delphi's form script format) in such a way that a form closes itself when shown?
I don't code in Delphi, so I'm not familiar with how these forms work, but it seems I could put code (but not standard Delphi code as it seems) in the OnShow or OnCreate events of the form. However, after trying several statements like Close, Exit, FormNameExit, Destroy, etc. won't work (a log will be created, stating the error that the value of the OnShow property was invalid, etc.)
The normal way of closing the form is through a button, but the button doesn't have a OnClick event, just a property, "ModalResult = 1".
Is there a way to make the window close upon opening, some standard function I could put on the OnCreate or OnShow events of the form? Or maybe, creating a checkbox on the form itself, that gives ModalResult = 1? (don't know if this works)
Thanks for any suggestion!
=)
(Note: maybe it's obvious, but I don't have the source.)
Not in DFM. You would have to modify the source.
The OnShow and OnCreate lines you're seeing are only used to give the name of a method that's already defined in the source code. You can't add much functionality at all by modifying the DFM file.
Perhaps the form already has a matching event handler that closes it: the OnClick handler for a close button or menu item, maybe? If so, you could try setting it as the OnShow or OnCreate handler.
You might be able to add a TButton to the form and set its ModalResult -- I don't recall whether you actually need a field in the form class for each control in the DFM -- but that would only work if the form is shown modally, and you'd still have to click it to make the form close.
EDIT: Seeing some of your comments added while I typed my text-wall clarifies things a bit.
I'm guessing that you you're using a resource editor to edit the DFM and modify the behaviour of the app without actually touching the source code?
In this case, the best you could try is to set the Visible property to False. However, this will have no benefit if the developer 'actively displays the form in code'. (He could have done this by calling Show, ShowModal or even by explicitly setting the Visible property.)
Unfortunately, if this is the case, then there's nothing you can do without modifying the actual source code. This is because the DFM is processed when the form is loaded; i.e. before the developer's code that shows the form. Even looking for a place to set ModalResult is useless, because the current ModalResult is cleared when ShowModal is called.
I don't think I understand exactly what you're trying to do, because it doesn't make sense.
It seems to me that you want the form to be automatically closed as soon as it is shown; and that doesn't make sense. :S
So, if I have understood you correctly, please explain why you would want to do this; there may be a better solution for your actual problem.
However, some general concepts...
If you want a form to close, you should link it to some action that closes the form. Either put a button on the form, or a menu item.
Standard forms have a standard Windows mechanism to close them by default. (I.e. the X on the top-right.)
There are two ways of showing forms, and the way in which it is shown does have an effect on how it will be closed. It can be shown modally (which means it is the only form of the application which will interact with the user), or it can be shown normally (which allows the user to switch between other forms of the application).
The point of showing a form modally is that it blocks the flow of your code until the user has finished doing something that was required; it often involves the user providing some form of answer or confirmation.
When shown modally, the form should rather be closed with a ModalResult.
When shown normally, the ModalResult has no effect.
Whenever a form is 'closed', there are a few ways in which this can be done.
The form can simply be hidden; it's still there, but invisible. Next time you want to show the form, you just make it visible again.
The form can be destroyed; meaning that it no longer exists in memory. If this is done, then next time you want to use the form, you have to recreate it.
The attempt to close the form can be actively prevented (Not usually advisable; but may be necessary in specific cases - if for example some information on the form is incorrect).
The form may be simply minimised (this is often done with MDI child forms).
NOTE: There are also a number of attributes on forms (FormStyle being the most important) that have an effect on how it behaves, displays, and can be closed. (E.g. MDI Child forms will by default either minimise, or do nothing when closed.)
NB:If the main form of an application is properly closed, then the application will shut down.
Now, some of the technicalities...
As mentioned earlier a form can be displayed either modally, or normally; using either MyForm.Show; or ModalResult := MyForm.ShowModal;
NOTE: If the form was shown modally, you then need to check the ModalResult to find out the user's answer and act accordingly.
If you displayed the form modally, you should set the ModalResult and the form will close itself. An easy way to do this is to assign a ModalResult to the buttons on the form; then the button will automatically set the form's ModalResult when clicked.
If you displayed the form normally, then all you need to do is call MyForm.Close at the appropriate point in time. NB: Note their are other ways to 'close' a form, but it is better to use this method because it allows you to handle the OnCloseQuery event which is a mechanism to confirm whether the form is allowed to close.
NOTE: While closing the form, there are two events that Delphi can call which you can handle in order to modify how the closing of the form behaves:
OnCloseQuery is called to confirm whether the form is allowed to close.
OnClose is called to find out how the form should close (as explained previously).
Coming back to your question (which sounds like you want the form closed automatically). Rather than closing the form automatically; just don't bother showing it. This is very easy to do. All forms have a Visible property; if set to True, Delphi will automatically show the form normally when it is created. So all you need to do is ensure the property is False.
You really can't do much without the source but move files around or change existing properties. If you have a MAP file for the program and there are existing events in place (onCreate/OnShow) you could patch the executable to invoke different code for those events, but it won't be easy and you have to insure that you don't inject more code than was there previously or make any external calls to routines which don't exist.

Resources