Вопрос

Я пытаюсь синхронизировать прокрутку двух компонентов TDBGrid в приложении VCL Forms. У меня возникают трудности с перехватом WndProc каждого компонента сетки без каких-либо проблем со стеком.Я пробовал отправлять сообщения WM_VSCROLL при событиях прокрутки, но это все равно приводит к неправильной работе.Он должен работать при нажатии на полосу прокрутки, а также при выделении ячейки или нажатии кнопки мыши вверх или вниз.Вся идея состоит в том, чтобы располагать две сетки рядом друг с другом, отображающие своего рода диалог соответствия.

Пытался

SendMessage( gridX.Handle, WM_VSCROLL, SB_LINEDOWN, 0 );

Также

procedure TForm1.GridXCustomWndProc( var Msg: TMessage );
begin
Msg.Result := CallWindowProc( POldWndProc, gridX.Handle, Msg.Msg, Msg.wParam, Msg.lParam );

   if ( Msg.Msg = WM_VSCROLL ) then 
   begin
      gridY.SetActiveRow( gridX.GetActiveRow );
      gridY.Perform( Msg.Msg, Msg.wParam, Msg.lParam );
      SetScrollPos( gridY.Handle, SB_VERT, HIWORD( Msg.wParam ), True );
   end;
end;

И

procedure TForm1.GridxCustomWndProc( var Msg: TMessage );
begin
   if ( Msg.Msg = WM_VSCROLL ) then 
   begin
      gridY.SetActiveRow( gridX.GetActiveRow );
      gridY.Perform( Msg.Msg, Msg.wParam, Msg.lParam );
      SetScrollPos( gridY.Handle, SB_VERT, HIWORD( Msg.wParam ), True );
   end;
   inherited WndProc( Msg );
end;

Первое — лишь временное решение, второе приводит к недопустимому чтению памяти, а третье — к переполнению стека.Так что ни одно из этих решений, похоже, мне не подходит.Мне бы хотелось получить информацию о том, как выполнить эту задачу!Заранее спасибо.

ОБНОВЛЯТЬ:Решение

  private
    [...]
    GridXWndProc, GridXSaveWndProc: Pointer;
    GridYWndProc, GridYSaveWndProc: Pointer;
    procedure GridXCustomWndProc( var Msg: TMessage );
    procedure GridYCustomWndProc( var Msg: TMessage );

procedure TForm1.FormCreate(Sender: TObject);
begin
  GridXWndProc := classes.MakeObjectInstance( GridXCustomWndProc );
  GridXSaveWndProc := Pointer( GetWindowLong( GridX.Handle, GWL_WNDPROC ) );
  SetWindowLong( GridX.Handle, GWL_WNDPROC, LongInt( GridXWndProc ) );

  GridYWndProc := classes.MakeObjectInstance( GridYCustomWndProc );
  GridYSaveWndProc := Pointer( GetWindowLong( GridY.Handle, GWL_WNDPROC ) );
  SetWindowLong( GridY.Handle, GWL_WNDPROC, LongInt( GridYWndProc ) );
end;

procedure TForm1.GridXCustomWndProc( var Msg: TMessage );
begin
   Msg.Result := CallWindowProc( GridXSaveWndProc, GridX.Handle, Msg.Msg, Msg.WParam, Msg.LParam );
   case Msg.Msg of
      WM_KEYDOWN:
      begin
         case TWMKey( Msg ).CharCode of VK_UP, VK_DOWN, VK_PRIOR, VK_NEXT:
            GridY.Perform( Msg.Msg, Msg.WParam, Msg.LParam );
         end;
      end;
      WM_VSCROLL:
         GridY.Perform( Msg.Msg, Msg.WParam, Msg.LParam );
      WM_HSCROLL:
         GridY.Perform( Msg.Msg, Msg.WParam, Msg.LParam );
      WM_MOUSEWHEEL:
      begin
         ActiveControl := GridY;
         GridY.Perform( Msg.Msg, Msg.WParam, Msg.LParam );
      end;
      WM_DESTROY:
      begin
         SetWindowLong( GridX.Handle, GWL_WNDPROC, Longint( GridXSaveWndProc ) );
         Classes.FreeObjectInstance( GridXWndProc );
      end;
  end;
end;

procedure TForm1.GridXMouseDown( Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer );
begin
   GridY.SetActiveRow( GridX.GetActiveRow );
end;

procedure TForm1.GridYCustomWndProc( var Msg: TMessage );
begin
   Msg.Result := CallWindowProc( GridYSaveWndProc, GridY.Handle, Msg.Msg, Msg.WParam, Msg.LParam );
   case Msg.Msg of
      WM_KEYDOWN:
      begin
         case TWMKey( Msg ).CharCode of VK_UP, VK_DOWN, VK_PRIOR, VK_NEXT:
            GridX.Perform( Msg.Msg, Msg.WParam, Msg.LParam );
         end;
      end;
      WM_VSCROLL:
         GridX.Perform( Msg.Msg, Msg.WParam, Msg.LParam );
      WM_HSCROLL:
         GridX.Perform( Msg.Msg, Msg.WParam, Msg.LParam );
      WM_MOUSEWHEEL:
      begin
         ActiveControl := GridX;
         GridX.Perform( Msg.Msg, Msg.WParam, Msg.LParam );
      end;
      WM_DESTROY:
      begin
         SetWindowLong( GridY.Handle, GWL_WNDPROC, Longint( GridYSaveWndProc ) );
         Classes.FreeObjectInstance( GridYWndProc );
      end;
   end;
end;

procedure TForm1.GridYMouseDown( Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer );
begin
   GridX.SetActiveRow( GridY.GetActiveRow );
end;

Спасибо Сертаку Акюзу за решение.При интеграции в приложение форм VCL с использованием сеток они будут имитировать друг друга при прокрутке и выделении выбранной записи.

Это было полезно?

Решение

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

type
  TForm1 = class(TForm)
    [..] 
  private
    FNoScrollGridX, FNoScrollGridY: Boolean;
    [..]

procedure TForm1.GridXCustomWndProc( var Msg: TMessage );
begin
  Msg.Result := CallWindowProc(POldWndProc, gridX.Handle, Msg.Msg, Msg.wParam, Msg.lParam );

  if ( Msg.Msg = WM_VSCROLL ) then 
  begin
    if not FNoScrollGridX then
    begin
      FNoScrollGridX := True
      gridY.SetActiveRow( gridX.GetActiveRow );
      gridY.Perform( Msg.Msg, Msg.wParam, Msg.lParam );
//      SetScrollPos( gridY.Handle, SB_VERT, HIWORD( Msg.wParam ), True );
    end;
    FNoScrollGridX := False;
  end;
end;
.

Символьный код для счисления.Кстати, ты не нуждаешься в SetscrollPos.


Редактировать:

TForm1 = class(TForm)
  [..]
private
  GridXWndProc, GridXSaveWndProc: Pointer;
  GridYWndProc, GridYSaveWndProc: Pointer;
  procedure GridXCustomWndProc(var Msg: TMessage);
  procedure GridYCustomWndProc(var Msg: TMessage);
  [..]

procedure TForm1.FormCreate(Sender: TObject);
begin
  [..]

  GridXWndProc := classes.MakeObjectInstance(GridXCustomWndProc);
  GridXSaveWndProc := Pointer(GetWindowLong(GridX.Handle, GWL_WNDPROC));
  SetWindowLong(GridX.Handle, GWL_WNDPROC, LongInt(GridXWndProc));

  GridYWndProc := classes.MakeObjectInstance(GridYCustomWndProc);
  GridYSaveWndProc := Pointer(GetWindowLong(GridY.Handle, GWL_WNDPROC));
  SetWindowLong(GridY.Handle, GWL_WNDPROC, LongInt(GridYWndProc));
end;

procedure TForm1.GridXCustomWndProc(var Msg: TMessage);
begin
  Msg.Result := CallWindowProc(GridXSaveWndProc, GridX.Handle,
      Msg.Msg, Msg.WParam, Msg.LParam);

  case Msg.Msg of
    WM_KEYDOWN:
      begin
        case TWMKey(Msg).CharCode of
          VK_UP, VK_DOWN, VK_PRIOR, VK_NEXT:
            GridY.Perform(Msg.Msg, Msg.WParam, Msg.LParam);
        end;
      end;
    WM_VSCROLL: GridY.Perform(Msg.Msg, Msg.WParam, Msg.LParam);
    WM_MOUSEWHEEL:
      begin
        ActiveControl := GridY;
        GridY.Perform(Msg.Msg, Msg.WParam, Msg.LParam);
      end;
    WM_DESTROY:
      begin
        SetWindowLong(GridX.Handle, GWL_WNDPROC, Longint(GridXSaveWndProc));
        Classes.FreeObjectInstance(GridXWndProc);
      end;
  end;
end;

procedure TForm1.GridYCustomWndProc(var Msg: TMessage);
begin
  Msg.Result := CallWindowProc(GridYSaveWndProc, GridY.Handle,
      Msg.Msg, Msg.WParam, Msg.LParam);

  case Msg.Msg of
    WM_KEYDOWN:
      begin
        case TWMKey(Msg).CharCode of
          VK_UP, VK_DOWN, VK_PRIOR, VK_NEXT:
            GridX.Perform(Msg.Msg, Msg.WParam, Msg.LParam);
        end;
      end;
    WM_VSCROLL: GridX.Perform(Msg.Msg, Msg.WParam, Msg.LParam);
    WM_MOUSEWHEEL:
      begin
        ActiveControl := GridX;
        GridY.Perform(Msg.Msg, Msg.WParam, Msg.LParam);
      end;
    WM_DESTROY:
      begin
        SetWindowLong(GridY.Handle, GWL_WNDPROC, Longint(GridYSaveWndProc));
        Classes.FreeObjectInstance(GridYWndProc);
      end;
  end;
end;
.

Другие советы

Я получил частичное, но теперь полное рабочее решение (по крайней мере, для двух TMEMO) ...

Я имею в виду частично, потому что это только слушает изменения на одном TMEMO, но не на другом ...

Я имею в виду полную работу, потому что это не зависит от того, что сделано ...

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

Это ничего не связано с сообщениями, но поскольку я пытался получить рабочее решение путем захвата сообщений WM_HSCROLL и т. Д. ... Я покинул код, потому что он работает ... Я постараюсь улучшить его позже ... для Пример захвата только wm_paint или другими способами ... но на данный момент я положил его так, как у него есть, так как это работает ... и я не нашел никуда чего-то еще лучше ...

Вот код, который работает:

// On private section of TForm1
Memo_OldWndProc:TWndMethod; // Just to save and call original handler
procedure Memo_NewWndProc(var TheMessage:TMessage); // New handler

// On implementation section of TForm1    
procedure TForm1.FormCreate(Sender: TObject);
begin
     Memo_OldWndProc:=Memo1.WindowProc; // Save the handler
     Memo1.WindowProc:=Memo_NewWndProc; // Put the new handler, so we can do extra things
end;

procedure TForm1.Memo_NewWndProc(var TheMessage:TMessage);
begin
     Memo_OldWndProc(TheMessage); // Let the scrollbar to move to final position
     Memo2.Perform(WM_HSCROLL
                  ,SB_THUMBPOSITION+65536*GetScrollPos(Memo1.Handle,SB_HORZ)
                  ,0
                  ); // Put the horizontal scroll of Memo2 at same position as Memo1
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
     Memo1.WindowProc:=Memo_OldWndProc; // Restore the old handler
end;
.

Работает для всех способов сделать прокрутки для изменения ...

Примечания:

    .
  • Я знаю, что это ужасно ловить все сообщения, но, по крайней мере, работает ...
  • Это моя первая успешная попытка иметь два TMEMOS с синхронизией Горизонтальная прокрутка ...
  • так, если кто-то может немного улучшить (не поймать все сообщения), пожалуйста сделай это и опубликуйте это.
  • это только делает memo1 на горизонтальную синхронизацию с помощью меток2 бар, но не MEMO2 будет на синхронизации с MEMO1
  • Нажмите клавиши вверх, вниз, слева, справа, мыши, и т. д. ... независимо от вас хочу, но на MEMO2, чтобы увидеть его в действии

Я постараюсь улучшить его: при выполнении чего-либо на Memo2, Memo1 Screoll по-прежнему будет на синхронизации ...

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

Как я сказал ...

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

Пожалуйста, прочитайте комментарии к Code, чтобы понять, что каждое предложение ... Это довольно сложно ... но главная идея такая же, как и раньше ... установить другую горизонтальную полосу прокрутки TMEMO, как оно на TMEMO, где действует пользователю ... Независимо от того, что делает пользователь, перемещать мышь и выбрать текст, нажмите влево, верно, домой, конечные клавиши, используйте мышь горизонтальное колесо (не у всех есть), перетащите srollbar, нажмите на любую часть горизонтальной прокрутки и т. Д. ...

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

Эта первая часть - просто добавить вещи в класс TMEMO, он просто создает новый полученный класс, но с именем одного класса, но только для блока внутри объявленного.

Добавьте это в раздел интерфейса перед декларацией TForm, поэтому ваш TFOR увидит этот новый класс TMEMO вместо обычного:

type
    TMemo=class(StdCtrls.TMemo) // Just to add things to TMemo class only for this unit
    private
       BusyUpdating:Boolean; // To avoid circular stack overflow
       SyncMemo:TMemo; // To remember the TMemo to be sync
       Old_WindowProc:TWndMethod; // To remember old handler
       procedure New_WindowProc(var Mensaje:TMessage); // The new handler
    public
       constructor Create(AOwner:TComponent);override; // The new constructor
       destructor Destroy;override; // The new destructor
    end;
.

Эта следующая часть - это реализация для предыдущих деклараций этого нового класса TMEMO.

Добавьте это в раздел реализации в любом месте, где вы превыпите:

constructor TMemo.Create(AOwner:TComponent); // The new constructor
begin
     inherited Create(AOwner); // Call real constructor
     BusyUpdating:=False; // Initialize as not being in use, to let enter
     Old_WindowProc:=WindowProc; // Remember old handler
     WindowProc:=New_WindowProc; // Replace handler with new one
end;

destructor TMemo.Destroy; // The new destructor
begin
     WindowProc:=Old_WindowProc; // Restore the original handler
     inherited Destroy; // Call the real destructor
end;

procedure TMemo.New_WindowProc(var Mensaje:TMessage);
begin
     Old_WindowProc(Mensaje); // Call the real handle before doing anything
     if  BusyUpdating // To avoid circular stack overflow
       or
         (not Assigned(SyncMemo)) // If not yet set (see TForm1.FormCreate bwlow)
       or
         (WM_PAINT<>Mensaje.Msg) // If not when need to be repainted to improve speed
     then Exit; // Do no more and exit the procedure
     BusyUpdating:=True; // Set that object is busy in our special action
     SyncMemo.Perform(WM_HSCROLL,SB_THUMBPOSITION+65536*GetScrollPos(Handle,SB_HORZ),0); // Send to the other TMemo a message to set its horizontal scroll as it is on this TMemo
     BusyUpdating:=False; // Set that the object is no more busy in our special action
end;
.

Теперь последняя часть, сообщите каждому TMEMO, что такое другая записка, которая должна быть на синхронизации.

В вашем разделе реализации для формы1 Создать событие Добавить что-то вроде этого:

procedure TForm1.FormCreate(Sender: TObject);
begin
     Memo1.SyncMemo:=Memo2; // Tell Memo1 what TMemo must sync (Memo2)
     Memo2.SyncMemo:=Memo1; // Tell Memo2 what TMemo must sync (Memo1)
end;
.

Помните, что мы добавили член Syncmemo на наш особый новый класс TMEMO, именно там было только для этого, расскажите друг друга, что один другой.

Теперь небольшая конфигурация для обоих TMEMO JSUT, чтобы сделать эту работу идеально:

    .
  • Позвольте обоим полосу прокрутки TMEMO быть видимыми
  • Пусть wordwrap false на обоих tmemo
  • положить много текста (то же самое для обоих), длинные линии и много строк

Запустите его и посмотрите, как обоих горизонтальные прокрутки всегда на синхронизации ...

    .
  • Если вы переместите одну горизонтальную прокрутку, другую горизонтальную прокрутку движется ...
  • Если вы идете на текст вправо или влево, начало линии или конец линии, и т. д. ..., независимо от того, где Selstart на другой ... горизонтальный Текстовый свиток находится на синхронизации.

Проблема, почему это не окончательная версия, это то, что:

    .
  • полосы прокрутки (горизонтальные в моем случае) не могут быть скрыты ... поскольку, если кто-то скрыт, при вызове getscrollpos он возвращает ноль, поэтому делает его не на синхронизации.

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

Примечания:

    .
  • Очевидно, то же самое можно сделать с вертикальной прокруткой ... просто изменить Wm_hscroll до wm_vscroll и sb_horz в sb_vert
  • Очевидно, то же самое можно сделать для обоих одновременно ... просто скопируйте синхронизацию. Дважды в два раза и на один адрес wm_hscroll и sb_horz и с другой - wm_vscroll и sb_vert

Вот пример процедуры New_windowProc для синхронизации обеих прокрутков одновременно, может быть, для ленивых людей, может быть, для людей, как правило, как копирование и вставка:

procedure TMemo.New_WindowProc(var Mensaje:TMessage);
begin
     Old_WindowProc(Mensaje); // Call the real handle before doing anything
     if  BusyUpdating // To avoid circular stack overflow
       or
         (not Assigned(SyncMemo)) // If not yet set (see TForm1.FormCreate bwlow)
       or
         (WM_PAINT<>Mensaje.Msg) // If not when need to be repainted to improve speed
     then Exit; // Do no more and exit the procedure
     BusyUpdating:=True; // Set that object is busy in our special action
     SyncMemo.Perform(WM_HSCROLL,SB_THUMBPOSITION+65536*GetScrollPos(Handle,SB_HORZ),0); // Send to the other TMemo a message to set its horizontal scroll as it is on this TMemo
     SyncMemo.Perform(WM_VSCROLL,SB_THUMBPOSITION+65536*GetScrollPos(Handle,SB_VERT),0); // Send to the other TMemo a message to set its vertical scroll as it is on this TMemo
     BusyUpdating:=False; // Set that the object is no more busy in our special action
end;
.

Надеюсь, кто-то может решить проблему скрытой одной прокрутки и getscrollpos, возвращающих ноль !!!

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

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

Сложная часть:

    .
  • положить тпанель, где tmemo есть и положить tmemo внутри tpanel
  • Скрыть границы TPanel, положите границу как 0, и все скос к BVNONE / BKNONE
  • Настроить TMEMO Alight на ALTOP, а не альпинировать и т. Д. ...
  • обрабатывать tpanel.onreasize, чтобы сделать TMEMO.Height больше, чем TPanel.Height столько, сколько горизонтальная высота прокрутки (на данный момент я использую постоянное значение 20 пикселей, но я хотел бы знать, как получить реальную ценность) < / li>

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

Вот полный код для архива, который:

в разделе интерфейса, перед декларацией TForm, поэтому ваш TFOR увидит этот новый класс TMEMO вместо нормального:

type
    TMemo=class(StdCtrls.TMemo) // Just to add things to TMemo class only for this unit
    private
       BusyUpdating:Boolean; // To avoid circular stack overflow
       SyncMemo:TMemo; // To remember the TMemo to be sync
       Old_WindowProc:TWndMethod; // To remember old handler
       procedure New_WindowProc(var Mensaje:TMessage); // The new handler
    public
       constructor Create(AOwner:TComponent);override; // The new constructor
       destructor Destroy;override; // The new destructor
    end;
.

в разделе реализации в любом месте, где вы превыпите:

constructor TMemo.Create(AOwner:TComponent); // The new constructor
begin
     inherited Create(AOwner); // Call real constructor
     BusyUpdating:=False; // Initialize as not being in use, to let enter
     Old_WindowProc:=WindowProc; // Remember old handler
     WindowProc:=New_WindowProc; // Replace handler with new one
end;

destructor TMemo.Destroy; // The new destructor
begin
     WindowProc:=Old_WindowProc; // Restore the original handler
     inherited Destroy; // Call the real destructor
end;

procedure TMemo.New_WindowProc(var Mensaje:TMessage);
begin
     Old_WindowProc(Mensaje); // Call the real handle before doing anything
     if  (WM_PAINT<>Mensaje.Msg) // If not when need to be repainted to improve speed
       or
         BusyUpdating // To avoid circular stack overflow
       or
         (not Assigned(SyncMemo)) // If not yet set (see TForm1.FormCreate bwlow)
     then Exit; // Do no more and exit the procedure
     BusyUpdating:=True; // Set that object is busy in our special action
     SyncMemo.Perform(WM_HSCROLL,SB_THUMBPOSITION+65536*GetScrollPos(Handle,SB_HORZ),0); // Send to the other TMemo a message to set its horizontal scroll as it is on this TMemo
     BusyUpdating:=False; // Set that the object is no more busy in our special action
end;
.

Также в разделе реализации в любом месте вы превыпите:

procedure TForm1.FormCreate(Sender: TObject);
begin
     Memo1.SyncMemo:=Memo2; // Tell Memo1 what TMemo must sync (Memo2)
     Memo2.SyncMemo:=Memo1; // Tell Memo2 what TMemo must sync (Memo1)
end;

procedure TForm1.pnlMemo2Resize(Sender: TObject);
begin
     Memo2.Height:=pnlMemo2.Height+20; // Make height enough big to cause horizontal scroll bar be out of TPanel visible area, so it will not be seen by the user
end;
.

Это люди! Я знаю, что это довольно сложно, но полностью функционально.

Обратите внимание, что я изменился на New_windowProc Порядок оценки или условий ... Это просто для улучшения скорости для всех других сообщений, поэтому задержка как можно меньше всех сообщений.

Надеюсь, когда-нибудь я буду знать, как заменить такие 20 по реальному (рассчитанному или читаю) горизонтальной высотой прокрутки TMEMO.

Спасибо GetSystemMetrics и SM_CYHSCROLL, но этого мало...просто нужно на 3 пикселя больше...

Поэтому я просто использую: GetSystemMetrics(SM_CYHSCROLL)+3

Примечание:Два таких пикселя могут быть связаны с наличием родительской панели с BevelWidth со стоимостью 1 но у меня есть BevelInner и BevelOuter со стоимостью bvNone так что не может;но лишний пиксель не знаю почему.

Большое спасибо.

Если хотите, просто объедините их в один большой пост, но я думаю, лучше не смешивать.

В ответ на "Сертак Акюз" (извините, что делаю это здесь, но я не знаю, как разместить их рядом с вашим вопросом):

  • Я помещаю сюда решения, которые нашел, как я их нашел...Мое намерение не было использовать его в качестве царапин ...я нашел решение за несколько секунд до написания сообщения
  • Я думаю, что лучше увидеть старые посты, а не редактировать времена множества такого же поста ...Это также не даст другим знать точное решение, также даст им знать, как достичь такого решения.
  • Я предпочитаю делать что -то таким образом, как «Учите, как ловить рыбу, а не дать рыбу».
  • Я не открывал новый вопрос только потому, что название этого вопроса точно соответствует тому, что я пытался сделать.

Важный:Я обнаружил, что идеальное решение не может быть достигнуто путем захвата сообщений, потому что есть случай, когда происходит прокрутка, но нет сообщения. WM_VSCROLL, WM_HSCROLL (только WM_PAINT)...это связано с выделением текста мышью...объясню, как я вижу это в действии...Просто начните с конца последней визуальной линии и переместите мышь немного вниз, затем остановите движение мыши и нажмите кнопку мыши...ничего не делая (мышь не двигается, не нажимается клавиша, не нажимается клавиша, не меняется кнопка мыши и т. д.) TMemo прокручивается вниз, пока не достигнет конца текста...то же самое происходит с горизонтальной прокруткой, когда мышь находится возле правого конца визуальной линии и перемещается вправо...тоже самое в разные стороны...такие прокрутки не проходят через сообщения WM_VSCROLL WM_HSCROLL, только WM_PAINT (по крайней мере на моем компьютере)...то же самое происходит и с Grids.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top