Плавное изменение размеров в форме без полей / окна в Delphi - PullRequest
5 голосов
/ 11 июля 2011

Я пытаюсь изменить размер формы без полей, но когда я увеличиваю размер с помощью правой / нижней стороны, я получаю зазор между границей и старой клиентской областью, который зависит от скорости перемещения мыши.

Эффект более заметен, когда вы изменяете размеры от левой границы или даже от нижнего левого угла, он ужасен везде (я пробовал с другими коммерческими приложениями, и это также происходит).Этот эффект также происходит, когда я изменяю на значительную границу, но он не такой ужасный, как когда я удаляю границы формы

Макет формы состоит из верхней панели, выполняющей функцию строки заголовка (с некоторыми изображениями и кнопками),и некоторые другие панели, показывающие другую информацию (например, памятку, другие элементы управления и т. д.)

В моем коде есть фрагмент кода, где я фиксирую кнопку мыши и отправляю сообщение в окна, но я также попытался сделать это вручнуюс аналогичными результатами

Активация двойного буфера для верхней панели позволяет избежать мерцания, но изменение размера панели не синхронизируется с изменением размера формы, таким образом, появляется зазор или часть панели исчезает

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 ]

6 голосов
/ 13 июля 2011

В прошлый раз, когда я пытался вручную создать окно верхнего уровня, которое изменяет размеры с помощью WM_SYSCOMMAND и перетаскивания мышью, независимо от того, задействованы ли какие-либо вложенные панели или нет, я обнаружил, что проблемы не ограничиваются только мерцанием.

Даже сbare-TForm без рамки с изменяемыми размерами, добавление моей собственной рамки с изменяемыми размерами и обработка сообщений «мышь вниз» и «перемещение мыши» и «мышь вверх» напрямую оказались слишком проблематичными.Я отказался от подхода кода, который вы показываете здесь, и вместо этого я нашел два работоспособных подхода:

  1. использую подход, при котором я беру на себя рисование не клиентских областей.Это то, что делают Google Chrome и многие другие полностью настраиваемые окна.У вас все еще есть неклиентская область, и вы должны ее покрасить и обработать не-клиентскую и пограничную краску.Другими словами, это не совсем без границ, но все это может быть один цвет, если вы этого хотите.Прочтите эту справку о сообщениях WM_NCPAINT , чтобы начать работу.

  2. Используйте окно с изменяемыми размерами без полей, которое все еще распознается (даже без его неклиентской области в качестве окна с изменяемыми размерами. Подумайтеапплета post-it-note-applet. Здесь - вопрос, который я задал некоторое время назад, в нижней части моего вопроса - полностью рабочая демонстрация, которая обеспечивает плавный способ без мерцания иметь окно с изменяемыми размерами без полей.Основная техника ответа была предоставлена ​​Дэвидом Х.

2 голосов
/ 15 июля 2011

Ну, Уоррен П уже довольно убедительно указал вам в другом направлении, но я постараюсь ответить на ваш вопрос.Или не совсем.

Ваша правка теперь делает вопрос очень ясным:

Эффект более заметен, когда вы изменяете размеры от левой границы или даже от нижнего левого угла, он ужасен везде (Я пробовал с другими коммерческими приложениями, и это также происходит).Этот эффект также происходит, когда я изменяю на значительную границу, но он не такой ужасный, как когда я удаляю границу.

Этот эффект проявляется не только в других коммерческих приложениях, но и в каждом окне ОС.,Растяжение верхней части окна проводника также «скрывает» и «расширяет» строку состояния или нижнюю панель.Я почти уверен, что его невозможно победить.

Это может показаться хуже для формы без границ, но я думаю, что это просто оптический обман.

Если бы мне пришлось угадывать объяснение этого эффекта, тогда я бы сказал, что во время операции изменения размера обновление top и left имеет приоритет над изменением ширины и высоты, что приводит к тому, что оба обновления не обновляются одинаковое количество раз.Может быть, это связано с видеокартой.Или, может быть ... о чём я говорю?Это вне моей досягаемости.

Хотя я все еще не могу воспроизвести его для изменения размера правой и / или нижней части формы.Если количество элементов управления или (комбинация) их свойств align и anchor является проблемой, то вы можете временно отключить выравнивание, но я почти уверен, что вы этого тоже не хотите.Ниже мой тестовый код, скопированный из вопроса, слегка измененный и, конечно, с добавленными константами 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 = alCustom и Anchors = [akLeft, akTop, akRight], хотя улучшение можетзависит от того, будет ли панель иметь цвет, отличный от формы, или, может быть, от меня обманут.;)

0 голосов
/ 06 марта 2014

Я знаю, что эта ветка довольно старая, но с ней по-прежнему борются люди.

Хотя ответ прост.Проблема в том, что при попытке изменить размер вы захотите использовать форму, которую вы изменяете, в качестве ссылки. Не делайте этого.

Используйте другую форму.

Вот полный источник 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 используется для указания края вашей формы, который вы хотите перетащить / изменить размер).

Oh, и установите для своей формы значение DoubleBuffered = true , чтобы избавиться от любыхоставшееся мерцание.

Это всего лишь маленькое счастье, которое я могу вам дать.

0 голосов
/ 11 июля 2011

Вы пытались установить форму DoubleBuffered := True?

...