Problem z TListBox

0

Witam. Mam poważny problem z TListBoxem. Postaram się go opisać
Mam włączony multiselect, używam tego komponentu w trybie lbVirtualOwnerDraw, wszystkie funkcje są obsłużone odpowiednio i ListBox korzysta z mojej struktury danych. Mam też własne sortowanie przy dodawaniu nowych elementów (nie wiadomo z góry gdzie się pojawi nowy element).
Mój problem polega na tym, że gdy użytkownik zaznacza i scrooluje liste w momencie gdy zachodzi jakaś zmiana na liście (przypisywany jest element ListBox->Count), całe to zaznaczenie znika, a lista jest przewijana na początek.
Co tu jeszcze dodac... używam BCB6.
Myślałem przez pare dni nad tym problemem i mam pare rozwiązań, ale nie za dobrych:

  1. Zrobić we własnej strukturze danych nowy element isSelected (pamietaj, ze multiselect jest mozliwy) i przy rysowaniu odpowiednio go stosowac. Cos takiego + zapamietywanie ItemIndex daje mozliwosc zapamietania elementów, problem jest nadal ze scroolowaniem - w tym wariancie nie wiem do konca jak sie uchronic przez przewijaniem lub jak zapamietac polozenie suwaka (?) i scroolowac po odswiezeniu w miejsce w ktorym był przed odświezaniem. Ten wariant jednak, nawet jesli sie to uda zaimplementowac, ma wade (chwilowe mrugniecie przy odswiezaniu co bedzie zauwazalne pewnie)

  2. Zrezygnowac z trybu wirtualnego ale nadal korzystac z mojej struktury danych przy rysowaniu (bardzo mi sie ten pomysl nie podoba bo lista znacznie zmniejszy wydajnosc).

  3. Zablokowac scroolowanie listy na poczatek. Ale jak? :>

Prosze o pomoc i zrozumienie, brak mi juz wlasnego pomyslu na to.

0

ListBox ma właściwość TopIndex, która wskazuje, na index pierwszego widocznego Itemu. Możesz to zapamiętywać, a po dodaniu elementu przypisywać do tej właściwości zapamiętaną wartość. Jednak to rozwiązanie ma wadę, bo widać, że lista jest przewijana tam i z powrotem.
Niestety nie znam lepszego sposbu.

0

Faktycznie jest to pewne rozwiazanie, ale nie usuwa problemu. To tylko kolejna próba "obejścia" tego przewijania ;P

Tak sobie myślę... jakby dało się na moment odświeżania zamrozić TListBox, a poźniej odmrozić (chodzi o co jest wyświetlanie na monitorze, a nie o rzeczywisty stan w pamięci) to mogłoby to w jakimś stopniu pomóc... Ale czy da się coś takiego zrobić?

0
Alpha napisał(a)

jakby dało się na moment odświeżania zamrozić TListBox, a poźniej odmrozić

LockWindowUpdate()

0

kolega ŁF napisał że LockWindowUpdate pomoże, i rzeczywiście okno w chwili ustawienia właściwości Count nie zostanie odświeżone ale przy pierwszym odświeżeniu zaznaczenie znika. Poniższy kod pokazuje co się dzieje podczas ustawiania właściwości Count:

procedure TCustomListBox.SetCount(const Value: Integer);
var
  Error: Integer;
begin
  if Style in [lbVirtual, lbVirtualOwnerDraw] then
  begin
    // Limited to 32767 on Win95/98 as per Win32 SDK
    Error := SendMessage(Handle, LB_SETCOUNT, Value, 0);
    if (Error <> LB_ERR) and (Error <> LB_ERRSPACE) then
      FCount := Value
    else
      raise Exception.CreateFmt(SErrorSettingCount, [Name]);
  end
  else
    raise Exception.CreateFmt(SListBoxMustBeVirtual, [Name]);
end;

z moich obserwacji wynika że właśnie wysłanie komunikatu LB_SETCOUNT powoduje wyczyszczenie zaznaczenia i przesunięcie suwaka na początek. Jeżeli piszesz to jako komponent to jedynym rozwiązaniem jest nadpisanie procedurę WndProc i przechwycić ten komunikat zanim poczyni zniszczenia, zaimplementować przepisywanie zaznaczenia po wykonaniu LB_SETCOUNT. Przypuszczam że kontrolka tak postępuje na wieść o komunikacie LB_SETCOUNT dlatego że nie wie co się zmieniło bo już dłużej nie kontroluje danych wyświetlanych. Nie wie w którym miejscu został dodany/usunięty element co implikuje niewiedze jak zmodyfikować tablice z zaznaczeniami.

0
dj napisał(a)

kolega ŁF napisał że LockWindowUpdate pomoże, i rzeczywiście okno w chwili ustawienia właściwości Count nie zostanie odświeżone ale przy pierwszym odświeżeniu zaznaczenie znika.

Tak, pomogło i to bardzo. Mam zaimplementowaną własną klasę tylko do obsługi i przechowywania danych w ListBoxie, dodam tam jedną zmienną (lub flagę w zbiorze), tak jak już opisałem w ad.2. Jeśli flaga będzie zapalona - element jest zaznaczony; flaga zgaszona - emelent niezaznaczony.
Takie rozwiązanie + sprytna kontrola odświeżania z LockWindowUpdate pozwoli uniknąc pisania wirtualnych funkcji w ew. nowym komponencie z klasą bazową TListBox i przechwytywania komunikatów.
Zrobie testy i przedstawie ich wyniki (chodzi o szybkość zastosowania takiego rozwiązania w porównaniu z innymi).

1 użytkowników online, w tym zalogowanych: 0, gości: 1