这可能看起来像一个愚蠢的&简单的问题,然而,我一直无法找到令人满意的答案.基本上,我有一个带数据的TListview(style = vsReport).有时,我必须更新它,因此,我必须清除列表视图并使用更新的数据再次填充它.
但是,当我这样做时,滚动条位置重置为0.我希望能够在清除之前获得滚动条位置并将其设置回原来的状态.如果更新的数据与旧数据具有完全相同的行数,我需要滚动条与之前的位置完全相同;如果没有,我只需要它和以前在同一个地方或多或少.
看起来很简单吧?然而,我发现的只有TopItem和MakeVisible的黑客或调整.有没有适当的方法来做到这一点?
谢谢!
解决方法
清除前保存顶部项目,
FSaveTop := ListView1.TopItem;
更新后,滚动列表视图,以便保存的顶部项目的“y”位置为0(标题高度):
var R: TRect; begin if Assigned(FSaveTop) then begin // account for header height GetWindowRect(ListView_GetHeader(ListView1.Handle),R); ListView1.Scroll(0,FSaveTop.Position.Y - (R.Bottom - R.Top)); end; end;
实际上,由于您要重新填充列表视图,因此您必须设计一种机制来查找您想要位于顶部的项目,而不是保存对它的引用.
如果你不喜欢通过’top item’修改滚动位置,因为像SetScrollInfo,SetScrollPos这样的函数不会更新控件的客户区域,你可以在清除列表之前使用GetScrollInfo来获取TScrollInfo的’nPos’,然后在填充后用’SB_LINEDOWN`发送那么多WM_VSCROLL消息.
保存滚动位置:
var FPos: Integer; SInfo: TScrollInfo; begin SInfo.cbSize := SizeOf(SInfo); SInfo.fMask := SIF_ALL; GetScrollInfo(ListView1.Handle,SB_VERT,SInfo); FPos := SInfo.nPos; ...
填充后,滚动(假设滚动位置为0):
var R: TRect; begin ... R := ListView1.Items[0].DisplayRect(drBounds); ListView1.Scroll(0,FPos * (R.Bottom - R.Top));
要么,
var i: Integer; begin ... for i := 1 to FPos do SendMessage(ListView1.Handle,WM_VSCROLL,SB_LINEDOWN,0);