Delphi Borderless and Captionless Application - delphi

I am willing to designed one Application in Delphi XE2 Borderlessly and Captionlessly by using the following code :
BorderIcons = []
BorderStyle = bsNone
But the problem is that there is no Menu on Right Click on the Application on Taskbar just like in the above image. Then I have tried the following codes on FormShow event, but there is also another problem. One Border is created on Left side and Left-Botton side. The codes are :
procedure TForm1.FormShow(Sender: TObject);
var
r: TRect;
begin
r := ClientRect;
OffsetRect(r, 0, GetSystemMetrics(SM_CYCAPTION));
OffsetRect(r, GetSystemMetrics(SM_CXFRAME), GetSystemMetrics(SM_CYFRAME));
SetWindowRgn(Handle,
CreateRectRgn(
r.Left, r.Top,
ClientWidth + r.Left, ClientHeight + r.Top), True);
end;
Please help me.

The simple solution is not to remove the system menu in the first place. Note that the system menu is the official name for the menu that is missing in your app.
Make your .dfm file look like this:
BorderIcons = [biSystemMenu]
BorderStyle = bsNone
Get rid of that FormShow code–it's not needed.
OK, it looks like a stray bit of code from my experimentation was confounding me. Here's what works.
Do exactly what you originally did in your .dfm form:
BorderIcons = []
BorderStyle = bsNone
Then add back the system menu using CreateParams:
TForm1 = class(TForm)
protected
procedure CreateParams(var Params: TCreateParams); override;
end;
...
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style := Params.Style or WS_SYSMENU;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
BorderStyle := bsNone;
SetWindowLong(Handle, GWL_STYLE,
WS_POPUP or WS_CLIPSIBLINGS or WS_CLIPCHILDREN or WS_SYSMENU);
SetWindowLong(Handle, GWL_EXSTYLE, WS_EX_CONTROLPARENT or WS_EX_APPWINDOW);
end;
You don't need the code in the OnShow handler with this solution.
The above code can be called any time (not just in OnCreate), it can be used to alter the behavior of a running form for instance (just include WS_VISIBLE to window styles if the form is already visible).
If you want the behavior to be in effect for the life time of the form, it's better to set the flags in an overriden CreateParams (where form styles are applied by VCL). This will also take possible recreation of the form into account. Don't set any form property from the OI for this solution, all of the flags are explicitly set in the code:
type
TForm1 = class(TForm)
..
protected
procedure CreateParams(var Params: TCreateParams); override;
..
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style := WS_POPUP or WS_CLIPSIBLINGS or WS_CLIPCHILDREN or WS_SYSMENU;
Params.ExStyle := WS_EX_CONTROLPARENT or WS_EX_APPWINDOW;
end;

You can have a window that appears not to have a caption bar, or a standard caption, by simply taking over the painting of the entire window:
Create a new empty application. Use this code for your form:
unit ncUnit1;
interface
// XE2 uses clause
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
// If you're not using XE2 take out the prefixes (WinApi, Vcl, System, etc)
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
protected
procedure WMNCPaint(var Message: TWMNCPaint); message WM_NCPAINT;
procedure SolidColorNcPaint(solidColor,frameColor:TColor);
procedure Resizing(State: TWindowState); override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TForm1 }
procedure TForm1.WMNCPaint(var Message: TWMNCPaint);
begin
SolidColorNcPaint(clBtnFace,clBtnFace);
end;
procedure TForm1.Resizing(State: TWindowState);
begin
inherited;
PostMessage(Self.Handle,WM_NCPAINT,0,0); {force initial paint}
end;
procedure TForm1.SolidColorNcPaint(solidColor,frameColor:TColor);
var
aBorder:Integer;
ahdc : HDC;
begin
aBorder := GetSystemMetrics(SM_CYSIZEFRAME);
canvas.Lock;
ahdc := GetWindowDC(Handle);
canvas.Handle := ahdc;
ExcludeClipRect(canvas.Handle, aBorder, 0, Width-aBorder, Height - aBorder) ;
Canvas.Brush.Style := bsSolid;
Canvas.Brush.Color := frameColor;
Canvas.Pen.Color := solidColor;
Canvas.Rectangle( 0,0, Width,Height);
ReleaseDC(Self.Handle, ahdc);
canvas.Handle := 0;
canvas.Unlock;
end;
end.
What you see above is only enough code to redraw a solid color over the non-client area of the window, not to remove it completely. Depending on the style of custom window you want, you should render whatever you want on the form. If you don't want a Close button then remove the close button, and if you do not want the resizing behaviour, remove the resizing behaviour. If you set the FormStyle=fsDialog plus the above code, you would get a window that has a complete custom drawn title area (which you can put whatever you want into). If you actually don't want the title area to exist at all, you can modify the above code to achieve that too.

You could do what David says and/or also take a look at:
SetWindowRgn API.
If you use just the SetWindowRgn you don't have to remove the TForm's border, just make a rectangle that starts below it.

Related

Create a borderless form without losing Windows commands

I've changed my form to a borderless form, I just changed the BorderStyle property to bsNone, but now my application loses the windows anchor and some commands like
WIN + ↑ (Align the form Client) WIN + ↓ (Minimize the form) WIN + →(Align the form Right) WIN + ←(Align the form Left)
I've tried to set BorderStyle: bsSizeable and use the below code inside of the FormCreate, but this does not worked:
procedure TfrmBase.FormCreate(Sender: TObject);
begin
SetWindowLong(Handle
,GWL_STYLE
,GetWindowLong(Handle, GWL_STYLE)
AND (NOT WS_CAPTION)
AND (NOT WS_THICKFRAME)
);
Refresh;
FormColor := oLauncher.oCor;
end;
This results:
The image above is what I want, but the Windows commands that I already have mentioned don't work
Have any way to set the BorderStyle: bsNone and don't lose these commands?
EDITED
If I use the WS_THICKFRAME my form returns a little top border and the windows commands works well, but I don't want that top border.
EDITED 2
I got very close to the expected result, but have a little problem yet...
I put this on my FormCreate
SetWindowLong(Handle
,GWL_STYLE
,GetWindowLong(Handle, GWL_STYLE)
AND (NOT WS_CAPTION)
);
And I create the method
private
procedure WmNCCalcSize(var Msg: TWMNCCalcSize); message WM_NCCALCSIZE;
and then
procedure TfrmBase.WmNCCalcSize(var Msg: TWMNCCalcSize);
begin
inherited;
if Msg.CalcValidRects then
begin
InflateRect(Msg.CalcSize_Params.rgrc[0], 0, 6);
Msg.Result := 0;
end;
end;
I got this method here
Now the border has disappeared, but when my Form loses the focus, the top / bottom border is shown again....
How can I avoid this?
SOLVED
I left the border as BorderStyle: bsSizeable, then I did it:
private
procedure WmNCCalcSize(var Msg: TWMNCCalcSize); message WM_NCCALCSIZE;
[...]
procedure TfrmBase.WmNCCalcSize(var Msg: TWMNCCalcSize);
var
R: TRect;
begin
if not Msg.CalcValidRects then
R := PRect(Msg.CalcSize_Params)^;
inherited;
if Msg.CalcValidRects then
Msg.CalcSize_Params.rgrc0 := Msg.CalcSize_Params.rgrc1
else
PRect(Msg.CalcSize_Params)^ := R;
Msg.Result := 0;
end;
procedure TfrmBase.FormCreate(Sender: TObject);
begin
BorderStyle := bsNone;
SetWindowLong(Handle
,GWL_STYLE
,WS_CLIPCHILDREN or WS_OVERLAPPEDWINDOW
);
end;
procedure TfrmBase.FormShow(Sender: TObject);
begin
Width := (Width - 1);
end;
Solution at GitHUB
I've create a repository here
Some of the commands you refer to are system commands related to sizing of the window. That requires the thick frame, without it "WIN + right" and "WIN + left" won't work. Additionally you need the minimize box and the maximize box for the WIN + up/down commands to work.
Best is to start from scratch and include the styles you need, otherwise VCL might interfere. If there's a possibility of your form to be recreated, put styling in a CreateWnd override.
procedure TForm1.FormCreate(Sender: TObject);
begin
BorderStyle := bsNone;
SetWindowLong(Handle, GWL_STYLE, WS_CLIPCHILDREN or WS_OVERLAPPEDWINDOW);
end;
Then there's the frame that you don't want. In an edit in the question you inflate the client rectangle to get rid of it. Don't guess the frame width/height, do it like the below.
procedure TForm1.WMNCCalcSize(var Message: TWMNCCalcSize);
var
R: TRect;
begin
if not Message.CalcValidRects then
R := PRect(Message.CalcSize_Params)^;
inherited;
if Message.CalcValidRects then
Message.CalcSize_Params.rgrc0 := Message.CalcSize_Params.rgrc1
else
PRect(Message.CalcSize_Params)^ := R;
Message.Result := 0;
end;
Reading the documentation for the message is mandatory at this point, the parameters have different meanings at different stages, etc..
The above leaves a window without any non-client area at all. The client rectangle is equal to the window rectangle. Although the caption is not visible, you can activate the system menu by pressing Alt+Space. The problem is, the system insists on drawing activation state. Now it draws a frame in the client area!!
Get rid of it by intercepting WM_NCACTIVATE, you also need it to draw your title according to the activation status:
procedure TForm1.WMNCActivate(var Message: TWMNCActivate);
begin
if Message.Active then
// draw active caption
else
// draw incactive caption
// don't call inherited
end;
You might have to deal with some glitches, messing up with the window has consequences. In my test, the minimized form does not have an associated icon in the alt+tab dialog for instance.
Below is my test unit in full.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
protected
procedure WMNCActivate(var Message: TWMNCActivate); message WM_NCACTIVATE;
procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
BorderStyle := bsNone;
SetWindowLong(Handle, GWL_STYLE, WS_CLIPCHILDREN or WS_OVERLAPPEDWINDOW);
end;
procedure TForm1.WMNCActivate(var Message: TWMNCActivate);
begin
if Message.Active then
// draw active caption
else
// draw incactive caption
// don't call inherited
end;
procedure TForm1.WMNCCalcSize(var Message: TWMNCCalcSize);
var
R: TRect;
begin
if not Message.CalcValidRects then
R := PRect(Message.CalcSize_Params)^;
inherited;
if Message.CalcValidRects then
Message.CalcSize_Params.rgrc0 := Message.CalcSize_Params.rgrc1
else
PRect(Message.CalcSize_Params)^ := R;
Message.Result := 0;
end;
end.

How to remove wsNormal fullscreen form title bar content offset?

When maximizing a Form via WindowState:=wsMaximized, the title bar looks like this:
When setting the form to WindowState:=wsNormal and setting the form size manualy to a fullscreen state, the content of the frame is identical but the title bar is slightly moved.
The wsNormal form Rect on a 800*600 screen to simulate a wsMaxed form is TRect(-8,-8,808,608).
(See this question why the difference of the size is necessary)
My Question:
How can i fix the moved title bar content for the wsNormal window, so it would look correct like the following mock up?
Simple example form with one button, which reproduces both form states.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private-Deklarationen }
FState: Integer;
public
{ Public-Deklarationen }
procedure WMGetMinMaxInfo(var Message: TWMGetMinMaxInfo); message WM_GETMINMAXINFO;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
LBRect: TRect;
begin
LBRect := Screen.Monitors[0].BoundsRect;
case FState of
0:
begin
WindowState := wsMaximized;
end;
1:
begin
WindowState := wsNormal;
LBRect := Screen.Monitors[0].WorkareaRect;
LBRect.Inflate(8,8); //offset 8 for a form with bsSizeable
BoundsRect := LBRect;
end;
end;
Inc(FState);
FState := FState mod 2;
end;
procedure TForm1.WMGetMinMaxInfo(var Message: TWMGetMinMaxInfo);
begin
//slightly smaller MaxTrackSize would prevent the wsNormal form the fully cover the screen on the right side
Message.MinMaxInfo.ptMaxTrackSize := Message.MinMaxInfo.ptMaxSize;
end;
end.
Edit1:
To clarify why this might be usefull:
We have a multi-monitor set up with identical resolution screens and want to span the form over this verly large "virtual screen. The desktop can not be unified to one large screen via e.g. AMDs Virtual Destkop settings.
The problem occurs because the title bar rect of a maximized Form has a slightly smaller height than a non-maximized form title bar.
Also, there is a need for the negative positions and enlarged size, caused by the (backwards compatible) way windows handles the border calculations and positioning. The actual offset/enlargement comes from the chosen borderstyle.
After some research, i found a way to reach the Goal for the described Scenario with minimal effort. This does not answer the question itself, it will only reach the goal.
When a form is created and also when it is maximized (by either the user or setting the forms WindowState to wsMaximized), the window message WM_GETMINMAXINFO gets send.
With sending WM_GETMINMAXINFO, the Form will be asked about its desired position and size in a maximized situation.
Here, we can override windows default positioning and e.g. span a maximized view over multiple screens by setting the values accordingly.
When analyzing the default values sent via WM_GETMINMAXINFO message, the values will have an offset of 8 in each dimension.
The offset was necessary on older Windows OS to tell the window manager to hide the borderstyle outside of the current screens view, and would also result in edge bleeding on multi-monitor setup.
The edge bleeding also occurs nowadays, when the WindowState is is set to wsNormal.
The Offset will also tell Windows Window Manager to reduce the title bars size when it is docked to the top AND to render the title bar content correctly (which is the goal we searched for).
See the following example:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private-Deklarationen }
FState: Integer;
public
{ Public-Deklarationen }
procedure WMGetMinMaxInfo(var Message: TWMGetMinMaxInfo); message WM_GETMINMAXINFO;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
case FState of
0:
begin
WindowState := wsMaximized;
end;
1:
begin
WindowState := wsNormal;
BoundsRect := TRect.Create(100,-8,300,192);
end;
2:
begin
WindowState := wsMaximized;
end;
3:
begin
WindowState := wsNormal;
BoundsRect := TRect.Create(100,0,300,200);
end;
4:
begin
WindowState := wsMaximized;
end;
5:
begin
WindowState := wsNormal;
BoundsRect := TRect.Create(100,100,300,300);
end;
end;
Inc(FState);
FState := FState mod 6;
end;
procedure TForm1.WMGetMinMaxInfo(var Message: TWMGetMinMaxInfo);
begin
case FState of
0, 1:
begin
Message.MinMaxInfo.ptMaxPosition.SetLocation(100,-8);
Message.MinMaxInfo.ptMaxSize.SetLocation(200,200);
Message.MinMaxInfo.ptMaxTrackSize := Message.MinMaxInfo.ptMaxSize;
inherited;
end;
2, 3:
begin
Message.MinMaxInfo.ptMaxPosition.SetLocation(100,0);
Message.MinMaxInfo.ptMaxSize.SetLocation(200,200);
Message.MinMaxInfo.ptMaxTrackSize := Message.MinMaxInfo.ptMaxSize;
inherited;
end;
4, 5:
begin
Message.MinMaxInfo.ptMaxPosition.SetLocation(100,100);
Message.MinMaxInfo.ptMaxSize.SetLocation(200,200);
Message.MinMaxInfo.ptMaxTrackSize := Message.MinMaxInfo.ptMaxSize;
inherited;
end;
end;
end;
end.
FYI: var Message: TWMGetMinMaxInfo seems to be persistent for the forms lifetime.
On state 0, the form is slightly moved into the top, which tells the window manager to render this forms title slightly smaller. This is the default behaviour for maximized forms and the optical result i searched for.
State 1 will show the problem i had, and the other states will show you that positioning a maximized form anywhere is possible AND that the title bar will have the default size when it is not cutting a screens edge.
If you want to know what offsets to pick for your TForm, you can ask windows by using AdjustWindowRectEx with your chosen styles and main menu informations. (See: VCL.Forms.pas, TCustomForm.GetClientRect implementation)
The standard caption of an API window is fixed, you cannot do anything to alter its position or size.

Too much calls paint method

I created a test component
unit Control1;
interface
uses
System.SysUtils, System.Classes, FMX.Types, FMX.Controls;
type
TTestComp = class(TControl)
private
i: integer;
protected
procedure Paint; override;
public
constructor Create(AOwner: TComponent); override;
published
property Width;
property Height;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Test', [TTestComp]);
end;
{ TTestComp }
constructor TTestComp.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
i := 0;
end;
procedure TTestComp.Paint;
begin
inherited;
inc(i);
canvas.BeginScene;
canvas.Fill.Color := $FF000000;
canvas.FillRect(localrect, 0, 0, [], 1);
canvas.Fill.Color := $FFFFFFFF;
canvas.FillText(localrect, IntToStr(i), false, 1, [], TTextAlign.Center);
canvas.EndScene;
end;
end.
Here is the problem:
Component is drawing in top-left corner
Too much paint method calls when resizing form.
Just resized form.
I have a lot of components, built according to this principle. And when I change the size of the form, they begin to lag (Low FPS).
Standard components (TButton and etc.) work fine
1.Component is drawing in top-left corner
Your component is drawing exactly where it is placed (Position property). If you don't assign any values to Position.X and Position.Y the default values 0 is used for both.
2.Too much paint method calls when resizing form.
When you resize the form all components are re-painted, also f.ex. buttons. In a test with 81 of your controls I did not realize any lagging (but I assume your actual controls do some more painting than this example control).

Delphi Panels and custom component Z order issue

I'm working on a personal project and I have been running into lots of issues lately resulting in many questions, but hey, thats what SO is for right? ;)
Anyhow I tried making a transparent TPanel which I achieved by making a custom component.
The app im making is about world of warcraft and I made a talent calculator like on the official website but in a windows application.
Talents are spells/skills and each talent has information which is displayed in a tooltip.
So I have a tooltip with info,
I have a grid with talents and when I hover on a talent I want to see the info.
Besides that, I want the info to be shown near the position of the cursor.
Almost works. The positioning works, it shows the correct info BUT! here is the problem.
Take a look at this image:
The black semi-transparent panel is my tooltip.
You see the talents (that little 4x6 grid) those are located in a dark grey panel called pnlTalents
The parent of that panel is the lighter grey panel that covers the entire form called Panel1.
The tooltip called TooltipTalent also has the parent Panel1.
I have tried sending pnlTalents to the back and bring TooltipTalent to the front but this made no difference at all.
I even tried TooltipTalent.BringToFront; the moment the position is changed.
Notice how the tooltip has no problem being on top of that darker grey panel at the top of the screen with the speedbuttons.
I compared both panels (the one at the top and the one with the talents) and found no difference in the properties.
I am seriously running out of ideas here. I have no clue what is causing it and how I can solve this problem.
As last resort I tried dropping another transparent panel that covers the entire form to see if that would help but the problem still persisted.
I could also post the code of my custom component but that would be a lot.
If you want to see the code let me know and i'll find a way to show it :)
Can anyone help me on this?
Kind regards
procedure TMyPanel.CreateParams(var params: TCreateParams);
begin
params.ExStyle := params.ExStyle or WS_EX_TRANSPARENT or WS_EX_TOPMOST ;
inherited CreateParams(params);
end;
With a Quickhackcode I get this result
Just as example, Image1 contains a Semitransparent png:
unit Unit4;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, dxGDIPlusClasses;
type
TMyPanel=Class(TPanel)
procedure CreateParams(var params: TCreateParams); override;
procedure WMEraseBkGnd(var msg: TWMEraseBkGnd); message WM_ERASEBKGND;
End;
TForm4 = class(TForm)
Panel1: TPanel;
Button1: TButton;
Image1: TImage;
Button2: TButton;
CheckBox1: TCheckBox;
Panel2: TPanel;
Button3: TButton;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private-Deklarationen }
Fmp:TMyPanel;
fisInPaint:Boolean;
public
{ Public-Deklarationen }
end;
var
Form4: TForm4;
implementation
{$R *.dfm}
{ TMyPanel }
procedure TMyPanel.CreateParams(var params: TCreateParams);
begin
params.ExStyle := params.ExStyle or WS_EX_TRANSPARENT or WS_EX_TOPMOST ;
inherited CreateParams(params);
end;
procedure TMyPanel.WMEraseBkGnd(var msg: TWMEraseBkGnd);
begin
SetBkMode (msg.DC, TRANSPARENT);
msg.result := 1;
end;
procedure TForm4.Button1Click(Sender: TObject);
begin
Fmp := TMyPanel.Create(self);
With Fmp do
begin
Parent := self;
left:= Panel1.Left -100;
top:= Panel1.top -100;
width := 300;
Height := 300;
end;
image1.Parent := Fmp;
Image1.Align := alClient;
Image1.Stretch := true;
Fmp.BringToFront;
Label1.Parent := FMP;
label1.Transparent := true;
Label1.Left := 100;
Label1.Left := 100;
end;
procedure TForm4.Button3Click(Sender: TObject);
begin
Fmp.Left := fmp.Left + 10;
end;
end.
Can't reproduce problem with XP either:

Distribute keypresses between parent form and child control

Sometimes a keystroke on a form can have different recipents, depending on the state of the application. See the following sample:
unit Unit1;
interface
uses
Windows,
Messages,
SysUtils,
Variants,
Classes,
Graphics,
Controls,
Forms,
Dialogs,
ComCtrls,
Buttons;
type
TForm1 = class(TForm)
private
ListView1: TListView;
ButtonOK: TBitBtn;
ButtonCancel: TBitBtn;
procedure ButtonClick(Sender: TObject);
public
constructor Create(AOwner: TComponent); override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
constructor TForm1.Create(AOwner: TComponent);
begin
inherited CreateNew(AOwner);
ClientWidth := 300;
ClientHeight := 240;
ListView1 := TListView.Create(Self);
ListView1.Name := 'ListView1';
ListView1.Parent := Self;
ListView1.Height := 200;
ListView1.Align := alTop;
ListView1.AddItem('aaaaa', nil);
ListView1.AddItem('bbbbb', nil);
ListView1.AddItem('ccccc', nil);
ButtonOK := TBitBtn.Create(Self);
ButtonOK.Parent := Self;
ButtonOK.Left := 8;
ButtonOK.Top := 208;
ButtonOK.Kind := bkOK;
ButtonOK.OnClick := ButtonClick;
ButtonCancel := TBitBtn.Create(Self);
ButtonCancel.Parent := Self;
ButtonCancel.Left := 90;
ButtonCancel.Top := 208;
ButtonCancel.Kind := bkCancel;
ButtonCancel.OnClick := ButtonClick;
end;
procedure TForm1.ButtonClick(Sender: TObject);
begin
ShowMessage((Sender as TBitBtn).Caption);
Application.Terminate;
end;
end.
(To run this, create a standard VCL app and replace the contents of Unit1.pas with the above.)
If one starts the app and presses Enter or Esc, the appropriate button are "clicked". However when one starts editing the listview (by clicking one and a half time on an item) Enter and Esc should accept or cancel the editing which they don't - they still "click" the buttons.
Similar scenarios exist if one has actions with shortcuts F2 or F4 on a form containing a cxGrid, which by default uses these shortcuts to start edit mode or drop down combobox editors.
Do you have an idea how I can continue do use the comfort of TButton.Default/Cancel and actions, while not having to reimplement the key handling of all the components I use?
I guess you have bad luck with the controls you use. TMemo handles it correctly, but indeed an editable TListView does not. The problem seems to originate from win32 rather then the VCL wrapper around it. So it eems that you have to reimplement the key handling on TListView if you do not like its current behavior.
procedure WMGetDlgCode(var Message: TMessage); message WM_GETDLGCODE;
procedure TMyListView.WMGetDlgCode(var Message: TMessage);
begin
inherited;
if IsEditing then
Message.Result := Message.Result or DLGC_WANTALLKEYS;
end;
Since all controls behave different and it is the controls themselves that decide which keys they are interested in, I can't see how you could fix it without having to change unwanted behavior.

Resources