How can I control the border size of a window?
I've got some forms that use absolute positioning and the extra pixels that Win7 adds into the "handles" to enhance translucency are screwing the controls up. I'd like to take them back. I tried using code in one of the answers here:
Can you make a Borderless Application Main Window in Windows, without WS_POPUP style?
Specifically the answer that GolezTrol provided utilizing an override in ShowFo开发者_如何学JAVArm with a call to SetWindowRgn. The code's behavior is a bit different under W7 than XP, and in any case I can't get the effect I'm looking for.
For a standard TForm in XP, the quantity Width-ClientWidth = 8, and in Win7, it's 16. I'd like those 8 pixels back. I'd like the height pixels back too while I'm at it, though the width is more important.
I think it is very bad practice to rely on the border width of a form, especially since the end-user can change this in the control panel! The ClientWidth
property is there for you to use instead of Width
. Set the former to whatever you like, and the latter will be computed.
You can see if the WinAPI function SystemParametersInfo will help, with the flag SPI_SETNONCLIENTMETRICS
. You can set various parts of the non-client area of all windows with values provided in the NONCLIENTMETRICS
record (structure in C).
Note that this is a global setting, and therefore won't affect just your windows but all windows on the system. It's most likely a very bad solution.
I implemented a control that emulates the border-resize behaviour, in Windows. Put this on your main form, align it to alClient, or use the anchor properties (anchors are probably better) and change your main form's border style to bsNone but do NOT do the hacks referenced in the other question you linked to.
unit ResizeBorderControlUnit;
// TResizeBorderControl:
//
// This is a subclass of TShape made to handle the border resize
// logic for a main form with border style bsNone. I wanted to
// make the form resizeable (think of a post-it note app) but I didn't
// want any Windows non-client painting or non-client manipulation logic.
//
// Written by Warren Postma
interface
uses Windows,
Messages,
Controls,
Forms,
ExtCtrls,
Classes,
SysUtils;
const
// if this value is too small, resizing gets too tricky.
MinSideMargin = 4;
MinBottomMargin = 4;
//Perform(wm_SysCommand, sc_DragMove, 0) etc:
sc_DragMove = $f012;
sc_Leftsize = $f001;
sc_Rightsize = $f002;
sc_Upsize = $f003;
sc_UpLeftsize = $f004;
sc_UpRightsize = $f005;
sc_Dnsize = $f006;
sc_DnLeftsize = $f007;
sc_DnRightsize = $f008;
type
TResizeBorderControl = class(TShape)
private
// FParentFormResize:TForm;
FSidesResizeHeight: Integer;
FBottomResizeHeight: Integer;
FBorderResizeFlag:Integer;
FAutoSize: Boolean;
FAutoSizeStartingAtTop: Integer; // currently active state.
procedure SetBottomResizeHeight(const Value: Integer);
procedure SetSidesResizeHeight(const Value: Integer);
procedure SetAutoSize(const Value: Boolean);
procedure SetAutoSizeStartingAtTop(const Value: Integer);
protected
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE; //override;
procedure CheckPosition(X, Y: Integer);
function MainFormMaximized:Boolean;
public
constructor Create(AOwner:TComponent); override;
published
property AutoSize:Boolean read FAutoSize write SetAutoSize;
property AutoSizeStartingAtTop:Integer read FAutoSizeStartingAtTop write SetAutoSizeStartingAtTop;
property BottomResizeHeight:Integer read FBottomResizeHeight write SetBottomResizeHeight;
property SidesResizeHeight:Integer read FSidesResizeHeight write SetSidesResizeHeight;
end;
implementation
// TResizeBorderControl
procedure TResizeBorderControl.CMMouseLeave(var Message: TMessage);
begin
Parent.Perform(CM_MOUSELEAVE, 0, Longint(Self));
if (Message.LParam = 0) then
begin
if Assigned(OnMouseLeave) then
OnMouseLeave(Self);
if ShowHint and not (csDesigning in ComponentState) then
if CustomHint <> nil then
CustomHint.HideHint(Self);
end;
FBorderResizeFlag := 0; // reset any active state bits.
Self.Cursor := crDefault;
end;
constructor TResizeBorderControl.Create(AOwner: TComponent);
begin
inherited;
Brush.Color := $202020;
end;
function TResizeBorderControl.MainFormMaximized: Boolean;
var
fm:TCustomForm;
begin
result := true;
fm := GetParentForm(Self);
if Assigned(fm) then
result := (fm.WindowState=wsMaximized);
end;
procedure TResizeBorderControl.MouseDown(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure DoResize(aType:Integer);
var
pw:TCustomForm;
begin
ReleaseCapture;
pw := Forms.GetParentForm(Self);
if Assigned(pw) then begin
ReleaseCapture;
pw.Perform(WM_syscommand, aType, 0);
end;
end;
begin
inherited;
if csDesigning in ComponentState then exit;
if MainFormMaximized then exit;
if (FBorderResizeFlag=0) then begin
CheckPosition(X, Y);
end;
if FBorderResizeFlag =1 then
DoResize( sc_Leftsize)
else if FBorderResizeFlag =2 then
DoResize( sc_Rightsize)
else if FBorderResizeFlag =4 then
DoResize( sc_Dnsize )
else if FBorderResizeFlag =5 then
DoResize( sc_DnLeftsize)
else if FBorderResizeFlag =6 then
DoResize( sc_DnRightsize)
else if FBorderResizeFlag =8 then
DoResize( sc_Upsize)
else
Self.Cursor := crDefault;
//FBorderResizeFlag := 0; // nope. second time mouse down without a move would glitch us.
end;
procedure TResizeBorderControl.MouseMove(Shift: TShiftState; X,
Y: Integer);
begin
if MainFormMaximized then exit;
CheckPosition(X,Y);
end;
procedure TResizeBorderControl.CheckPosition(X, Y: Integer);
var
SideMargin,BottomMargin:Integer;
begin
if csDesigning in ComponentState then begin
inherited;
exit;
end;
FBorderResizeFlag := 0;
if MainFormMaximized then exit;
BottomMargin := FBottomResizeHeight;
if BottomMargin<MinBottomMargin then
BottomMargin := MinBottomMargin;
SideMargin := FSidesResizeHeight;
if SideMargin < MinSideMargin then
SideMargin := MinSideMargin;
if (X<SideMargin) then
FBorderResizeFlag := FBorderResizeFlag or 1 // left
else if (X>=Self.Width-SideMargin) then
FBorderResizeFlag := FBorderResizeFlag or 2; // right
if (Y>=Self.Height-BottomMargin) then
FBorderResizeFlag := FBorderResizeFlag or 4; // bottom
//
// else if (Y<BottomMargin) then // BottomMargin could also be used for top!
// FBorderResizeFlag := FBorderResizeFlag or 8; // top
if FBorderResizeFlag =1 then
Self.Cursor := crSizeWE
else if FBorderResizeFlag =2 then
Self.Cursor := crSizeWE
else if FBorderResizeFlag =4 then
Self.Cursor := crSizeNS
else if FBorderResizeFlag =5 then
Self.Cursor := crSizeNESW // bottom left
else if FBorderResizeFlag =6 then
Self.Cursor := crSizeNWSE // bottom right
else if FBorderResizeFlag =8 then
Self.Cursor := crSizeNS // up
else begin
Self.Cursor := crDefault;
inherited;
end;
end;
procedure TResizeBorderControl.SetAutoSize(const Value: Boolean);
begin
FAutoSize := Value;
if FAutoSize then begin
Align := alNone;
if Self.Left<>0 then
Self.Left := 0;
if Self.Width<>Parent.Width then
Self.Width := Parent.Width;
if Self.Height<>Parent.Height then
Self.Width := Parent.Width;
Anchors := [akLeft, akTop, akRight, akBottom];
end;
Invalidate;
end;
procedure TResizeBorderControl.SetAutoSizeStartingAtTop(
const Value: Integer);
begin
FAutoSizeStartingAtTop := Value;
end;
procedure TResizeBorderControl.SetBottomResizeHeight(
const Value: Integer);
begin
FBottomResizeHeight := Value;
end;
procedure TResizeBorderControl.SetSidesResizeHeight(
const Value: Integer);
begin
FSidesResizeHeight := Value;
end;
end.
P.S. Note that there are some side effects to this kind of thing that made it "a neat Hack" but not something I would ship in a real world piece of software. This is a toy.
精彩评论