ListView - wolne sortowanie, miganie

0

Przesiadam się stopniowo z Delphi na C# i nawet nieźle mi idzie, ale trafiłem na dwa problemy, które zdecydowanie nie leżą po stronie mojego kodu, a po stronie kiepskawej kontrolki ListView.

Co robię?
Wczytuję sobie plik binarny, w zależności od szczęścia (zapisywany w innym programie) mam w nim od 1 do ok. 3000 rekordów (to taka jakby baza danych). Samo wczytywanie rozpracowałem świetnie, jeśli nie sortuję nic, to wczytywanie pliku następuje w mgnieniu oka.

Problem z sortowaniem
Chcę, aby po załadowaniu posortowało elementy wg pierwszej kolumny (zwykłe dane liczbowe), idealnie zaraz po wczytaniu, zanim zawartość się wyświetli. Niby banał. Niestety po aktywacji Sorting --> Ascending w opcjach kontrolki cały proces trwa nawet 6 sekund. Pomyślałem sobie, że może te sortowanie to mega kiks i może wywołuje się po każdym dodaniu itema, albo coś. No to wyłączyłem to i w kod wrzuciłem:

ListView1.Sort();

Zaraz przed EndUpdate(), ale już po dodaniu wszystkich itemów, za pętlą, żeby czasami nie sortował co item. Niestety efekt identyczny - ListView mieli strasznie długo. I tu w porównaniu do ListView z Delphi jest katastrofa. W Delphi ta sama operacja - posortowanie listy wypełnionej uprzednio tymi samymi danymi - zajmowało poniżej 1s.

Problem z wyświetlaniem
Przy przewijaniu ListView całość miga i jest to dość irytujące. W Delphi miałem po prostu DoubleBuffered - True i po problemie. Tu nic takiego nie widzę, nie licząc cacheowania itemów, ale jak mam ubijać mrówkę za pomocą Uzi, to trochę się nie opłaca - nadmiar czasu na wyeliminowanie w sumie drobnego utrudnienia... (poza tym nie wiem, czy akurat Cache załatwiłoby problemy odrysowywania kontrolki - bo generalnie do tego sprowadza się problem z miganiem zawartości przy szybkim przewijaniu).

Czy istnieją jakieś umiarkowanie proste sposoby na załatwienie obydwu problemów? ;)

Jest jeszcze problem trzeci, ale to już jest związane nie z kontrolką a z typem danych, na których operuję, być może jak nic wujek google nie pomoże to założę nowy temat

0

Z listView nie miałem za bardzo do czynienia, ale popróbuj może z SuspendLayout() i ResumeLayout().
A tak dokopiesz się do właściwości doubleBuffered:

PropertyInfo property = typeof(LisView).GetProperty("DoubleBuffered", BindingFlags.NonPublic | BindingFlags.Instance);
property.SetValue(listView1, true, null);

Tylko ciekaw jestem czy to pomoże

0

Ja się wcale nie dziwię, że tyle to trwało. Kwestia użycia odpowiedniego narzędzia.
Prosty kod, Rekord to struktura z trzema polami int a, b, c.

Stopwatch stopwatch = new Stopwatch();

stopwatch.Start();

Rekord[] rekordy = new Rekord[3000];

Random random = new Random();

for (int i = 0, iloscRekordow = rekordy.Length; i < iloscRekordow; i++)
{
    rekordy[i] = new Rekord { a = random.Next(), b = random.Next(), c = random.Next() };
}

Array.Sort(rekordy, (Rekord a, Rekord b) => { return a.a.CompareTo(b.a); });

listView1.Items.Clear();

listView1.BeginUpdate();
for (int i = 0, iloscRekordow = rekordy.Length; i < iloscRekordow; i++)
{
    ListViewItem lvi = new ListViewItem(new string[] { rekordy[i].a.ToString(),
        rekordy[i].b.ToString(), rekordy[i].c.ToString(), });
    listView1.Items.Add(lvi);
}
listView1.EndUpdate();

stopwatch.Stop();

Średni czas na Core 2 Duo 2.53GHz to od 0.15 do 0.2 sekundy, nigdy więcej, z czego najdłużej trwa.. wstawianie elementów do ListView. Czas półtorej sekundy osiągnąłem dopiero przy.. 30 tys.

Problem z twoim kodem jest taki, że używasz ListView do sortowania. A co sortuje ListView? 3000 stringów.

A problem z miganiem przy przewijaniu trzeba rozwiązać tak jak pokazał adams. Jako, że autorzy klasy Control uznali, że to od projektantów kontrolek zależy czy używają double buffering (jako, że spowalnia to proces rysowania), tak twórcy ListView uznali, że nie chcą z niego korzystać :). Możesz też dziedziczyć po ListView, jako, że Control.DoubleBuffered jest protected i wtedy nie trzeba będzie używać Reflection.

0

Dzięki Panowie [browar]
Double Buffered załatwiony, sortowanie załatwione [browar] Sortowanie teraz, po zmianie sposobu działa nawet lepiej, jak w Delphi, ale moja radość może być przedwczesna, bo w Delphi mam jeszcze malowanie po ListView, a tu go jeszcze nie przepisałem - może nie być tak kolorowo jak się w to wgryzę ;) Bez malowania tempo jest świetne!

Pytanie pomocnicze

Za pomocą Array.Sort mogę poustawiać sobie wartości najpierw wg przykładowo kolumny 3, a później (zachowując wcześniejsze efekty pierwszego sortowania) przesortować całość jeszcze wg kolumny 1?
Zacząłem od najbanalniejszego pomysłu, jaki mógł mi przyjść do głowy o 4 w nocy, tj. dać dwa razy Array.Sort z różnymi argumentami, ale jak się okazało to byłoby jednak zbyt łatwe ;)

0

Jeżeli chcesz posortować tą metodą, to trzeba troszkę zmienić tzw. comparer, czyli funkcja, która porównuje dwa dane rekordy.

Array.Sort(rekordy, (a, b) =>
{
    return (a.c == b.c) ? a.a.CompareTo(b.a) : a.c.CompareTo(b.c);
});

Ale np. w Linq korzystając z jego własności wykonania zapytania dopiero w momencie, w którym faktycznie chcemy dane uzyskać, możemy zrobić tak:

var posortowane = from r in rekordy
               orderby r.c
               select r;

posortowane = from r in posortowane
              orderby r.a
              select r;

rekordy = posortowane.ToArray();

Ale można i w miejscu:

var posortowane = from r in rekordy
               orderby r.c, r.a
               select r;

rekordy = posortowane.ToArray();

Można też użyć rozszerzeń Linq, gdzie tutaj możemy skorzystać z wbudowanego comparera (zwykłe int.CompareTo), bo Linq wywoła go na elementach, które mu zwróciliśmy.

rekordy = rekordy.OrderBy(r => { return r.c; })
                 .OrderBy(r => { return r.a; }).ToArray();

Ale pomimo tego, że wszystkie te metody używają QuickSorta, Array.Sort zawsze będzie najbardziej optymalny, bo nie musimy kopiować całej tablicy (co innego, jeżeli pobieralibyśmy te dane np. od razu z bazy danych).

No.. to poznałeś już trochę technik sortowania tablic w C# :), a ja idę się wyspać na jutrzejsze kolokwium z algorytmów ;).

0

Dzięki [browar] Zostałem przy Array.Sort - u mnie sprawdza się świetnie, więc dalej nie kombinowałem ;)

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