Delphi 同步滚动组件
我正在尝试同步 VCL Forms 应用程序中两个 TDBGrid 组件的滚动,但在没有堆栈问题的情况下拦截每个网格组件的 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;
感谢 - Sertac Akyuz 提供的解决方案。当使用网格集成到 VCL 表单应用程序中时,它们将在滚动和突出显示所选记录方面相互模仿。
I am trying to synchronize the scrolling of two TDBGrid components in a VCL Forms application, I am having difficulties intercepting the WndProc of each grid component without some stack issues. I have tried sending WM_VSCROLL messages under scrolling events but this still results in the incorrect operation. It needs to work for clicking the scrollbar, as well as highlighting a cell, or an up or down mouse button. The whole idea is to have two grids next to each other displaying a sort of matching dialog.
Tried
SendMessage( gridX.Handle, WM_VSCROLL, SB_LINEDOWN, 0 );
Also
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;
And
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;
The First is only a temporary solution, the second results in invalid memory reads, and the third results in a stack overflow. So none of these solutions seems to work for me. I'd love some input on how to accomplish this task! Thanks in advance.
UPDATE: Solution
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;
Thanks to - Sertac Akyuz for the solution. When integrated into a VCL forms application using grids, they will mimmic each other in scrolling, and highlighting the selected record.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您可能正在为两个网格实现消息覆盖。 GridX 滚动 GridY,GridY 又滚动 GridX,GridX 又滚动……所以。您可以通过用标志包围块来保护表面滚动代码。
GridY 的类似代码。顺便说一句,您不应该需要 SetScrollPos。
edit:
You are probably implementing the message override for both of the grids. GridX scrolls GridY, which in turn scrolls GridX, which in turn ... SO. You can protect the superficial scrolling code by surrounding the block with flags.
Similiar code for the GridY. BTW, you shouln't need the SetScrollPos.
edit:
我得到了部分但现在完整工作的解决方案(至少对于两个 TMemo)...
我的意思是部分的,因为它只监听一个 TMemo 上的更改,但不监听另一个 TMemo...
我的意思是完全工作,因为它不依赖于关于所做的事情...
就像在一个备忘录上放置相同的水平滚动值一样简单,就像在另一个备忘录上一样...
它与消息无关,但因为我试图通过捕获消息来获得工作解决方案WM_HSCROLL 等...我留下了代码,因为它有效...我稍后会尝试改进它...例如仅捕获 WM_PAINT,或以其他方式...但现在,我将其按我所拥有的方式放置因为它有效...而且我没有找到更好的东西...
这是有效的代码:
它适用于使滚动改变的所有方法...
注释:
水平滚动条...
做并发布它。
Memo2 与 Memo1 同步 按
想要但在 Memo2 上看到它的运行情况,
我将尝试通过以下方式改进它:在 Memo2 上执行某些操作时,Memo1 滚动仍然同步...
我认为它可以适用于几乎所有具有 ScrollBar 的控件,而不仅仅是 TMemo。 ..
I got a partial, but now full working solution (at least for two TMemo)...
I mean partial, because it only listen for changes on one TMemo but not on the other...
I mean full working because it does not depend on what is done...
It is just as simple as put same horizontal scroll value on one Memo as it is on the other...
It is nothing related with messages, but since i was trying to get a working solution by trapping messages WM_HSCROLL, etc... i left the code because it works ... i will try to improve it later... for example trapping only WM_PAINT, or in other ways... but for now, i put it as i have it since as that it works... and i did not find anywhere something yet better...
Here is the code that works:
It works for all ways to make scroll to change...
Notes:
horizontal scrollbar...
do it and post it.
Memo2 to be on sync with Memo1
want but on Memo2 to see it in action
I will try to improve it by: when doing something on Memo2, Memo1 scroll still be on sync...
I think it can work for allmost any control that has a ScrollBar, not only TMemo...
正如我所说...
在效率、干净的代码和双向方面,这是一个更好的解决方案(不是最终的解决方案)...对任何一个的更改都会影响另一个...
请阅读代码注释以了解什么每个句子都是...这很棘手...但主要思想与之前相同...设置另一个 TMemo 水平滚动条,因为它位于用户正在操作的 TMemo 上...无论什么用户是的,移动鼠标并选择文本,按向左、向右、home、结束键,使用鼠标水平滚轮(并非所有都有),拖动滚动条,按水平滚动条的任何部分等...
主要思想是...该对象需要重新绘制,所以然后将另一个对象水平滚动条与此相同...
第一部分只是向 TMemo 类添加内容,它只是创建一个新的派生类,但使用相同的类名,但仅适用于声明的单元。
将其添加到 TForm 声明之前的接口部分,这样您的 TForm 将看到这个新的 TMemo 类而不是普通的 TMemo 类:
下一部分是该新 TMemo 类的先前声明的实现。
将其添加到您喜欢的任何地方的实现部分:
现在是最后一部分,告诉每个 TMemo 必须同步的其他备忘录是什么。
在您的实现部分,为 Form1 Create 事件添加如下内容:
请记住,我们已将 SyncMemo 成员添加到我们特殊的新 TMemo 类中,它的存在就是为了这个,告诉彼此哪个是另一个。
现在,对两个 TMemo 进行一些配置,让其完美运行:
运行它并查看两个水平滚动条如何始终同步...
移动...
等等......,无论SelStart在另一个......水平方向上
文本滚动已同步。
为什么这不是最终版本的问题是:
如果有人知道如何模拟隐藏或使 GetScrollPos 不返回零,请发表评论,这是我需要为最终版本修复的唯一问题。
注意:
WM_HSCROLL 到 WM_VSCROLL,SB_HORZ 到 SB_VERT
这是一个示例New_WindowProc程序用于同时同步两个滚动条,也许适合懒人,也许适合喜欢复制粘贴的人:
希望有人能解决隐藏一个滚动条和GetScrollPos返回零的问题!
As i told...
Here it is a better solution (not final one) in terms of efficiency, clean code and bi-directional... changing on any one affects the other...
Please, read comments on code to understand what does each sentence... it is quite tricky... but the main idea is the same as was before... set the other TMemo horizontal scroll bar as it is on the TMemo where user is acting... no matter what user does, move mouse and select text, press left, right, home, end keys, use the mouse horizontal wheel (not all have one), drag the srollbar, press on any part of the horizontal scrollbar, etc...
The main idea is... the object needs to be re-painted, so then put the other object horizontal scrollbar identical to this one...
This first part is just to add things to TMemo class, it is just creating a new derived class but with same class name, but only for the unit within declared.
Add this to interface section, before your TForm declaration, so your TForm will see this new TMemo class instead of normal one:
This next part is the implementation for previous declarations of that new TMemo class.
Add this to implementation section anywhere you preffer:
Now the last part, tell each TMemo what is the other Memo that has to be on sync.
On your implementation section, for the Form1 Create event add something like this:
Remember we have added SyncMemo member to our special new TMemo class, it was there just for this, tell each other what one is the other one.
Now a little configuration for both TMemo jsut to let this work perfectly:
Run it and see how both horizontal scrollbars are allways on sync...
moves...
etc..., no matter where is SelStart on the other... the horizontal
text scroll is on sync.
The problem why this is not a final version is that:
If someone knows how to emulate hidden or make GetScrollPos to not return zero, please comment, it the only thing i need to fix for final version.
Notes:
WM_HSCROLL to WM_VSCROLL and SB_HORZ to SB_VERT
Here is an example of New_WindowProc procedure for sync both scrollbars at the same time, maybe for lazy people, maybe for people just like copy&paste:
Hope someone can fix the problem of hidden one scrollbar and GetScrollPos returning zero!!!
我找到了一个解决方案...我知道这很棘手...但至少它功能齐全...
而不是试图隐藏水平滚动条...我让它显示在可见区域之外,所以用户看不到...
棘手的部分:
就是这样......完成!水平滚动条不在可见区域...您可以将 TPanel 放在您想要的位置,给它您想要的大小...用户不会看到水平滚动条并且它不会隐藏,因此 GetScrollPos 将正常工作...我知道这很棘手,但功能齐全。
以下是存档的完整代码:
在接口部分,在 TForm 声明之前,因此您的 TForm 将看到这个新的 TMemo 类,而不是普通的类:
在您喜欢的任何地方的实现部分:
在您喜欢的任何地方的实现部分:
就是这样,伙计们!我知道这很棘手,但功能齐全。
请注意,我在 New_WindowProc 上更改了评估 OR 条件的顺序...这只是为了提高所有其他消息的速度,因此尽可能少地延迟所有消息处理。
希望有一天我会知道如何用真实的(计算或读取的)TMemo 水平滚动条高度替换这样的 20。
I found a solution... i know it is quite tricky... but at least it is fully functional...
Instead of trying to hide the horizontal scroll bar... i make it to be displayed out of visible area, so it can not be seen by user...
The tricky part:
That's it... done!!! The horizontal scroll bar is out of visible area... you can put where you want the TPanel, give it the size you want... that horizontal scrollbar will not be seen by user and it is not hidden, so GetScrollPos will work properly... tricky i know, but fully functional.
Here is the full code to archive that:
On interface section, before your TForm declaration, so your TForm will see this new TMemo class instead of normal one:
On implementation section anywhere you preffer:
Also on implementation section anywhere you preffer:
Thas's it folks! I know it is quite tricky, but fully functional.
Please note that i have changed on New_WindowProc the order of evaluating the OR conditions... it is just to improve speed for all other messages, so delay as less as possible all the messages treatment.
Hope sometime i will know how to replace such 20 by the real (calculated or readed) TMemo horizontal scroll bar height.
感谢
GetSystemMetrics
和SM_CYHSCROLL
,但这还不够...只需要多 3 个像素...所以我只使用:
GetSystemMetrics(SM_CYHSCROLL)+ 3
注意:其中两个像素可能是因为父面板的
BevelWidth
值为1
,但我有BevelInner
和BevelOuter
的值为bvNone
所以可能不会;但额外的像素我不知道为什么。多谢。
如果您愿意,可以将它们加入到一篇大帖子中,但我认为最好不要混合它们。
回答“Sertac Akyuz”(很抱歉在这里这样做,但我不知道如何将它们发布在你的问题旁边):
不要将它用作便签本...我在写帖子之前几秒钟就发现了解决方案
多次同一个帖子...它也不会让其他人知道确切的解决方案,
也会让他们知道如何达成这样的解决方案。
我没有提出一个新问题,
重要:我发现通过消息捕获无法完成完美的解决方案,因为有是一种导致滚动但没有消息
WM_VSCROLL
、WM_HSCROLL
(仅WM_PAINT
)的情况...它与用鼠标选择文本有关。让我解释一下我如何看待它的实际效果...只需在最后一条视线的末端附近开始并将鼠标向下移动一点,然后停止鼠标移动并按下鼠标按钮...不执行任何操作(鼠标不移动) ,没有按键,没有按键,没有鼠标按钮更改等...)TMemo 向下滚动直到到达文本末尾...当鼠标靠近视线右端并向右移动时,水平滚动也会发生同样的情况...相反方向也相同...此类滚动不通过消息WM_VSCROLL
WM_HSCROLL
,仅通过WM_PAINT
(至少在我的计算机上) ...同样的情况也发生在网格上。Thanks for
GetSystemMetrics
andSM_CYHSCROLL
, but it is not just enought... just need 3 pixels more...So i just use:
GetSystemMetrics(SM_CYHSCROLL)+3
Note: Two of such pixels could be because having parent panel with
BevelWidth
with value1
but i haveBevelInner
andBevelOuter
with valuebvNone
so may not; but the extra pixel i do not know why.Thanks a lot.
If you preffer, just join them onto one Big post, but i think it is better not to mix them.
In answer to "Sertac Akyuz" (sorry to do it here, but i do not know how to post them next to your question):
not to use it as a scratch-pad... i discobered the solution jsut seconds before writting the posts
times just the same post... it will not also let others know the exact solution,
also will let them know how to reach such solution.
fish".
Important: I discover that a perfect solution can not be done by message capturing because there is a case that causes scroll but no message
WM_VSCROLL
,WM_HSCROLL
(onlyWM_PAINT
)... it is related to selecting text with mouse... let me explain how i see it in action... Just start near the end of last visual line and move mouse just a little down, then stop mouse move and let mouse button pressed... without doing anything (mouse does not move, no keyup, no keydown, no mouse button change, etc...) the TMemo is scrolling down till reaches the end of the text... same happens for horizontal scrolls when mouse is near the right end of visual line and moved right... also same in opposite directions... such scrolls does not through messagesWM_VSCROLL
WM_HSCROLL
, onlyWM_PAINT
(at least on my computer)... also same happens on Grids.