5

我正在尝试调整无边框表单的大小,但是当我使用右侧/底部增加大小时,边框和旧客户区之间会出现间隙,这取决于您移动鼠标的速度。

当您从左边框甚至从左下角调整大小时,效果会更加明显,到处都是可怕的(我尝试使用其他商业应用程序,它也发生了)。当我更改为相当大的边框时也会发生这种效果,但它不像我删除表单边框时那么糟糕

表单布局包括一个执行标题栏功能的顶部面板(带有一些 tImage 和按钮),以及一些显示其他信息的其他面板(如备忘录、其他控件等)

我的代码有一个片段,我在其中捕获鼠标按钮并向窗口发送消息,但我也尝试手动执行,结果相似

激活顶部面板的双缓冲区可避免闪烁,但调整面板大小与调整表单大小不同步,因此出现间隙或部分面板消失

 procedure TOutputForm.ApplicationEvents1Message( var Msg: tagMSG;
  var Handled: Boolean );
const
  BorderBuffer = 5;
var
  X, Y: Integer;
  ClientPoint: TPoint;
  direction: integer;
begin
  Handled := false;
  case Msg.message of
    WM_LBUTTONDOWN:
      begin
        if fResizable then
        begin
          if fSides = [sTop] then
            direction := 3
          else if fSides = [sLeft] then
            direction := 1
          else if fSides = [sBottom] then
            direction := 6
          else if fSides = [sRight] then
            direction := 2
          else if fSides = [sRight, sTop] then
            direction := 5
          else if fSides = [sLeft, sTop] then
            direction := 4
          else if fSides = [sLeft, sBottom] then
            direction := 7
          else if fSides = [sRight, sBottom] then
            direction := 8;
          ReleaseCapture;
          SendMessage( Handle, WM_SYSCOMMAND, ( 61440 + direction ), 0 );
          Handled := true;
        end;
      end;
    WM_MOUSEMOVE:
      begin
        // Checks the borders and sets fResizable to true if it's in a "border" 
        // ...
      end; // mousemove
  end; // case
end;

我怎样才能避免该区域和/或强制重绘窗口?我正在使用 Delphi,但通用解决方案(或其他语言)甚至是前进的方向对我来说都很好

先感谢您

4

4 回答 4

6

上次我尝试手动制作一个通过 WM_SYSCOMMAND 和鼠标拖动调整大小的顶级窗口,无论是否涉及任何嵌套面板,我发现问题不仅限于闪烁。

即使使用没有可调整大小边框的裸 TForm,添加我自己的可调整大小边框并直接处理鼠标向下、鼠标移动和鼠标向上消息也证明太成问题了。我放弃了您在此处展示的代码方法,而是找到了两种可行的方法:

  1. 使用我接管非客户区域的绘画的方法。这就是 Google Chrome 和许多其他完全自定义的窗口所做的。您仍然有一个非客户区,由您来绘制它并处理非客户区和边框绘制。换句话说,它并不是真正的无边界,但如果你想要的话,它都可以是一种颜色。阅读有关 WM_NCPAINT 消息的帮助以开始使用。

  2. 使用仍然可以识别的无边框可调整大小的窗口(即使没有将其非客户区作为可调整大小的窗口。想想便利贴小程序。是我刚才问的一个问题,在我的问题的底部是一个完全工作演示,提供了一种平滑的无闪烁方式来拥有无边框可调整大小的窗口。答案的基础技术由 David H.

于 2011-07-13T16:45:23.703 回答
2

好吧,Warren P 已经非常令人信服地为您指出了另一个方向,但我会尝试回答您的问题。或者不是真的。

您的编辑现在使问题非常清楚:

当您从左边框甚至从左下角调整大小时,效果会更加明显,到处都是可怕的(我尝试使用其他商业应用程序,它也发生了)。当我更改为相当大的边框时也会发生这种效果,但它并不像我删除边框时那么糟糕。

不仅其他商业应用程序,而且每个操作系统窗口都表现出这种效果。拉伸资源管理器窗口的顶部也会“隐藏”和“展开”状态栏或底部面板。我很确定它不会被打败。

无边界形式可能看起来更糟,但我认为这只是视觉上的欺骗。

如果我不得不猜测解释这种效果,那么我会说在调整大小操作期间,top 和 left 的更新优先于 width 和 height 的更新,这导致两者的更新次数不相等。可能和显卡有关。或者,也许……我在说什么?这是我无法企及的。

虽然,我仍然无法复制它来调整表单的右侧和/或底部的大小。如果控件的数量或(组合)它们的对齐和锚属性是一个问题,那么您可以考虑暂时禁用对齐,但我几乎可以肯定您也不希望这样。下面是我的测试代码,从问题中复制而来,略有改动,当然还添加了 Sertac 的常量:

function TForm1.ResizableAt(X, Y: Integer): Boolean;
const
  BorderBuffer = 5;
var
  R: TRect;
  C: TCursor;
begin
  SetRect(R, 0, 0, Width, Height);
  InflateRect(R, -BorderBuffer, -BorderBuffer);
  Result := not PtInRect(R, Point(X, Y));
  if Result then
  begin
    FSides := [];
    if X < R.Left then
      Include(FSides, sLeft)
    else if X > R.Right then
      Include(FSides, sRight);
    if Y < R.Top then
      Include(FSides, sTop)
    else if Y > R.Bottom then
      Include(FSides, sBottom);
  end;
end;

function TForm1.SidesToCursor: TCursor;
begin
  if (FSides = [sleft, sTop]) or (FSides = [sRight, sBottom]) then
    Result := crSizeNWSE
  else if (FSides = [sRight, sTop]) or (FSides = [sLeft, sBottom]) then
    Result := crSizeNESW
  else if (sLeft in FSides) or (sRight in FSides) then
    Result := crSizeWE
  else if (sTop in FSides) or (sBottom in FSides) then
    Result := crSizeNS
  else
    Result := crNone;
end;

procedure TForm1.ApplicationEventsMessage(var Msg: tagMSG;
  var Handled: Boolean);
var
  CommandType: WPARAM;
begin
  case Msg.message of
    WM_LBUTTONDOWN:
      if FResizable then
      begin
        CommandType := SC_SIZE;
        if sLeft in FSides then
          Inc(CommandType, WMSZ_LEFT)
        else if sRight in FSides then
          Inc(CommandType, WMSZ_RIGHT);
        if sTop in FSides then
          Inc(CommandType, WMSZ_TOP)
        else if sBottom in FSides then
          Inc(CommandType, WMSZ_BOTTOM);
        ReleaseCapture;
        DisableAlign;
        PostMessage(Handle, WM_SYSCOMMAND, CommandType, 0);
        Handled := True;
      end;
    WM_MOUSEMOVE:
      with ScreenToClient(Msg.pt) do
      begin
        FResizable := ResizableAt(X, Y);
        if FResizable then
          Screen.Cursor := SidesToCursor
        else
          Screen.Cursor := Cursor;
        if AlignDisabled then
          EnableAlign;
      end;
  end;
end;

关于您的顶部对齐面板:尝试设置Align = alCustomAnchors = [akLeft, akTop, akRight],尽管增强可能取决于面板具有与表单颜色不同的颜色,或者可能取决于我被光学欺骗。;)

于 2011-07-14T21:30:12.447 回答
0

您是否尝试将表单设置为DoubleBuffered := True

于 2011-07-11T16:26:35.150 回答
-1

我知道这个线程已经相当老了,但它仍然是人们仍在努力解决的问题。

不过,答案很简单。问题是尝试调整大小会使您想要使用要调整大小的表单作为参考。不要那样做。

使用另一种形式。

这是可以帮助您的 TForm 的完整来源。确保此表单具有BorderStyle = bsNone。您可能还想确保它不可见。

unit UResize;
{
  Copyright 2014 Michael Thomas Greer
  Distributed under the Boost Software License, Version 1.0
  (See accompanying file LICENSE.txt or copy
   at http://www.boost.org/LICENSE_1_0.txt )
}

//////////////////////////////////////////////////////////////////////////////
interface
//////////////////////////////////////////////////////////////////////////////

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

const
  ResizeMaskLeft   = $1;
  ResizeMaskTop    = $2;
  ResizeMaskWidth  = $4;
  ResizeMaskHeight = $8;

type
  TResizeForm = class( TForm )
    procedure FormMouseMove( Sender: TObject;      Shift: TShiftState; X, Y: Integer );
    procedure FormMouseUp(   Sender: TObject;
                             Button: TMouseButton; Shift: TShiftState; X, Y: Integer );
  private
    anchor_g: TRect;
    anchor_c: TPoint;
    form_ref: TForm;
    resize_m: cardinal;

  public
    procedure SetMouseDown( AForm: TForm; ResizeMask: cardinal );
  end;

var
  ResizeForm: TResizeForm;


//////////////////////////////////////////////////////////////////////////////
implementation
//////////////////////////////////////////////////////////////////////////////

{$R *.DFM}

//----------------------------------------------------------------------------
procedure TResizeForm.SetMouseDown( AForm: TForm; ResizeMask: cardinal );
  begin
  anchor_g.Left   := AForm.Left;
  anchor_g.Top    := AForm.Top;
  anchor_g.Right  := AForm.Width;
  anchor_g.Bottom := AForm.Height;
  anchor_c        := Mouse.CursorPos;
  form_ref        := AForm;
  resize_m        := ResizeMask;
  SetCapture( Handle )
  end;

//----------------------------------------------------------------------------
procedure TResizeForm.FormMouseMove(
  Sender: TObject;
  Shift:  TShiftState;
  X, Y:   Integer
  );
  var
    p: TPoint;
    r: TRect;
  begin
  if Assigned( form_ref ) and (ssLeft in Shift)
    then begin
         p := Mouse.CursorPos;
         Dec( p.x, anchor_c.x );
         Dec( p.y, anchor_c.y );

         r.Left   := form_ref.Left;
         r.Top    := form_ref.Top;
         r.Right  := form_ref.Width;
         r.Bottom := form_ref.Height;

         if (resize_m and ResizeMaskLeft)   <> 0 then begin r.Left   := anchor_g.Left   + p.x;  p.x := -p.x end;
         if (resize_m and ResizeMaskTop)    <> 0 then begin r.Top    := anchor_g.Top    + p.y;  p.y := -p.y end;
         if (resize_m and ResizeMaskWidth)  <> 0 then       r.Right  := anchor_g.Right  + p.x;
         if (resize_m and ResizeMaskHeight) <> 0 then       r.Bottom := anchor_g.Bottom + p.y;

         with r do form_ref.SetBounds( Left, Top, Right, Bottom )
         end
  end;

//----------------------------------------------------------------------------
procedure TResizeForm.FormMouseUp(
  Sender: TObject;
  Button: TMouseButton;
  Shift:  TShiftState;
  X, Y:   Integer
  );
  begin
  ReleaseCapture;
  form_ref := nil
  end;

end.

现在,您的应用程序中的任何无边框表单都可以通过简单的连接到 ResizeForm 来平滑调整大小

ResizeForm.SetMouseDown( self, (sender as TComponent).Tag );

放置它的好地方是您用于跟踪无边界表单边缘的任何组件的 MouseDown 事件。(请注意 Tag 属性如何用于指示您希望拖动/调整表单的哪个边缘)。

哦,将表单设置为DoubleBuffered = true以消除任何剩余的闪烁。

这只是我能给你的一个小小的幸福。

于 2014-03-06T09:56:51.710 回答