tags:

views:

88

answers:

4

I would like to have my own caption bar and therefore I am using basically a panel (Name: pnCaption) and remove the original caption bar in CreateParams. But the ability to move the window by MouseDown-MouseMove in the new panel is a problem.

Normally you would use the NCHITTEST message. BUT this isn't signaled if the mouse is over the panel (my own caption). See code ...

procedure TForm1.CreateParams(var params: TCreateParams);  
begin  
  inherited Createparams(Params);  
  with Params do  
    Style := (Style or WS_POPUP) and (not WS_DLGFRAME);  
end;  

procedure TForm1.WM_NCHitTest(var Msg: TWMNcHitTest);  
begin  
  inherited;  
  if PtInRect(pnCaption.BoundsRect, ScreenToClient(Point(Msg.XPos, Msg.YPos)))  
      then Msg.Result := HTCAPTION;  
end;  

I would appreciate any hints how to accomplish that task.

Christian

+3  A: 

As I am looking into our old code for custom StatusBar component, which is descendant of TWinControl, to provide form resizing using StatusBar's grip we handle WM_NCHITTEST in the control, not in the form and return HTBOTTOMRIGHT:

procedure TElStatusBar.WMNCHitTest;
var
  P : TPoint;

  function InGrip(Point : TPoint) : boolean;
  var
    r : TRect;
  begin
    R := ClientRect;
    R.Left := R.Right - R.Bottom + hMargin;
    result := PtInRect(R, Point);
  end;

begin
  if not FSizeGrip then
  begin
    inherited;
    exit;
  end;
  P := ScreenToClient(Point(Message.XPos, Message.YPos));
  if InGrip(P) and (TForm(Parent).WindowState = wsNormal)
    and (TForm(Parent).BorderStyle in [bsSizeable, bsSizeToolWin]) then
    Message.Result := HTBOTTOMRIGHT
  else
    inherited;
end;

This means that you need to implement descendant of your panel component (or hook it's message handling) and handle WM_NCHITTEST there.

Also, I'd go the route of handling WM_NCCALCSIZE and WM_NCPAINT messages in the form in order to provide your own caption area and avoid using TPanel or other control. But this is only my preference.

Eugene Mayevski 'EldoS Corp
Hmm, I thought, that I'll give it a try and descend from a TPanel where I use the WM_NCHITTEST of it. But now I am able to move the panel during runtime :( not the thing I wanted ;) I noticed that your WMNCHitTest procedure has no parameters...type TasCaptionPanel = class(TPanel) protected procedure WM_NCHitTest(var Msg: TWMNcHitTest); message WM_NCHITTEST; end;implementationprocedure TasCaptionPanel.WM_NCHITTEST;begin if TForm(Parent).WindowState = wsNormal then Msg.Result := HTCAPTION else inherited;end;
Christian
I AM STUCK.. HOW CAN I FLAG THE TEXT AS CODE AS IN THE ORIGINAL MESSAGE
Christian
No parameters - this is just implementation (which allows not to copy parameters, already declared in interface) It can be that the system handles HTBOTTOMRIGHT in some specific way.
Eugene Mayevski 'EldoS Corp
You can also try the following: try returning HTTRANSPARENT in TPanel.WMNcHitTest and ALSO handle Form's WM_NCHITTEST message. This trick can work as well.
Eugene Mayevski 'EldoS Corp
@Christian: Check for `csDesigning` in `ComponentState` to limit the behaviour to runtime only.
mghie
+2  A: 

The easiest way is probably to use a component that hasn't a HWND window handle and therefore can't receive messages. They will be passed to your form, where they can be handled the way you show in your question.

Simply replacing the TPanel with a top-aligned TPaintBox, TImage or similar TGraphicControl descendant would make your code work. You keep both the message handling of the form and the alignment support of the VCL.

mghie
Yes, using a TBevel or something similar works. The nice thing with the TPanel would have been that I can drop other components on it using straightforward VCL techniques.
Christian
@Christian: You didn't specify that as a requirement in your question. Only `TWinControl` descendants can be parent to other controls, so this solution won't work then.
mghie
+1  A: 

Not exactly what you're looking for, but for others interested in a similar technique, here's code for a TLabel-descended component that can serve as a caption bar:

unit Draglbl;

interface

uses
  WinTypes, WinProcs, Classes, Graphics, Controls, Forms, StdCtrls;

type
  TDragWindowTitle = class(TCustomLabel)
  private
    { Private declarations }
    _lastx,
    _lasty  : integer ;
  protected
    { Protected declarations }
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override ;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override ;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
  published
    { Published declarations }
    property Alignment;
    property Caption;
    property Color;
    property DragCursor;
    property DragMode;
    property Enabled;
    property FocusControl;
    property Font;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ShowHint;
    property Visible;
    property WordWrap;
    property OnClick;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDrag;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
  end;

procedure Register;

implementation
constructor TDragWindowTitle.Create(AOwner: TComponent);
begin
  inherited Create(AOwner) ;
  color := clActiveCaption ;
  font := TForm(AOwner).Font ;
  font.color := clCaptionText ;
  Align := alTop ;
  AutoSize := false ;
  ShowAccelChar := false ;
  Transparent := false ;
end ;

procedure TDragWindowTitle.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  if ssLeft in Shift then begin
    TForm(owner).left := TForm(owner).left+(x-_lastx) ;
    TForm(owner).top := TForm(owner).top+(y-_lasty) ;
  end ;

  inherited MouseMove(shift,x,y) ;
end ;

procedure TDragWindowTitle.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if Button=mbLeft then begin
    _lastx := x;
    _lasty := y ;
  end ;
end ;

procedure Register;
begin
  RegisterComponents('MYCOMPONENTS', [TDragWindowTitle]);
end;

end.
Erik Knowles
What's going to happen if I drop your control on a `TFrame`? Or if I create your component at runtime, passing an owner that isn't a `TForm`, or `nil`?
mghie
Sheesh, I wasn't looking for a argument; I was just posting some code related to the question that might be useful to someone. From your question you obviously understood that the control was meant to replace the caption on a TForm. Leave it at that.
Erik Knowles
+1  A: 

You can always drag a window by any control that has a mousedown event by using the "Magic" $F012 number with a WM_SYSCOMMAND message. It's something I picked up from Ray Kanopka (author of the excellent raize components), but I no longer remember how this was imparted to me.

It is also a neat and simple way of allowing users to move borderless forms by giving them a label of panel that looks like a caption. For example, I use it to allow users to move a borderless about dialog:

procedure TAbout_Dlg.LblTitleMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
const
  sc_DragMove = $F012;
begin
  ReleaseCapture;
  Perform( wm_SysCommand, sc_DragMove, 0 );
end;
Marjan Venema
$F012 is `SC_MOVE` with $0002 OR'ed with it. The 4 low-order bits ($0000-$000F) of `WM_SYSCOMMAND` are used by the OS internally. In this case, $0002 means the Drag bit is enabled.
Remy Lebeau - TeamB
Great - that does exactly what I was looking for. Thank you.
Christian
@Remy: thanks. That's the bit of background info I never got :-)
Marjan Venema