Sort TListView Columns with Dates as Strings ? lazarus / 31-LCL ---------------------------------------------------------------------- holmesshanea · 2015-12-26 03:45 I have 5 columns in my TListView, 2 of which are displaying strings representing dates. However, every row is not filled with data. How can I sort my date fields so that the filled date cells are listed first in the ascending order and the blank date fields are listed first in descending order ? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TfrmMain.lvwMountainsColumnClick(Sender: TObject; Column: TListColumn  );begin ColumnToSort := Column.Index; Column.Tag:= Column.Tag * -1; if Column.Tag = 0 then Column.Tag:=1; (Sender as TCustomListView).AlphaSort;end; Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TfrmMain.lvwMountainsCompare(Sender: TObject; Item1,  Item2: TListItem; Data: Integer; var Compare: Integer); function CompareTextAsInteger(const s1, s2: string): Integer;begin  Result := CompareValue(StrToInt(s1), StrToInt(s2));end; function CompareTextAsDateTime(const s1, s2: string): Integer;begin  Result := CompareDateTime(StrToDateTime(s1), StrToDateTime(s2))end; var  D: TDateTime;begin case ColumnToSort of    0:  Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareTextAsInteger(Item1.Caption, Item2.Caption);    1,4:  Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareText(Item1.subitems[0], Item2.subitems[0]);    2,3:  begin       if (TryStrToDate(Item1.subitems[1], D, 'mm/dd/yy')) AND (TryStrToDate(Item2.subitems[1], D, 'mm/dd/yy')) then          Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareTextAsDateTime(Item1.subitems[1],Item2.subitems[1])         else           Compare:= 0;        end;  end;end; ---------------------------------------------------------------------- taazz · 2015-12-26 05:52 So you are using the tag to define the sort order in that case I would simple change the CompareTextasDateTime something along the lines of Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TfrmMain.lvwMountainsCompare(Sender: TObject; Item1,  Item2: TListItem; Data: Integer; var Compare: Integer); function CompareTextAsInteger(const s1, s2: string): Integer;begin  Result := CompareValue(StrToInt(s1), StrToInt(s2));end; function CompareTextAsDateTime(const s1, s2: string): Integer;begin  if s1<> s2 then begin    if s1 = '' then exit(1);    if s2 = '' then exit(-1);  end;  Result := CompareDateTime(StrToDateTime(s1), StrToDateTime(s2))end; var  D: TDateTime;begin case ColumnToSort of  0:  Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareTextAsInteger(Item1.Caption, Item2.Caption);    1,4:  Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareText(Item1.subitems[0], Item2.subitems[0]);    2,3:  begin       if (TryStrToDate(Item1.subitems[1], D, 'mm/dd/yy')) AND (TryStrToDate(Item2.subitems[1], D, 'mm/dd/yy')) then           Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareTextAsDateTime(Item1.subitems[1],Item2.subitems[1])         else            Compare:= 0;        end;  end;end; ---------------------------------------------------------------------- holmesshanea · 2015-12-26 20:54 Thank you for you reply! If I modify my CompareTextAsDateTime routine to include your change, it still does not sort them properly. i.e. I need them to show dates first in ascending order and empty strings last. And the reverse for descending. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function CompareTextAsDateTime(const s1, s2: string): Integer;begin  if s1<> s2 then begin    if s1 = '' then exit(1);    if s2 = '' then exit(-1);  end;  Result := CompareDateTime(StrToDateTime(s1), StrToDateTime(s2))end; var  D: TDateTime;begin case ColumnToSort of  0:  Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareTextAsInteger(Item1.Caption, Item2.Caption);    1,4:  Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareText(Item1.subitems[0], Item2.subitems[0]);    2,3:  begin       if (TryStrToDate(Item1.subitems[1], D, 'mm/dd/yy')) AND (TryStrToDate(Item2.subitems[1], D, 'mm/dd/yy')) then          Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareTextAsDateTime(Item1.subitems[1],Item2.subitems[1])         else           Compare:= 0;        end;  end;end; On another note, if I try your code with the TryStrToDate's commented out, I get errors ---------------------------------------------------------------------- taazz · 2015-12-26 21:25 OK. Did the code executed as expected? Were s1,s2 empty strings or they had spaces or other invisible characters in them? If the code executed as expected did the procedure exited on exit(-/1) or it executed the CompareDateTime as well? ---------------------------------------------------------------------- holmesshanea · 2015-12-26 23:11 Please see attached screenshots Screenshot 1 shows the test values when I run the application. Screenshot 2 shows the test values when I click the Date column the first time Screenshot 3 shows the test values when I click the Date column the second time ---------------------------------------------------------------------- taazz · 2015-12-27 00:24 Can you provide a small sample application that demonstrates the problem? In this case the data play a huge role so it would be of benefit to share part of them in a csv or dbf file along with the code. The date values must be exact everything else can be random or sequential values. From your screenshots I see the problem but most of my questions remain answered. ---------------------------------------------------------------------- holmesshanea · 2015-12-27 01:35 Here is a demo ---------------------------------------------------------------------- taazz · 2015-12-27 02:46 Here are my changes, seems that they work as expected now (possibly) if I have misunderstood something I'll be happy to clarify or change it as needed. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  procedure TfrmMain.lvwMountainsColumnClick(Sender: TObject; Column: TListColumn);begin //Clear the active sort column order. if (ColumnToSort > -1) and (ColumnToSort < lvwMountains.ColumnCount) then   lvwMountains.Column[ColumnToSort].Tag := 0; // set sorting to the new column and order. ColumnToSort := Column.Index; Column.Tag := Column.Tag * -1; if Column.Tag = 0 then Column.Tag := 1; (Sender as TCustomListView).AlphaSort;end; procedure TfrmMain.lvwMountainsCompare(Sender: TObject; Item1,  Item2: TListItem; Data: Integer; var Compare: Integer); function CompareTextAsInteger(const s1, s2: string): Integer;begin  Result := CompareValue(StrToInt(s1), StrToInt(s2));end; function CompareTextAsDateTime(const s1, s2: string): Integer;var  D1, D2 : TDateTime;begin  Result := 0;  if Trim(s1) <> Trim(s2) then begin    if trim(s1) = '' then exit(1);    if Trim(s2) = '' then exit(-1);  end;  if TryStrToDate(s1, D1, 'mm/dd/yy') AND TryStrToDate(s2, D2, 'mm/dd/yy') then    Result := CompareDateTime(D1, D2);end; //var//  D: TDateTime;begin case ColumnToSort of    0:  Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareTextAsInteger(Item1.Caption, Item2.Caption);    1,4:  Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareText(Item1.subitems[0], Item2.subitems[0]);    2,3:  begin          //if (TryStrToDate(Item1.subitems[1], D, 'mm/dd/yy')) AND (TryStrToDate(Item2.subitems[1], D, 'mm/dd/yy')) then              Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareTextAsDateTime(Item1.subitems[1],Item2.subitems[1])            //else            //  Compare := 0;          end;  end;end;  procedure TfrmMain.FormActivate(Sender: TObject);begin  ReadData;  ColumnToSort := -1;end; ---------------------------------------------------------------------- holmesshanea · 2015-12-27 13:36 WOW! That is close to what I need. Thanks again for responding and helping. See the two new screenshots. First click of Date column (screenshot1) and second click of Date column (Screenshot2) See the last set of dates for Summer (Date) and Winter (WDate). Seems that if it is not the 1st row and there are boh dates in the row, it will not sort correctly thanks ---------------------------------------------------------------------- taazz · 2015-12-27 13:48 Sorry I do not understand. The grid is sorted on one column only, it seems that you have sorted it on the "Date" column which looks correct on both screen shots. What is the problem exactly? ---------------------------------------------------------------------- holmesshanea · 2015-12-27 14:28 I genuinely apologize. I was NOT looking at the years. My Bad!  You are correct. It is sorting properly. THANK YOU for all your help!  Much Appreciated! ---------------------------------------------------------------------- holmesshanea · 2015-12-27 15:44 Looks like all columns sort correctly, except the WDate column Let me give you my data file and you can place it in your ProgramsData folder and see the effect you will need to rename ext from ".txt" to ".dat". It wouldnt let me upload a ".dat" file thankx ---------------------------------------------------------------------- taazz · 2015-12-28 02:04 That specific problem was present in your code from the start, before adding any special ordering. To solve it you need to make sure that the correct data are passed to the compareasdate function. eg Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  case ColumnToSort of    0 : Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareTextAsInteger(Item1.Caption, Item2.Caption);    1 : Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareText(Item1.subitems[0], Item2.subitems[0]);    2 : Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareTextAsDateTime(Item1.subitems[1],Item2.subitems[1]);    3 : Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareTextAsDateTime(Item1.subitems[2],Item2.subitems[2]);    4 : Compare := TListView(Sender).Column[ColumnToSort].Tag * CompareText(Item1.subitems[3], Item2.subitems[3]);  end;  Now I feel like I'm doing your work though so this is my last post on the subject. ---------------------------------------------------------------------- holmesshanea · 2015-12-29 16:04 Once again, thank you! I can see how you would feel that way. I am sorry. I truly was working hard on trying to figure out why it wouldnt work. Sometimes you stare at it too long and you just cant see the obvious. I initially had those case statements broken out into all four, I cant remember why i combined them. KRAZY! Thanks again....100% appeciated