How can I make a Form (ShowModal) with BorderStyle bsDialog. but one that could still be resized and have the close button (without the Icon,Minimize, Maximize)?
I do not need it to show the size grip.
Here is my solution which seems to work OK:
type
TForm2 = class(TForm)
procedure FormCreate(Sender: TObject);
private
protected
procedure CreateWnd; override;
procedure CreateParams(var Params: TCreateParams); override;
public
end;
var
Form2: TForm2;
implementation
{$R *.DFM}
procedure TForm2.FormCreate(Sender: TObject);
begin
BorderIcons := [biSystemMenu];
BorderStyle := bsSizeable;
AutoScroll := False;
end;
procedure TForm2.CreateWnd;
begin
inherited;
SendMessage(Handle, WM_SETICON, 1, 0);
end;
procedure TForm2.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.ExStyle := Params.ExStyle or WS_EX_DLGMODALFRAME or WS_EX_WINDOWEDGE;
end;
IMO, This cant be done with bsDialog but the above feels and looks just like a "bsDialog" which could be resized.
Set the BorderStyle to bsSizeToolWin.
Related
In a Delphi 10.4.2 32-bit VCL application on Windows 10, I am trying to customize the Hint Font.Size:
type
TExHint = class(THintWindow)
constructor Create(AOwner: TComponent); override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
constructor TExHint.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
with Canvas.Font do
begin
//Name := 'Verdana';
Size := 15;
//Style := [fsBold, fsItalic];
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
HintWindowClass := TExHint;
end;
But it does not work.
How can I customize the Hint Font.Size of my application?
It's much easier than this.
Just set the Screen.HintFont property:
procedure TForm.FormCreate(Sender: TObject);
begin
Screen.HintFont.Size := 20;
end;
or even
or
I want to make a custom control with a selectable border size. See the code below. The border is drawn in the non-client area and his width can be 0, 1 or 2 pixels. I've successfully done the border drawings in the WM_NCPAINT. The problem is that after I change the property that control the border size I don't know how to tell the system to recalculate the new dimensions of client and non-client areas. I've noticed that when I resize the window (with the mouse) the changes are applied, but I donn't know how to do that immediately after I change the border size.
SuperList.pas
unit SuperList;
interface
uses Windows, Controls, Graphics, Classes, Messages, SysUtils, StdCtrls, UxTheme;
type
TBorderType = (btNone, btSingle, btDouble);
TSuperList = class(TCustomControl)
private
HHig,HMidH,HMidL,HLow:TColor;
BCanvas: TCanvas;
FBorderSize: TBorderType;
procedure SetBorderSize(const Value:TBorderType);
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
procedure WMSize(var Message: TWMSize); message WM_SIZE;
procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
procedure WMNCPaint(var Message: TWMNCPaint); message WM_NCPAINT;
protected
procedure Paint; override;
procedure CreateParams(var Params: TCreateParams); override;
public
constructor Create(AOwner:TComponent); override;
published
property BorderType:TBorderType read FBorderSize write SetBorderSize default btDouble;
end;
implementation
constructor TSuperList.Create(AOwner:TComponent);
begin
inherited;
BCanvas:=TCanvas.Create;
FBorderSize:=btDouble;
HHig:=clWhite; HMidH:=clBtnFace; HMidL:=clGray; HLow:=cl3DDkShadow;
end;
procedure TSuperList.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style := Params.Style or WS_VSCROLL or WS_HSCROLL;
end;
procedure TSuperList.SetBorderSize(const Value:TBorderType);
begin
if Value<>FBorderSize then begin
FBorderSize:=Value;
// .... ?????? I think here must be done something...
Perform(WM_NCPAINT,1,0); // repainting the non-client area (I do not know how can I invalidate the non-client area differently)
Invalidate; // repainting the client area
// I've tried even with the... RedrawWindow(Handle,nil,0,RDW_FRAME or RDW_INVALIDATE or RDW_UPDATENOW or RDW_INTERNALPAINT);
end;
end;
procedure TSuperList.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
Message.Result:=1;
end;
procedure TSuperList.WMSize(var Message: TWMSize);
begin
inherited;
Perform(WM_NCPAINT,1,0);
end;
procedure TSuperList.WMNCCalcSize(var Message: TWMNCCalcSize);
begin
inherited;
if FBorderSize>btNone then
InflateRect(Message.CalcSize_Params^.rgrc0,-Integer(FBorderSize),-Integer(FBorderSize));
end;
procedure TSuperList.Paint;
begin
Canvas.Brush.Color:=clWhite;
Canvas.FillRect(ClientRect);
end;
procedure TSuperList.WMNCPaint(var Message: TWMNCPaint);
var DC: HDC;
R: TRect;
HS_Size,VS_Size:Integer;
HS_Vis,VS_Vis:Boolean;
begin
inherited;
Message.Result:=0;
if FBorderSize>btNone then
begin
DC:=GetWindowDC(Handle); if DC=0 then Exit;
BCanvas.Handle:=DC;
BCanvas.Pen.Color:=clNone;
BCanvas.Brush.Color:=clNone;
try
VS_Size:=GetSystemMetrics(SM_CXVSCROLL);
HS_Size:=GetSystemMetrics(SM_CYHSCROLL);
VS_Vis:=GetWindowLong(Handle,GWL_STYLE) and WS_VSCROLL <> 0;
HS_Vis:=GetWindowLong(Handle,GWL_STYLE) and WS_HSCROLL <> 0;
R:=ClientRect;
OffsetRect(R,Integer(FBorderSize),Integer(FBorderSize));
if VS_Vis and HS_Vis then begin
ExcludeClipRect(DC, R.Left, R.Top, R.Right, R.Bottom+HS_Size);
ExcludeClipRect(DC, R.Left, R.Top, R.Right+VS_Size, R.Bottom);
BCanvas.Brush.Color:=HMidH;
R.Right:=Width-Integer(FBorderSize); R.Left:=R.Right-VS_Size;
R.Bottom:=Height-Integer(FBorderSize); R.Top:=R.Bottom-HS_Size;
BCanvas.FillRect(R);
end else begin
if VS_Vis then Inc(R.Right,VS_Size);
if HS_Vis then Inc(R.Bottom,HS_Size);
ExcludeClipRect(DC, R.Left, R.Top, R.Right, R.Bottom);
end;
BCanvas.MoveTo(0,Height-1);
BCanvas.Pen.Color:=HMidL; BCanvas.LineTo(0,0); BCanvas.LineTo(Width-1,0);
if IsThemeActive then begin
BCanvas.Pen.Color:=HMidL;
BCanvas.LineTo(Width-1,Height-1);
BCanvas.LineTo(-1,Height-1);
end else begin
if FBorderSize=btDouble then begin
BCanvas.Pen.Color:=HHig;
BCanvas.LineTo(Width-1,Height-1);
BCanvas.LineTo(-1,Height-1);
end else begin
if VS_Vis then BCanvas.Pen.Color:=HHig else BCanvas.Pen.Color:=HMidL;
BCanvas.LineTo(Width-1,Height-1);
if HS_Vis then BCanvas.Pen.Color:=HHig else BCanvas.Pen.Color:=HMidL;
BCanvas.LineTo(-1,Height-1);
end;
end;
if FBorderSize=btDouble then begin
BCanvas.MoveTo(1,Height-2);
BCanvas.Pen.Color:=HLow; BCanvas.LineTo(1,1); BCanvas.LineTo(Width-2,1);
BCanvas.Pen.Color:=HMidH; BCanvas.LineTo(Width-2,Height-2); BCanvas.LineTo(0,Height-2);
end;
finally
ReleaseDC(Handle,DC);
end;
end;
end;
end.
Unit1.pas
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, SuperList, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
public
List: TSuperList;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
List:=TSuperList.Create(self);
List.Parent:=Form1;
List.Margins.Left:=20; List.Margins.Right:=20;
List.Margins.Top:=50; List.Margins.Bottom:=20;
List.AlignWithMargins:=true;
List.Align:=alClient;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
List.BorderType:=btNone;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
List.BorderType:=btSingle;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
List.BorderType:=btDouble;
end;
end.
Send a CM_BORDERCHANGED message:
Perform(CM_BORDERCHANGED, 0, 0);
This will fire the handler in TWinControl:
procedure TWinControl.CMBorderChanged(var Message: TMessage);
begin
inherited;
if HandleAllocated then
begin
SetWindowPos(Handle, 0, 0,0,0,0, SWP_NOACTIVATE or
SWP_NOZORDER or SWP_NOMOVE or SWP_NOSIZE or SWP_FRAMECHANGED);
if Visible then
Invalidate;
end;
end;
And from the documentation on SetWindowPos:
SWP_FRAMECHANGED: Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE is sent only when the window's size is being changed.
I have a graphical TCustomControl descendant component with a TScrollBar on it. The problem is that when I press the arrow key to move the cursor the whole canvas is painted in background color, including the region of the scroll bar, then the scroll bar is repainted and that makes scroll bar flicker. How can I solve this ?
Here is the code. There is no need install the component or to put something on the main form, just copy the code and assign TForm1.FormCreate event:
Unit1.pas
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, SuperList;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
List: TSuperList;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
List:=TSuperList.Create(self);
List.Top:=50; List.Left:=50;
List.Visible:=true;
List.Parent:=Form1;
end;
end.
SuperList.pas
unit SuperList;
interface
uses Windows, Controls, Graphics, Classes, Messages, SysUtils, StdCtrls, Forms;
type
TSuperList = class(TCustomControl)
public
DX,DY: integer;
ScrollBar: TScrollBar;
procedure Paint; override;
constructor Create(AOwner: TComponent); override;
procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
published
property OnMouseMove;
property OnKeyPress;
property OnKeyDown;
property Color default clWindow;
property TabStop default true;
property Align;
property DoubleBuffered default true;
property BevelEdges;
property BevelInner;
property BevelKind default bkFlat;
property BevelOuter;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Marus', [TSuperList]);
end;
procedure TSuperList.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
inherited;
Message.Result:= Message.Result or DLGC_WANTARROWS;
end;
procedure TSuperList.WMKeyDown(var Message: TWMKeyDown);
begin
if Message.CharCode=VK_LEFT then begin dec(DX,3); Invalidate; exit; end;
if Message.CharCode=VK_RIGHT then begin inc(DX,3); Invalidate; exit; end;
if Message.CharCode=VK_UP then begin dec(DY,3); Invalidate; exit; end;
if Message.CharCode=VK_DOWN then begin inc(DY,3); Invalidate; exit; end;
inherited;
end;
procedure TSuperList.WMLButtonDown(var Message: TWMLButtonDown);
begin
DX:=Message.XPos;
DY:=Message.YPos;
SetFocus;
Invalidate;
inherited;
end;
constructor TSuperList.Create(AOwner: TComponent);
begin
inherited;
DoubleBuffered:=true;
TabStop:=true;
Color:=clNone; Color:=clWindow;
BevelKind:=bkFlat;
Width:=200;
Height:=100;
DX:=5; DY:=50;
ScrollBar:=TScrollBar.Create(self);
ScrollBar.Kind:=sbVertical;
ScrollBar.TabStop:=false;
ScrollBar.Align:=alRight;
ScrollBar.Visible:=true;
ScrollBar.Parent:=self;
end;
procedure TSuperList.Paint;
begin
Canvas.Brush.Color:=Color;
Canvas.FillRect(Canvas.ClipRect);
Canvas.TextOut(10,10,'Press arrow keys !');
Canvas.Brush.Color:=clRed;
Canvas.Pen.Color:=clBlue;
Canvas.Rectangle(DX,DY,DX+30,DY+20);
end;
end.
I think the first thing that I would do is remove that scroll bar control. Windows come with ready made scroll bars. You just need to enable them.
So, start by removing ScrollBar from the component. Then add a CreateParams override:
procedure CreateParams(var Params: TCreateParams); override;
Implement it like this:
procedure TSuperList.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style := Params.Style or WS_VSCROLL;
end;
Yippee, your control now has a scroll bar.
Next you need to add a handler for WM_VSCROLL:
procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
And that's implemented like this:
procedure TSuperList.WMVScroll(var Message: TWMVScroll);
begin
case Message.ScrollCode of
SB_LINEUP:
begin
dec(DY, 3);
Invalidate;
end;
SB_LINEDOWN:
begin
inc(DY, 3);
Invalidate;
end;
...
end;
end;
You'll need to fill out the rest of the scroll codes.
I would also suggest that you do not set DoubleBuffered in the constructor of your component. Let the user set that if they wish. There's no reason for your control to require double buffering.
I am trying to create a THintWindow and place a TButton or a TFrame on it. here is my code:
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
HintWindow: THintWindow;
public
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
HintWindow := THintWindow.Create(Self);
HintWindow.Color := clInfoBk;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
P: TPoint;
R: TRect;
Control: TControl;
begin
Control := Button1;
P := Control.ClientToScreen(Point(0, Control.Height));
R := Rect(P.X, P.Y, P.x + 100, P.Y + 100);
with TButton.Create(HintWindow) do
begin
Parent := HintWindow;
Caption := 'My Button';
end;
HintWindow.ActivateHint(R, 'My Hint');
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
HintWindow.ReleaseHandle;
end;
The Hint window is shown but I don't see the TButton. it seems that there are no child windows inside the Hint window (I tested with Spy++ for "first child").
I also tried to subclass THintWindow with new CreateParams ie:
procedure TMyHintWindow.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style := Params.Style or WS_CLIPCHILDREN;
Params.ExStyle := Params.ExStyle or WS_EX_CONTROLPARENT;
end;
When I create a TFrame as child on the Hint window, Spy++ shows that there is a child on the hint window but I cant see it (even after I force it to be visible).
Any feed-backs on this?
Don't ask me why, but you can make this work in old versions of Delphi by setting the ParentWindow to Application.Handle immediately after you create the THintWindow instance:
HintWindow := THintWindow.Create(Self);
HintWindow.ParentWindow := Application.Handle;
This answer was inspired by the modern versions of the Delphi VCL source.
How can I reserve one edge of the desktop with Delphi? Something like the Vista Sidebar does.
What you want is called an application desktop toolbar
You must use the TAppBarData Object declared in the ShellAPI unit and the SHAppBarMessage function.
See this simple example.
type
TApplicationTaskBar = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
procedure CreateParams(var Params: TCreateParams); override;
public
{ Public declarations }
end;
var
ApplicationTaskBar: TApplicationTaskBar;
implementation
{$R *.dfm}
Uses
ShellAPI;
procedure TApplicationTaskBar.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.ExStyle := Params.ExStyle or WS_EX_TOOLWINDOW;
Params.Style := (Params.Style OR WS_POPUP) AND (NOT WS_DLGFRAME); //Remove title Bar
end;
procedure TApplicationTaskBar.FormCreate(Sender: TObject);
var
MyTaskBar : TAppBarData;
begin
GlassFrame.Enabled :=True;//Activate Glass , optional
GlassFrame.SheetOfGlass :=True;//optional
Left:=0;
Top :=0;
Width := 300;
Height:= Screen.Height;
FillChar(MyTaskBar, SizeOf(TAppBarData), 0);
MyTaskBar.cbSize := SizeOf(TAppBarData);
MyTaskBar.hWnd := Handle;
MyTaskBar.uCallbackMessage := WM_USER+777; //Define my own Mesaage
MyTaskBar.uEdge := ABE_LEFT;
MyTaskBar.rc := Rect(0, 0, Width, Height);
SHAppBarMessage(ABM_NEW, MyTaskBar);
SHAppBarMessage(ABM_ACTIVATE, MyTaskBar);
SHAppBarMessage(ABM_SETPOS, MyTaskBar);
Application.ProcessMessages;
end;
procedure TApplicationTaskBar.FormDestroy(Sender: TObject);
var
MyTaskBar : TAppBarData;
begin
FillChar(MyTaskBar, SizeOf(TAppBarData), 0);
MyTaskBar.cbSize := SizeOf(TAppBarData);
MyTaskBar.hWnd := Self.Handle;
SHAppBarMessage(ABM_Remove, MyTaskBar);
end;
Check this links
SHAppBarMessage
ABM_ACTIVATE
ABM_SETPOS
ABM_NEW
Bye.