How can I reduce PageControl flicker in Delphi? - delphi

In Delphi 2009 I found that the flicker of a PageControl - which occurs during resizing of the form - can be reduced by setting its DoubleBuffered property to true.
However if I add controls to the PageControl tabsheets, they will flicker regardless of their DoubleBuffered property setting. I have also tried with and without runtime themes enabled.

Setting ParentBackground to False for components on the PageControl helped a lot. However this results in a different color of these panel components, they all have a darker background now. Maybe this can be fixed easily (without losing Theme support).
I also installed VCL Fix Pack which has a fix for QC 56252 (TPageControl flickers a lot with active theming).

This is far from perfect, but you might want to use this:
protected
procedure WMExitSize(var Message: TMessage); message WM_EXITSIZEMOVE;
procedure WMEnterSize(var Message: TMessage); message WM_ENTERSIZEMOVE;
procedure TFormMain.WMEnterSize(var Message: TMessage);
begin
if Assigned(PageControlView.ActivePage) then
PageControlView.Align := alNone;
end;
procedure TFormMain.WMExitSize(var Message: TMessage);
begin
if Assigned(PageControlView.ActivePage) then
PageControlView.Align := alClient;
end;
It's the best I found this far, and will reduce the windows update of your page control. It might be less pretty, though, but that's a matter of opinions...

Related

Keyboard language changes to default when editing a dbgrid with Delphi XE2 [duplicate]

I use ActivateKeyboardLayout(HKL_NEXT, KLF_ACTIVATE); to load Persian keyboard layout using Delphi XE2, But sometimes I click on TextBox or DbGrid control the keyboard automatically is changed to English layout.
How can I disable automatic changing of keyboard layout?
We have same issues. This bug appeared after upgrade from D2006 to D2010.
Issue is in DBGrids.pas:
procedure TCustomDBGrid.WMKillFocus(var Message: TMessage);
begin
ImeName := Screen.DefaultIme;
ImeMode := imDontCare;
inherited;
if not ((InplaceEditor <> nil) and
(HWND(Message.WParam) = InplaceEditor.Handle)) then
ActivateKeyboardLayout(Screen.DefaultKbLayout, KLF_ACTIVATE);
end;
I have no idea why Borland/Embarcadero added "if block" after inherited. Change causes switching keyboard layout back to default. We had to copy DBGrids to our projects and remove "if block".

Why is the taskbar button context menu of my Delphi 2006 application incomplete?

When I create a new VCL application in Delphi 2006 and run it (without adding any of my own code or refernce any of my own units), the application won't have all of menu items one would expect in the context menu of it's taskbar button. The application's system menu (the menu you get when left-clicking the form's icon), however, has all the regular menu items. As you can see in the following screenshots, Move (Verschieben), Size(Größe ändern) and Maximize(Maximieren) are missing from the former
I could not reproduce this in Delphi XE (the only other vesion of Delphi I have access to) and I haven't found anybody else reporting this behavior, either.
I have also looked through the properties of TForm and TApplication whether there was one to control these menus, but haven't found one.
All applications I know of have the same set of menu items in those two menus and I would like my application to do, too. How do I get these two menus to show the same set of items?
The difference lies in Application.MainFormOnTaskBar, a property introduced in D2007 which is set automatically True.
To acquire the same effect in earlier versions, I always use the following approach:
Project.dpr:
uses
Windows,
...
Application.CreateForm(TMainForm, MainForm);
ShowWindow(Application.Handle, SW_HIDE);
Application.Run;
FMain.pas:
TMainForm = class(TForm)
private
procedure WMSysCommand(var Message: TWMSysCommand);
message WM_SYSCOMMAND;
protected
procedure CreateParams(var Params: TCreateParams); override;
...
procedure TMainForm.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
begin
ExStyle := ExStyle or WS_EX_APPWINDOW;
WndParent := GetDesktopWindow;
end;
end;
procedure TMainForm.WMSysCommand(var Message: TWMSysCommand);
begin
if Message.CmdType = SC_MINIMIZE then
ShowWindow(Handle, SW_MINIMIZE)
else
inherited;
end;
This works only when MainForm.Visible is set True design time.
In D2006, the taskbar button is owned by the TApplication window. Clicking on the Taskbar button displays the TApplication system menu, which is altered by the VCL to always remove the Maximize, Size, and Move menu items. When clicking on a TForm, on the other hand, the Form's system menu is displayed instead, which is altered by the VCL according to the Form's BorderStyle and BorderIcon properties. So you are really dealing with two separate menus for two separate windows.
In modern Delphi versions, with the new TApplication.ShowMainFormOnTaskbar property set to true, the taskbar is owned by TForm instead of TApplication, so clicking on the Taskbar button will display the Form's system menu instead of the the TApplication system menu. So in this case, you are really dealing with a single menu for a single window.

Control's OnExit eats up mouseup event for new control when showing another window

I found this question on Experts-Exchange.
Control's OnExit eats up mouseup event for new control when showing
another window
The problem can be replicated easily.
place 3 tedits on a form. write a showmessage('exit') in edit1's
onexit event run the program give edit1 focus use the mouse to give
edit3 focus, click ok to the showmessage observe how you can't write
anything in edit3 now, until you click with the mouse somewhere on the
form ! give edit2 focus, then use to the mouse to give edit3 focus
observe how you can type what you want in edit3 now !
So far I've established that the problem lies in the fact that edit3
doesn't receive a mouseup-message when the old controls onExit event
displays a window of any kind, i've tried it as well with showing a
form of my own in the onExit event, same result. In fact, windows is
under the impression that the mouse is held down over edit3 after
you've clicked Ok to the showmessage
I guess it's a bug in Delphi/Windows but how to work around it ? I
know i can force a WM_LBUTTONUP on edit3's onMouseDown event (since
its the last event called in the process) but that's more than
tedious, and not always applicable
I am trying to do something similiar:
In the onexit event I show a warningbox and then want to proceed
as normal - moving the focus to where the user in fact clicked.
Is that possible?
Once again PostMessage to the rescue! Defer your dialog just a little bit longer so that Windows can finish its focus change. Post yourself a message instead of showing the dialog directly:
const
WM_SHOWMYDIALOG = WM_APP + 321;
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
procedure Edit1Exit(Sender: TObject);
private
procedure WMSHOWMYDIALOG(var Message: TMessage); message WM_SHOWMYDIALOG;
end;
procedure TForm1.Edit1Exit(Sender: TObject);
begin
PostMessage(Self.Handle, WM_SHOWMYDIALOG, 0, 0);
end;
procedure TForm1.WMSHOWMYDIALOG(var Message: TMessage);
begin
ShowMessage('Nice one');
end;
And everything is fine :)
I'm not so sure that the reason of the behavior is an eaten mouse message. Anyway, either that's the case or not, when you activate a window in an OnExit event of a control, what you're doing is, changing the focus while the focus is changing. That's because, the window is activated before WM_SETFOCUS for the newly focused control returns. This is discouraged, the below quote is from 'Best practices' section of 'Win32 Activation and Focus', a blog entry on MSDN:
Avoid manually changing focus when getting and/or losing focus. This
usually proves to be error prone.
The fact that the controls are disabled during focus transfer due to the modal nature of the activated window certainly would not help. If you really must do this, an approach like Heinrich's answer would at least delay the launch of the window till the focus transfer completes.
In the onexit event I show a warningbox and then want to proceed as normal - moving the focus to where the user in fact clicked. Is that possible?
Yes, (Screen.)ActiveControl will always point to Edit3: before and after the call to ShowMessage:
procedure TForm1.Edit1Exit(Sender: TObject);
begin
ShowMessage('Exit');
PostMessage(ActiveControl.Handle, WM_LBUTTONUP, 0, 0);
end;
But this is just for completeness sake to your question! And it certainly is no tip nor advice! See Sertac's answer for the reason.

System Menu for Layered Windows?

We're having an issue with layered windows and system menus in Delphi 2009. That is, our layered windows (which have no border) have no system menu. When I say system menu, I am referring to the menu you get when clicking an application's icon, right clicking it's title-bar or (in Windows 7, with the addition of the shift key,) right clicking an application in the task-bar:
When you attempt to access the system menu, e.g. by right-clicking on the task-bar icon, of such a layered window, instead the layered window is risen. Why is this? Is there some sort of style to set, or some sort of event to handle?
Here's a hastily made demo showing the issue. It can really be reproduced with any form with a bsNone borderstyle, though.
http://ompldr.org/vODd5dw
You need to add back the WS_SYSMENU style which is removed with bsNone border style.
type
TLayeredForm = class(TForm)
procedure FormCreate(Sender: TObject);
protected
procedure CreateParams(var Params: TCreateParams); override;
end;
...
procedure TLayeredForm.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style := Params.Style or WS_SYSMENU;
end;

Windows 7 style Notifications Flyouts in Delphi

Regarding Notification Area recommendations by Microsoft, I'm looking for ideas or a Delphi component to implement Notification Area Flyouts.
The first "natural" idea is to use a standard Delphi form, but I'm facing two issues with it:
I can't get the form border behavior using the standard "BorderStyle" property. Tried to "mimic" the border using the GlassFrame property along with BorderStyle set to bsNone, but there's no GlassFrame when there's no border (at least, in Delphi 2007).
I can't figure out how to make the form close when the user clicks everywhere out of the form itself. Yesterday I was trying with different messages, but no one works as expected.
I will thank any clue or component to make it happen :)
Best regards.
jachguate.
ps. There's a related question in converting notification area icon to Program icon in Win7 (Delphi).
update[0]
I'm still looking for advise. #skamradt answer looks very good, but unfortunately doesn't work well in practice.
update[1]
Finally, The auto-close behavior is working with the WM_ACTIVATE message after a calling SetForegroundWindog to force flyout "activation"
begin
FlyoutForm.Show;
SetForegroundWindow(FlyoutForm.Handle);
end;
Now, I'm looking for advise to reach the border behavior and visual style, because the closest behavior is achieved with style as WS_POPUP or WS_DLGFRAME, while the closest visual goal is achieved setting style as WS_POPUP or WS_THICKFRAME.
I believe what your after is the following:
TForm1 = class(TForm)
:
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure WMActivate(Var msg:tMessage); message WM_ACTIVATE;
end;
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style := WS_POPUP or WS_THICKFRAME;
end;
procedure TForm4.WMActivate(var msg: tMessage);
begin
if Msg.WParam = WA_INACTIVE then
Hide; // or close
end;
This will give you a sizeable popup window with a glass frame. You can't move the window without additional programming, since the standard windows caption is missing. When another window gets focus, the FormDeactivate event gets fired...but only if you switch to another form in the same application. To handle it regardless of the application switched, use the message capture method.

Resources