Delphi-получить и установить положение полосы прокрутки ListView
Это может показаться глупым и простым вопросом, и все же я не смог найти удовлетворительного ответа. В принципе, у меня есть TListview (style = vsReport) с данными. Иногда мне приходится обновлять его, и поэтому я должен очистить listview и снова заполнить его обновленными данными.
однако, когда я это делаю, позиция полосы прокрутки сбрасывается до 0. Я хотел бы иметь возможность получить позицию полосы прокрутки до очистки и установить ее обратно в то, что это было до. Если обновленные данные имеют точно такое же количество строк, как и старые данные, мне нужно, чтобы полоса прокрутки была в том же положении, что и раньше; если нет, мне просто нужно, чтобы она была более или менее в том же месте, что и раньше.
кажется легким, не так ли? Тем не менее, все, что я нашел, это хаки или твики с TopItem и MakeVisible. Есть ли какой-нибудь подходящий метод для этого?
спасибо!
2 ответов
сохранить верхний элемент перед очисткой,
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;
на самом деле, поскольку вы повторно заполняете listview, вам нужно разработать механизм, чтобы найти, какой элемент вы хотите быть наверху, а не сохранять ссылку на него.
Если вам не нравится изменять положение прокрутки через "верхний элемент", так как функции, такие как SetScrollInfo
, SetScrollPos
не будет обновлять клиентскую область элемента управления, вы можете использовать GetScrollInfo
чтобы получить "НКО"TScrollInfo
перед очисткой списка, а затем отправить, что многие WM_VSCROLL
сообщения с SB_LINEDOWN после заполнения.
сохранить позиции прокрутки:
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);
type
TForm1 = class(TForm)
ListView1: TListView;
Timer1: TTimer;
Timer2: TTimer;
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure Timer2Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
pb:array [0..20] of Tprogressbar;
i:integer;
mintop,spacetop,readtop:integer;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
mintop:=19;
spacetop:=14;
for i:=0 to 20 do
begin
listview1.AddItem('Item no'+inttostr(i),nil);
pb[i]:=Tprogressbar.create(self);
pb[i].Parent:=listview1;
pb[i].width:=120;
pb[i].height:=14;
pb[i].top:=mintop+i*spacetop;
pb[i].position:=i*5;
pb[i].Left:=listview1.Column[0].Width;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
z,FPos: Integer;
SInfo: TScrollInfo;
begin
SInfo.cbSize := SizeOf(SInfo);
SInfo.fMask := SIF_ALL;
GetScrollInfo(ListView1.Handle, SB_VERT, SInfo);
FPos := SInfo.nPos;
form1.caption:='FPOS='+inttostr(fpos);
if fpos>0 then
begin
for z:=0 to 11 do
begin
pb[z+fpos-1].Top:=mintop+z*spacetop;
form1.caption:=form1.caption+' Z='+inttostr(z)+' !';
end;
end;
end;
procedure TForm1.Timer2Timer(Sender: TObject);
var x:integer;
begin
for x:=0 to 20 do
begin
pb[x].Position:=Pb[x].position+1;
end;
end;
end.
/// Code by BRTH1 - Simao Coelho - Portugal