Hold GUI event thread until until method finish - delphi

Currently in my application the user is able to create windows (no controls on the as of yet) which are docked to a pagecontrol. Each editor has a set of toolboxes associated with them. This set is reset upon editor construction and modified as toolboxes are closed and opened. When the page control changes tab the state of the toolboxes is saved, they are closed and then the toolboxes for the new tab are restored.
My application is crashing (seemingly randomly at first) when you change tab. I believe it is when you change tab very fast (the speed of a double click) the processing of the toolboxes in the PageControl.OnChange event doesnt complete before the next PageControl.OnChange event starts and I end up with two events running in 'parallel'. My questions are:
1) Does this sound like a plausible theory? I am aware there is a GUI processing thread does delphi work by dispatching new threads to deal with events?
2) How would you suggest going about fixing this? Hold the messages until the first method finishes (it is a very quick method so there shouldnt be any use lag really)?
Here is some code incase anyone would like to look at the problem from another angle.
This is the code for the onchange event.
procedure TMainForm.PageDockingAreaChange(Sender: TObject);
var
count : integer;
// Shortcut variables
FormShortcut : TForm;
WelcomeShortcut : TWelcomePageForm;
BaseEditorShortcut : TBaseEditor;
begin
// Set nil values
FormShortcut := nil;
WelcomeShortcut := nil;
BaseEditorShortcut := nil;
// Create shortcut value
FormShortcut := Self.GetActiveForm;
if (FormShortcut is TWelcomePageForm) then WelcomeShortcut := TWelcomePageForm(FormShortcut);
if (FormShortcut is TBaseEditor) then BaseEditorShortcut := TBaseEditor(FormShortcut);
// Hide all tabs when welcome page is visible
if (WelcomeShortcut <> nil) then
begin
// Clear any existing toolboxes
Self.ToolboxClearAll;
end;
{endif}
// Try to execute toolbox setup
try
// Load toolbox state
Self.ToolboxSetup(BaseEditorShortcut);
except
// Display fatal error to the user
ShowMessage('Fatal Error : Unable to load toolboxes.');
end;
// Update last active editor
if (BaseEditorShortcut = nil) then
Self.LastActiveEditor := nil
else
Self.LastActiveEditor := BaseEditorShortcut;
{endif}
end;
This method closes all toolboxes and frees them in turn.
procedure TMainForm.ToolboxClearAll;
var
count : integer;
begin
// Save toolbox state if needed
if Assigned(Self.LastActiveEditor) then
Self.LastActiveEditor.SaveToolboxState;
// Close and free all toolboxes
for count := 0 to length(Self.ActiveToolboxes)-1 do
Self.ToolboxCloseAndFree(Self.ActiveToolboxes[count]);
// Reset array size (should already be done though)
SetLength(Self.ActiveToolboxes, 0);
end;

Related

The listviewer not change select in onshow

how can I change a selected item in listview...
here is my code sample:
for c := 0 to Form1.LV1.Items.count -1 do
begin
if (form1.lv1.Items[c].SubItems.Objects[3] as TTabSheet).TabIndex =
pgc1.ActivePageIndex then
begin
form1.lv1.Items[c].Selected:= True;
end;
end;
I created a simple application with a list view, set it into report mode, added some items, and added this OnShow event:
procedure TForm1.FormShow(Sender: TObject);
begin
ListView1.Items[1].Selected := True;
end;
The specified item was indeed selected.
The conclusion that I draw from this is that the Selected property can be used from the OnShow event. Therefore, if your program does not result in the list view selection be set, it would seem that either:
The code in the question is not running at all, or
The code is running, but the if statement condition is never True.
Your next step is to debug your program. Inspect your program as it executes using your preferred debugging technique. The interactive debugger would be a sound choice.

Shortcut triggers TAction on first created form instead of form with focus

I found (in Delphi 2010) that shortcuts always end up on first form (as owned by main form) that has that action, but not the currently focused form. My TMainFrm owns several TViewFrm. Each has a TActionManager with the same TActons.
I see some ways out, but wonder whats the best fix.. (and not a bad hack)
The forms are navigated using a tabset which calls their Hide() and Show(). I'd did not expect hidden forms to receive keypresses. Am i doing something wrong?
It seems that action shortcuts are always start at the main form, and using TCustomForm.IsShortCut() get distributed to owned forms. I see no logic there to respect hidden windows, should i override it and have it trigger the focused form first?
Disabling all TActions in TViewFrm.Hide() .. ?
Moving the TActionToolBar to TMainFrm but that is a pit of snakes and last resort.
I have found a workaround thats good enough for me; my main form now overrides TCustomForm.IsShortcut() and first checks visible windows from my list of editor tabs.
A list which i conveniently already have, so this might not work for everyone.
// Override TCustomForm and make it check the currently focused tab/window first.
function TFormMain.IsShortCut(var Message: TWMKey): Boolean;
function DispatchShortCut(const Owner: TComponent) : Boolean; // copied function unchanged
var
I: Integer;
Component: TComponent;
begin
Result := False;
{ Dispatch to all children }
for I := 0 to Owner.ComponentCount - 1 do
begin
Component := Owner.Components[I];
if Component is TCustomActionList then
begin
if TCustomActionList(Component).IsShortCut(Message) then
begin
Result := True;
Exit;
end
end
else
begin
Result := DispatchShortCut(Component);
if Result then
Break;
end
end;
end;
var
form : TForm;
begin
Result := False;
// Check my menu
Result := Result or (Menu <> nil) and (Menu.WindowHandle <> 0) and
Menu.IsShortCut(Message);
// Check currently focused form <------------------- (the fix)
for form in FEditorTabs do
if form.Visible then
begin
Result := DispatchShortCut(form);
if Result then Break;
end;
// ^ wont work using GetActiveWindow() because it always returns Self.
// Check all owned components/forms (the normal behaviour)
if not Result then
Result := inherited IsShortCut(Message);
end;
Another solution would be to change DispatchShortCut() to check for components being visible and/or enabled, but that might impact more than i'd like. I wonder whether the original code architects had a reason not to -- by design. Best would be have it called twice: first to give priority to visible+enabled components, and second call as fallback to normal behavior.

How to force execution of code at form show

I have the following code in a unit that I include as the last unit in my uses list
{ TFormHelper }
procedure TForm.WMMoving(var aMessage: TWMMoving);
var
rec: ^TRect;
wrk: TRect;
begin
wrk := GetWorkArea;
rec := Pointer(aMessage.DragRect);
if rec^.Left < wrk.Left then
begin
rec^.Right := rec^.Right - (rec^.Left - wrk.Left);
rec^.Left := wrk.Left;
end
else if rec^.Right > wrk.Right then
begin
rec^.Left := rec^.Left - (rec^.Right - wrk.Right);
rec^.Right := wrk.Right;
end;
if rec^.Top < wrk.Top then
begin
rec^.Bottom := rec^.Bottom - (rec^.Top - wrk.Top);
rec^.Top := wrk.Top;
end
else if rec^.Bottom > wrk.Bottom then
begin
rec^.Top := rec^.Top - (rec^.Bottom - wrk.Bottom);
rec^.Bottom := wrk.Bottom;
end;
end;
It is supposed to check if a form is inside the working window of my main form and if not then it should move the form to a valid position.
In the form I want to check I put
SendMessage(Handle, WM_MOVING, 0, 0);
in the FormShow event, but it has no effect.
I know the function works because if I try to drag the form with the mouse it is moved to a valid position at once.
So my question is: how can I force the code to run when form is shown?
To run code when a form is shown, put code in the OnShow event handler.
That's not your problem, though. Your problem is that your code has no effect. The wm_Moving message is normally sent while a window is being moved by the user, as during a drag operation. The OS continually sends the message to ask the form where it's allowed to go, including tentative window coordinates where the window will be moved to. The window responds to the message by adjusting the proposed window bounds, and then the OS either moves the window to those new coordinates or changes the drag rectangle (depending on whether the "full window drag" option is set).
Merely sending a lone wm_Moving message doesn't do any of that, though. You're essentially asking the form where it wants to go, but since you're playing the role of the OS in this scenario, you still need to act on the results you get and actually move the window. The wm_Moving message is a notification, not a command; it has no inherent side effects of its own.

Incorrectly drawn themed checkbox in TVirtualStringTree

Checkbox handling in version 5.0.0 of VirtualTrees.pas appears broken when toThemeAware is enabled. Nodes that are csUncheckedNormal are drawn as checked + hot.
To correctly paint an unchecked, themed checkbox using DrawElement, the Details record must be : Element = teButton, Part = 3, and State = 5. However, VirtualTrees.pas ends up calling DrawElement with State = 1 when a node is set to csUncheckedNormal.
There seems to be a good deal of indirection and extra constants declared in VirtualTrees, so I'm not sure how best to fix this. Ideas welcomed...
(Even the minimal code to get a TVirtualStringTree on screen and filled with some data is a bit lengthy to post here. Aside from the basics, all that's needed to reproduce this is to enable toCheckSupport in TreeOptions.MiscOptions, and set Node.CheckType := ctTriStateCheckBox in the InitNode callback.)
Well, since I think the VirtualTreeView does not count with the VCL styles when porting to delphi XE2, this might light up to solve your problem. You have to get element details before you draw it, otherwise you'll get something like this (it's the simulation of how the VirtualTreeView paint check box states). Notice the different order and the artifacts; it's the result of the same code once with VCL styles disabled, second time enabled:
Quite strange I know, but I can't answer you why is this happening. I can just tell you that you should call the TThemeServices.GetElementDetails or optionally calculate the state index by your own to get the element rendering to work properly. You may try to use the following fix:
procedure TBaseVirtualTree.PaintCheckImage(Canvas: TCanvas;
const ImageInfo: TVTImageInfo; Selected: Boolean);
var
// add a new variable for calculating TThemedButton state from the input
// ImageInfo.Index; I hope ImageInfo.Index has the proper state order
State: Integer;
begin
...
case Index of
0..8: // radio buttons
begin
// get the low index of the first radio button state and increment it by
// the ImageInfo.Index and get details of the button element
State := Ord(TThemedButton(tbRadioButtonUncheckedNormal)) + Index - 1;
Details := StyleServices.GetElementDetails(TThemedButton(State));
end;
9..20: // check boxes
begin
// get the low index of the first check box state and increment it by
// the ImageInfo.Index and get details of the button element
State := Ord(TThemedButton(tbCheckBoxUncheckedNormal)) + Index - 9;
Details := StyleServices.GetElementDetails(TThemedButton(State));
end;
21..24: // buttons
begin
// get the low index of the first push button state and increment it by
// the ImageInfo.Index and get details of the button element
State := Ord(TThemedButton(tbPushButtonNormal)) + Index - 21;
Details := StyleServices.GetElementDetails(TThemedButton(State));
end;
else
Details.Part := 0;
Details.State := 0;
end;
...
end;
I've tested this for all check types and it works for me.

Check is current active window Desktop or no

I try to check if current active window is Desktop do something , i wrote below code in a timer but the handle value returned by GetDektopWindow & GetForegroundWindow is not same value :
if GetForegroundWindow = GetDesktopWindow then
// Do something
How do this ?
// not defined in D2007
function GetShellWindow: HWND; stdcall; external user32;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if GetForegroundWindow = GetShellWindow then
[..]
end;
With the only non-explorer shell I use (sharpe) it fails though.
update:
Sometimes the window hierarchy of the desktop is different (see Andreas' comments). The below shot is Spy++'s take when Windows 7's desktop picture rotation functionality is activated. Some 'WorkerW' window takes over the screen and it is the one that gets activated when clicked on the desktop. Since GetShellWindow returns the 'Progman's handle, the above test fails.
At this point it might seem reasonable to test if the foreground window has the shell's default view window as its immediate child, however I saw multiple references that indicate multiple 'WorkerW' windows might get nested. So I think the below would be a more fail-safe approach:
procedure TForm1.Timer1Timer(Sender: TObject);
function HasDefViewChild(Wnd: HWND): Boolean;
begin
Result := Wnd <> 0;
if Result then begin
Result := FindWindowEx(Wnd, 0, 'SHELLDLL_DefView', nil) <> 0;
if not Result then
Result := HasDefViewChild(FindWindowEx(Wnd, 0, 'WorkerW', nil));
end;
end;
begin
if HasDefViewChild(GetForegroundWindow) then
[...]
end;
This will work when the foreground window is 'Progman', because then the 'DefView' is 'Progman's child. OTOH when 'WorkerW' is the active window, the code will iterate if the first child is not 'DefView' and yet another 'WorkerW' instead.
A great tool for figuring out the structure of window parent/child relationships, window classes, etc., is WinDowse by Greatis Software.
http://www.greatis.com/delphicb/windowse/
I would start there. And I'd output the values of GetForegroundWindow and GetDesktopWindow (etc.,) onto labels in your test app. So you can see what those values are, as you poke around with WinDowse, in real time.
I search about this , GetWindowDesktop return the handle of desktop window but the desktop window is under another window called shell , so when you switch to dektop really you switch to shell window and must get shell handle , if you terminate process of shell window ( explorer.exe ) then you can see the real dektop window .

Resources