Problem z sortowanim liczb w TListViev

dział ogólny

Problem z sortowanim liczb w TListViev

Nowy postprzez Darek_C++ » piątek, 25 lipca 2008, 14:43

Witam Ponownie Cyfrowego Barona oraz innych użytkowników forum.

Mam problem z sortowaniem kolumn w TListView, a mianowicie po kliknięciu w nagłówki kolumn sortowanie jest dobre jeśli w kolumnie są wartości słowne, ale jeśli same liczby na czym mi też zależy niestety sortowanie jest błędne.
Zamieszczam kod:
Kod: Zaznacz cały
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
int sort; bool column[20]; int sort2; bool column2[20];
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender)
{
    TListColumn *ListCol;
    TListItem *ListIt;

    ListIt = ListView1->Items->Add();
    ListIt->Caption = "Beata";
    ListIt->SubItems->Add("30");

    ListIt = ListView1->Items->Add();
    ListIt->Caption = "Iwona";
    ListIt->SubItems->Add("138910");

    ListIt = ListView1->Items->Add();
    ListIt->Caption = "Syliwa";
    ListIt->SubItems->Add("60370");

    ListIt = ListView1->Items->Add();
    ListIt->Caption = "Aneta";
    ListIt->SubItems->Add("70");

    ListIt = ListView1->Items->Add();
    ListIt->Caption = "Zenobia";
    ListIt->SubItems->Add("40");

    ListIt = ListView1->Items->Add();
    ListIt->Caption = "FIlona";
    ListIt->SubItems->Add("370");

}
//---------------------------------------------------------------------------

void __fastcall TForm1::ListView1ColumnClick(TObject *Sender,
      TListColumn *Column)
{
   sort=Column->Index;
   ((TCustomListView *)Sender)->AlphaSort();
   if (column[sort]==false) column[sort]=true;
   else column[sort]=false;   
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ListView1Compare(TObject *Sender, TListItem *Item1,
      TListItem *Item2, int Data, int &Compare)
{
   if (column[sort]==true)
   {
      if (sort==0)
      Compare=CompareText(Item1->Caption,Item2->Caption);
      else
      Compare=CompareText(Item1->SubItems->Strings[sort-1],Item2->SubItems->Strings[sort-1]);
      }
      else
      {
      if (sort==0)
      Compare=CompareText(Item2->Caption,Item1->Caption);
      else
      Compare=CompareText(Item2->SubItems->Strings[sort-1],Item1->SubItems->Strings[sort-1]);
   }   
}
//---------------------------------------------------------------------------

Oraz dla ułatwienia cały projekt z BCB6.
Projekt Sortowania
ListViewSortowanie.rar


Mam nadzieję, że ktoś z Was pomoże mi w tej kwestii sortowania.

Pozdrawiam
Nie masz wystarczających uprawnień, aby zobaczyć pliki załączone do tego postu.
Avatar użytkownika
Darek_C++
Elektrowied
Elektrowied
 
Posty: 454
Dołączył(a): piątek, 25 lipca 2008, 14:33
Podziękował : 66
Otrzymał podziękowań: 4
System operacyjny: Windows XP Pro SP2
Kompilator: Turbo Explorer C++
Gadu Gadu: 0
    NieznanyNieznana

Re: Problem z sortowanim liczb w TListViev

Nowy postprzez Cyfrowy Baron » piątek, 25 lipca 2008, 16:47

Funkcja AlphaSort traktują liczby również jako tekst i sortuje je np. tak: 1, 10, 2, 3, 4, 40, itd..., czyli tekst., jest o tym napisane w plikach pomocy do BCB (czasami warto tam zajrzeć).
Jeżeli chcesz posortować liczby jako liczby musisz użyć funkcji CustomSort, ale tutaj już trzba stworzyć dwie funkcje pomocnicze. Opis korzystania z funkcji CustomSort znajduje się ► patrz serwis: Cyfrowy Baron w dziale: porady | ListBox | Sortowanie listy wg. różnych kryteriów.



Bez zagłębiania się w problem, masz tutaj gotowy kod:

Kod: Zaznacz cały
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
int sort; bool column[20]; int sort2; bool column2[20];
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
#pragma warn +csu
int foo(int u, int i)
{
int z = u - i;
return z;
}
//--------------------------------
int __stdcall CustomSortDbInt(long Item1, long Item2, long lParam)
{
String cTemp1 = ((TListItem *)Item1)->SubItems->Strings[0];
String cTemp2 = ((TListItem *)Item2)->SubItems->Strings[0];
int rTemp1 = cTemp1.Trim().ToIntDef(0);
int rTemp2 = cTemp2.Trim().ToIntDef(0);
if(!(BOOL)lParam) return -foo(rTemp1, rTemp2);
return foo(rTemp1, rTemp2);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
    TListColumn *ListCol;
    TListItem *ListIt;

    ListIt = ListView1->Items->Add();
    ListIt->Caption = "Beata";
    ListIt->SubItems->Add("30");

    ListIt = ListView1->Items->Add();
    ListIt->Caption = "Iwona";
    ListIt->SubItems->Add("138910");

    ListIt = ListView1->Items->Add();
    ListIt->Caption = "Syliwa";
    ListIt->SubItems->Add("60370");

    ListIt = ListView1->Items->Add();
    ListIt->Caption = "Aneta";
    ListIt->SubItems->Add("70");

    ListIt = ListView1->Items->Add();
    ListIt->Caption = "Zenobia";
    ListIt->SubItems->Add("40");

    ListIt = ListView1->Items->Add();
    ListIt->Caption = "FIlona";
   ListIt->SubItems->Add("370");
}
//---------------------------------------------------------------------------

void __fastcall TForm1::ListView1ColumnClick(TObject *Sender,
      TListColumn *Column)
{
   sort = Column->Index;
   if(Column->Index == 0)
     ((TCustomListView *)Sender)->AlphaSort();
   else
   {
    static bool tmp = false;
    ((TCustomListView *)Sender)->CustomSort(CustomSortDbInt, (long)tmp);
    tmp = !tmp;
   }
   
   if (column[sort]==false) column[sort]=true;
   else column[sort]=false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ListView1Compare(TObject *Sender, TListItem *Item1,
      TListItem *Item2, int Data, int &Compare)
{
   if (column[sort]==true)
   {
      if (sort==0)
      Compare=CompareText(Item1->Caption,Item2->Caption);
      else
      Compare=CompareText(Item1->SubItems->Strings[sort-1],Item2->SubItems->Strings[sort-1]);
      }
      else
      {
      if (sort==0)
      Compare=CompareText(Item2->Caption,Item1->Caption);
      else
      Compare=CompareText(Item2->SubItems->Strings[sort-1],Item1->SubItems->Strings[sort-1]);
   }
}
//---------------------------------------------------------------------------




Funkcja CustomSortDbInt została skonstruowana w taki sposób, że nie uwzględnia pierwszej kolumny (Column 0). Pisząc ten kod zakładałem, że w pierwszej kolumnie zawsze będzie tekst, a kolejne kolumny zawierają liczby. Możesz to oczywiście sobie dowolnie przerabiać.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4730
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 444
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    NieznanyNieznana

Re: Problem z sortowanim liczb w TListViev

Nowy postprzez Darek_C++ » piątek, 25 lipca 2008, 16:53

Wielkie dzięki Cyfrowy Baronie! Jak zwykle Okazałeś się niezawodnY
Pozdrawiam

PS Dobrze, że Reaktywowałeś forum ;)
Avatar użytkownika
Darek_C++
Elektrowied
Elektrowied
 
Posty: 454
Dołączył(a): piątek, 25 lipca 2008, 14:33
Podziękował : 66
Otrzymał podziękowań: 4
System operacyjny: Windows XP Pro SP2
Kompilator: Turbo Explorer C++
Gadu Gadu: 0
    NieznanyNieznana

Re: Problem z sortowanim liczb w TListViev

Nowy postprzez Darek_C++ » piątek, 25 lipca 2008, 22:46

Niestety, ale pojawił się problem, a mianowicie jeśli kolumn będzie więcej niż dwie sortowanie dla pozostałych przestaje działać.
Kod: Zaznacz cały
// więcej kolumn i sortowanie tylko dziala dla pierwszej :(
void __fastcall TForm1::FormCreate(TObject *Sender)
{
   TListColumn *ListCol;
   TListItem *ListIt;

   ListIt = ListView1->Items->Add();
   ListIt->Caption = "Beata";
   ListIt->SubItems->Add("Katowice");
   ListIt->SubItems->Add("30");

   ListIt = ListView1->Items->Add();
   ListIt->Caption = "Iwona";
   ListIt->SubItems->Add("Poznań");
   ListIt->SubItems->Add("138910");

   ListIt = ListView1->Items->Add();
   ListIt->Caption = "Syliwa";
   ListIt->SubItems->Add("Warszawa");
   ListIt->SubItems->Add("60370");

   ListIt = ListView1->Items->Add();
   ListIt->Caption = "Aneta";
   ListIt->SubItems->Add("Antoninek");
   ListIt->SubItems->Add("70");

   ListIt = ListView1->Items->Add();
   ListIt->Caption = "Zenobia";
   ListIt->SubItems->Add("Inowrocław");
   ListIt->SubItems->Add("40");

   ListIt = ListView1->Items->Add();
   ListIt->Caption = "FIlona";
   ListIt->SubItems->Add("Lublin");
   ListIt->SubItems->Add("370");
}


Docelowo powinno być możliwe sortowanie w kolumnach stringowych, liczbowych oraz zawierających daty w formacie 2008-07-22 22:23:54 lub data jako sama data bez minut 2008-05-12 i była by możliwość wybrania typu sortowania dla danej kolumny niezależnie od ich ilości. Czyli chodzi o uniwersalną implementacje tego sortowania.

Wiem, że dużo wymagam, ale wyjątkowo mi ta kwestia nie wychodzi :(
Avatar użytkownika
Darek_C++
Elektrowied
Elektrowied
 
Posty: 454
Dołączył(a): piątek, 25 lipca 2008, 14:33
Podziękował : 66
Otrzymał podziękowań: 4
System operacyjny: Windows XP Pro SP2
Kompilator: Turbo Explorer C++
Gadu Gadu: 0
    NieznanyNieznana

Re: Problem z sortowanim liczb w TListViev

Nowy postprzez Cyfrowy Baron » sobota, 26 lipca 2008, 09:51

Sądziłem, że mając ten kod, poradzisz sobie z jego przerobieniem, no ale skoro nie poradziłeś sobie, to pewnie będziesz częstym gościem tego forum.
Odpowiedź na Twój problem znajduje się w zasadzie już w ► patrz serwis: Cyfrowy Baron, w poradzie o której wspomniałem, ale jak się domyślam, nawet tam nie zajrzałeś. :|((

Co do sortowania daty, to należy ją traktować jako tekst nie liczbę, a będzie sortowana prawidłowo.
Stworzyłem uniwersalną funkcję sortującą, zrezygnowałem przy tym z funkcji AlphaSort(). Teraz funkcja sprawdza która kolumna została kliknięta i odpowiednio do tego się przełącza. Funkcja sprawdza również czy w kolumnie znajduje się tekst czy liczba i w zależności od zawartości zmienia sposób sortowania, w efekcie jeżeli kolumna zawiera wartości mieszane, czyli część wierszy w kolumnie zawiera tekst, a część liczby, to najpierw zostaną posortowana liczby jako liczby, a potem tekst jako tekst. W zależności od potrzeb może to być pożądane lub nie. Jeżeli w Twoim przypadku nie będzie to pożądane, to musisz zastanowić się trochę nad mechanizmem działania funkcji, gdyż zmiana tego stanu rzeczy jest banalnie (wręcz śmiesznie) prosta.
Zmienna sDirection służy do sterowania kierunkiem sortowania, a konkretnie jego automatycznym przełączaniem - rosnąco - malejąco. Jest to zmienna globalna i nie można jej zawrzeć wewnątrz funkcji, gdyż sam fakt zmiany jej stanu (true - fale) wewnątrz tejże funkcji sprawi, że przestanie ona działać prawidłowo.


Gotowiec:
Kod: Zaznacz cały
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
int sort; bool column[20]; int sort2; bool column2[20];
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
#pragma warn +csu
int foo(double u, double i)
{
double z = u - i;
return z;
}
//--------------------------------
BOOL sDirection = true;
int __stdcall CustomSortDbInt(long Item1, long Item2, long lParam)
{
bool test = true;
String cTemp1, cTemp2;
if(lParam == -1)
{
  cTemp1 = ((TListItem *)Item1)->Caption;
  cTemp2 = ((TListItem *)Item2)->Caption;
}
else
{
  cTemp1 = ((TListItem *)Item1)->SubItems->Strings[lParam];
  cTemp2 = ((TListItem *)Item2)->SubItems->Strings[lParam];
}

double rTemp1, rTemp2;

try{ rTemp1 = cTemp1.Trim().ToDouble(); } catch(...){ test = false;}
try{ rTemp2 = cTemp2.Trim().ToDouble(); } catch(...){ test = false;}

if(!test)
{
  if(!sDirection) return CompareText(cTemp1.Trim(), cTemp2.Trim());
  return -CompareText(cTemp1.Trim(), cTemp2.Trim());
}

if(!sDirection) return foo(rTemp1, rTemp2);
return -foo(rTemp1, rTemp2);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
    TListColumn *ListCol;
   TListItem *ListIt;

   ListIt = ListView1->Items->Add();
   ListIt->Caption = "Beata";
   ListIt->SubItems->Add("Katowice");
   ListIt->SubItems->Add("30");
   ListIt->SubItems->Add("100");
   ListIt->SubItems->Add("2008-07-22");
   ListIt->SubItems->Add("1000");

   ListIt = ListView1->Items->Add();
   ListIt->Caption = "Iwona";
   ListIt->SubItems->Add("Poznań");
   ListIt->SubItems->Add("138910");
   ListIt->SubItems->Add("50,1");
   ListIt->SubItems->Add("2008-04-11");
   ListIt->SubItems->Add("tekst 1");

   ListIt = ListView1->Items->Add();
   ListIt->Caption = "Syliwa";
   ListIt->SubItems->Add("Warszawa");
   ListIt->SubItems->Add("60370");
   ListIt->SubItems->Add("111");
   ListIt->SubItems->Add("2006-01-31");
   ListIt->SubItems->Add("17");

   ListIt = ListView1->Items->Add();
   ListIt->Caption = "Aneta";
   ListIt->SubItems->Add("Antoninek");
   ListIt->SubItems->Add("70");
   ListIt->SubItems->Add("90");
   ListIt->SubItems->Add("2007-11-01");
   ListIt->SubItems->Add("5");

   ListIt = ListView1->Items->Add();
   ListIt->Caption = "Zenobia";
   ListIt->SubItems->Add("Inowrocław");
   ListIt->SubItems->Add("40");
   ListIt->SubItems->Add("21");
   ListIt->SubItems->Add("2008-04-06");
   ListIt->SubItems->Add("Antoni");

   ListIt = ListView1->Items->Add();
   ListIt->Caption = "FIlona";
   ListIt->SubItems->Add("Lublin");
   ListIt->SubItems->Add("370");
   ListIt->SubItems->Add("113");
   ListIt->SubItems->Add("2008-01-31");
   ListIt->SubItems->Add("Ananas");
}
//---------------------------------------------------------------------------

void __fastcall TForm1::ListView1ColumnClick(TObject *Sender,
      TListColumn *Column)
{
sort = Column->Index;

sDirection = !sDirection;
((TCustomListView *)Sender)->CustomSort(CustomSortDbInt, (long)Column->Index - 1);

if (column[sort]==false) column[sort]=true;
else column[sort]=false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ListView1Compare(TObject *Sender, TListItem *Item1,
      TListItem *Item2, int Data, int &Compare)
{
   if (column[sort]==true)
   {
      if (sort==0)
      Compare=CompareText(Item1->Caption,Item2->Caption);
      else
      Compare=CompareText(Item1->SubItems->Strings[sort-1],Item2->SubItems->Strings[sort-1]);
      }
      else
      {
      if (sort==0)
      Compare=CompareText(Item2->Caption,Item1->Caption);
      else
      Compare=CompareText(Item2->SubItems->Strings[sort-1],Item1->SubItems->Strings[sort-1]);
   }
}
//---------------------------------------------------------------------------
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4730
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 444
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    NieznanyNieznana

Re: Problem z sortowanim liczb w TListViev

Nowy postprzez Darek_C++ » sobota, 26 lipca 2008, 10:25

Naprawdę jesteś "Cyfrowy Baron"! Jeszcze raz wielkie dzięki za pomoc no i gotowca.

Warto jeszcze dodać, że przedstawiony kod działa jako program, bo w środowisku testowym zgłaszany jest wyjątek
Kod: Zaznacz cały
try{ rTemp1 = cTemp1.Trim().ToDouble(); } catch(...){ test = false;}

zresztą obsługiwany przez kod, co nieźle mnie zmyliło na samym początku.
/* to pewnie będziesz częstym gościem tego forum.*/
Odpowiem w ten sposób -> dobrze, że Reaktywowałeś forum, bo Twoja pomoc bardzo ułatwia tworzenie aplikacji w BCB C++ ;)
Avatar użytkownika
Darek_C++
Elektrowied
Elektrowied
 
Posty: 454
Dołączył(a): piątek, 25 lipca 2008, 14:33
Podziękował : 66
Otrzymał podziękowań: 4
System operacyjny: Windows XP Pro SP2
Kompilator: Turbo Explorer C++
Gadu Gadu: 0
    NieznanyNieznana

Re: Problem z sortowanim liczb w TListViev

Nowy postprzez Cyfrowy Baron » sobota, 26 lipca 2008, 10:45

Metoda try{;}catch(...){;} zawsze zgłasza wyjątek, gdyż taka jest jej konstrukcja: spróbuj wykonać polecenie zawarte w pierwszym bloku (instrukcja warunkowa), a jeżeli się nie uda wykonaj polecenie zawarte w drugim bloku (instrukcja bezwarunkowa). Komunikaty wyjątku są zgłaszane tylko gdy program zostanie uruchomiony w środowisku. Można te komunikaty wyłączyć w ustawieniach środowiska BCB.
Zdecydowałem się na użycie tej metody, żeby nie tworzyć dodatkowej funkcji sprawdzającej czy mamy do czynienia z tekstem czy też z liczbą.



Kolejna sprawa o której zapomniałem wspomnieć, jeżeli używasz mojej funkcji, to używanie zdarzenie OnCompare i tej zmiennej sort do zmiany kierunku sortowania jest tutaj zupełnie zbędne, gdyż to zdarzenie ma zastosowanie tylko w przypadku funkcji AlphaSort(). Jak to wyjaśniałem w poprzednim poście, u mnie kierunkiem sortowania steruje zmienna globalna sDirection.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4730
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 444
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    NieznanyNieznana

Re: Problem z sortowanim liczb w TListViev

Nowy postprzez Darek_C++ » sobota, 26 lipca 2008, 11:28

Cyfrowy Baron napisał(a):Metoda try{;}catch(...){;} zawsze zgłasza wyjątek, gdyż taka jest jej konstrukcja:

Tutaj wyraziłeś się niejasno. Blok try{}catch(...) nie zgłasza wyjątku, ale go przechwytuje oczywiście wtedy jeśli w klamrach try{}catch(...){} wywołamy funkcje, metodę która takowy wyjątek zgłosi np:
Kod: Zaznacz cały
try
{
   String xx="tekst";
   xx.ToInt();
   // Lub wyrzucenie wyjątku
   // throw Exception("Komunikat Wujątku");
}
catch(Exception &e)
{
   ShowMessage(e.Message);
}
Avatar użytkownika
Darek_C++
Elektrowied
Elektrowied
 
Posty: 454
Dołączył(a): piątek, 25 lipca 2008, 14:33
Podziękował : 66
Otrzymał podziękowań: 4
System operacyjny: Windows XP Pro SP2
Kompilator: Turbo Explorer C++
Gadu Gadu: 0
    NieznanyNieznana

Re: Problem z sortowanim liczb w TListViev

Nowy postprzez Cyfrowy Baron » sobota, 26 lipca 2008, 12:09

Ani o jedno, ani o drugie mi nie chodziło, tylko o to, że jeżeli skompilujesz program i uruchomisz go w środowisku BCB, to kompilator zgłosi ostrzeżenie, gdy natknie się na wyjątek, tak więc "zgłaszanie wyjątku", było swego rodzaju nieprecyzyjnym skrótem myślowym.
Zakładam, że każdy korzystający z tej metody wie o tym, że przechwytuje ona wyjątki.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4730
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 444
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    NieznanyNieznana


  • Podobne tematy
    Odpowiedzi
    Wyświetlone
    Ostatni post

Powrót do Ogólne problemy z programowaniem

Kto przegląda forum

Użytkownicy przeglądający ten dział: Brak zalogowanych użytkowników i 3 gości

cron