menu
  Home  ==>  papers  ==>  vcl  ==>  delphi_sorted_tlistview   

Delphi Sorted tListView - Felix John COLIBRI.


1 - Sorting a tListView

This component will enable the sorting of the rows of a tListView by clicking on the column header. It allows
  • automatic sortingthe columns by clicking on the header
  • left or right justification
  • sizing of columns



2 - Delphi Sortable tListView

2.1 - The Delphi tListView

The Delphi tListView is an encapsulation of the Windows ListView control.

It contains some of the features of the tListBox like Items, TopItem, Clear. But it has many other possibilities, like resizing of the Items, custom drawing etc



2.2 - Sorting a tListView by column

To sort by ²colum, the tListView has
  • an AlphaSort method
  • which requires the writing of an OnCompare event
Whenevet AlphSort requires the comparison of 2 rows, it calls OnCompare where we tell which row is greater or lower than the other.



In our case, our main goal was to compare file information: file name, file size, file date. Therefore we wrote the OnCompare event for those type of values.



2.3 - The sorted listview component

To manage each column, we use an array of t_column_info Records:

Type t_sort_kind=  (e_not_sortede_ascendinge_descending);
     t_column_info=
         Record
           m_column_type: (e_unknown_typee_string_typee_integer_type);
           m_sort_kindt_sort_kind;
         End;

and :

  • t_sort_kind stores the sort kind, which enables the toggling between ascending and descending
  • we only need two kind of type :
    • String for the names (file name, extension, path) and even for dates (our dates are always in string-sortable order: "yyyy-mm-dd hh-nn_ss")
    • Integer for the file size


The tListView encapsulation is then

Type c_sorted_listview=
         Class(tListView)
           Public
             m_nameString;
             m_total_widthInteger;

             m_clicked_columnInteger;
             m_column_info_arrayArray Of t_column_info;

             m_c_result_listtStringList;

             Constructor create_sorted_listview(p_nameString;
                 p_c_ownertComponent);
             Procedure add_column(p_titleStringp_widthIntegerp_typeString);
             Procedure add_value(p_value_arrayArray Of String);

             Procedure handle_listview_compare(SenderTObject;
                 Item1Item2TListItemDataIntegerVar CompareInteger);
             // -- dispatch handler
             Procedure ColClick(ColumnTListColumn); Override;
             Procedure ClickOverride;

             Destructor DestroyOverride;
         End// c_sorted_listview

where:

  • m_column_info_array keeps track of each column's information
  • m_total_width is used to adjust the size of the last column to the remaining size at the right
  • m_c_result_list is used to store a textual representation of the tListView (the text can be displayed in a tMemo and then easily copied and pasted)


add_column is used to create the tListViews columns. Here is an example:

With g_c_sorted_listview Do
Begin
  add_column('name',    70, '');
  add_column('-number', 50, 'i');
  add_column('path',    -1, '');
End// with g_c_sorted_listview do

and the code is:

Procedure c_sorted_listview.add_column(p_titleStringp_widthInteger;
    p_typeString);
  Var l_info_array_indexInteger;
  Begin
    With Columns.Add  Do
    Begin
      If (Length(p_title)> 0) And (p_title[1]= '-')
        Then Begin
            System.Delete(p_title, 1, 1);
            Caption:= p_title;
            Alignment:= taRightJustify;
          End
        Else Caption:= p_title;

      If p_width< 0
        Then Begin
            Width:= Self.Width- 15;
          End
        Else Begin
            Width:= p_width;
            Inc(m_total_widthp_width);
          End;
    End// with file_listview_

    l_info_array_index:= Length(m_column_info_array);
    SetLength(m_column_info_arrayl_info_array_index+ 1);

    With m_column_info_array[l_info_array_indexDo
      If p_type''
        Then m_column_type:= e_string_type
        Else
          If p_type'i'
            Then m_column_type:= e_integer_type
            Else
              If p_type'd'
                Then m_column_type:= e_double_type;
  End// add_column

where

  • the first parameter is the caption of the column, with an optional "-" to spedify right justification (for numbers). The default is left justification
  • the second parameter is the requires width. "-1" is used to set the width to the remaining width for the last column
  • the default type is String, and the "i" type refers to Integer


To fill a tListView row, we use add_value.

Here is a simple example:

With g_c_sorted_listview Do
Begin
  Clear;

  _add('bbb''10''dddddd');
  _add('www''2''aaa');
  _add('eee''55''nnn');
End// with g_c_sorted_listview

and the code is

Procedure c_sorted_listview.add_value(p_value_arrayArray Of String);
  Var l_value_indexInteger;
  Begin
    With Items.Add Do
    Begin
      If High(p_value_array)> 0
        Then Caption:= p_value_array[0];

      For l_value_index:= 1 To High(p_value_arrayDo
        SubItems.Add(p_value_array[l_value_index]);
    End// with Items.Add 
  End// add_value

The special handling of the 0 value is required by the way Delphi fills a tListView row.



To trigger a sort, the user clicks on some column header. This triggers the ColClick dispatcher, which we override like this:

Procedure c_sorted_listview.ColClick(ColumnTListColumn);
  Begin
    m_clicked_column:= Column.Index;

    If m_column_info_array[m_clicked_column].m_sort_kind
        In [e_not_sortede_descending]
      Then m_column_info_array[m_clicked_column].m_sort_kind:= e_ascending
      Else m_column_info_array[m_clicked_column].m_sort_kind:= e_descending;

    AlphaSort;
  End// ColClick



AlphaSort then calls, whenever required by the sorting algorithm, our OnCompare event:

Procedure c_sorted_listview.handle_listview_compare(SenderTObject;
    Item1Item2TListItemDataIntegerVar CompareInteger);
  Var l_directionInteger;
      l_size_1l_size_2Integer;
      l_item_1l_item_2String;
  Begin
    If m_column_info_array[m_clicked_column].m_sort_kinde_ascending
      Then l_direction:= 1
      Else l_direction:= -1;

    If m_clicked_column= 0
      Then Begin
          l_item_1:= Item1.Caption;
          l_item_2:= Item2.Caption;
        End
      Else Begin
          l_item_1:= Item1.SubItems[m_clicked_column- 1];
          l_item_2:= Item2.SubItems[m_clicked_column- 1];
        End;

      Case m_column_info_array[m_clicked_column].m_column_type Of
        e_string_type :
            Compare:= l_directionCompareText(l_item_1l_item_2);
        e_integer_type :
            Begin
              l_size_1:= StrToInt(l_item_1);
              l_size_2:= StrToInt(l_item_2);
              If l_size_1l_size_2
                Then Compare:= -1
                Else Compare:= 1;
              Compare := l_directionCompare;
            End;
      End// case
  End// ListView1Compare



We also added of displaying a textual representation of the tListView along with the selected row count. Here is the code:

Procedure c_sorted_listview.Click;
  Var l_list_indexInteger;
      l_selected_countInteger;

      l_displayString;
  Begin
    m_c_result_list.Free;
    m_c_result_list:= tStringList.Create;

    With Items Do
    Begin
      l_selected_count:= 0;
      For l_list_index:= 0 To Count- 1 Do
        With Items[l_list_indexDo
        Begin
          l_display:= Caption;

          m_c_result_list.Add(l_display);

          If Selected
            Then Inc(l_selected_count);
    End// with file_listview_, Items

    With m_c_result_list Do
      m_c_result_list.Insert(0, Format('// count %d  sel %d', [Items.Countl_selected_count]));
  End// Click

Obviously the code here is very much application dependent.



Finally the Constructor is:

Constructor c_sorted_listview.create_sorted_listview(p_nameString;
    p_c_ownertComponent);
  Begin
    Inherited Create(p_c_owner);
    m_name:= p_name;

    Parent:= p_c_owner As tWinControl;
    Align:= alClient;
    ViewStyle:= vsReport;

    HideSelection:= False;
    MultiSelect:= True;

    OnCompare:= handle_listview_compare;
  End// create_sorted_listview




2.4 - Simple tListView sorting example

In this example
  • we create the sorted tListView with

    Procedure TForm1.create_Click(SenderTObject);
      Begin
        g_c_sorted_listview.Free;
        g_c_sorted_listview:= c_sorted_listview.create_sorted_listview('',
            listview_panel);

        With g_c_sorted_listview Do
        Begin
          add_column('name', 70, '');
          add_column('-number', 50, 'i');
          add_column('path', -1, '');

          Clear;

          _add('bbb''10''dddddd');
          _add('www''2''aaa');
          _add('eee''55''nnn');
        End// with g_c_sorted_listview
      End// create_click

  • and we display the result of some row clicks with

    Procedure TForm1.display_rows_Click(SenderTObject);
      Begin
        If (g_c_sorted_listview<> Nil)
            And (g_c_sorted_listview.m_c_result_list<> Nil)
          Then
            With g_c_sorted_listview Do
              display_memo_.Lines.Assign(m_c_result_list)
      End// display_rows_Click

Here is the snapshot of the example (after clickin on the number column and selecting 2 rows):

sorted_listview




3 - Download the Sources

Here are the source code files: The .ZIP file(s) contain:
  • the main program (.DPR, .DOF, .RES), the main form (.PAS, .DFM), and any other auxiliary form
  • any .TXT for parameters, samples, test data
  • all units (.PAS) for units
Those .ZIP
  • are self-contained: you will not need any other product (unless expressly mentioned).
  • for Delphi 6 projects, can be used from any folder (the pathes are RELATIVE)
  • will not modify your PC in any way beyond the path where you placed the .ZIP (no registry changes, no path creation etc).
To use the .ZIP:
  • create or select any folder of your choice
  • unzip the downloaded file
  • using Delphi, compile and execute
To remove the .ZIP simply delete the folder.

The Pascal code uses the Alsacian notation, which prefixes identifier by program area: K_onstant, T_ype, G_lobal, L_ocal, P_arametre, F_unction, C_lass etc. This notation is presented in the Alsacian Notation paper.



As usual:

  • please tell us at fcolibri@felix-colibri.com if you found some errors, mistakes, bugs, broken links or had some problem downloading the file. Resulting corrections will be helpful for other readers
  • we welcome any comment, criticism, enhancement, other sources or reference suggestion. Just send an e-mail to fcolibri@felix-colibri.com.
  • or more simply, enter your (anonymous or with your e-mail if you want an answer) comments below and clic the "send" button
    Name :
    E-mail :
    Comments * :
     

  • and if you liked this article, talk about this site to your fellow developpers, add a link to your links page ou mention our articles in your blog or newsgroup posts when relevant. That's the way we operate: the more traffic and Google references we get, the more articles we will write.



4 - The author

Felix John COLIBRI works at the Pascal Institute. Starting with Pascal in 1979, he then became involved with Object Oriented Programming, Delphi, Sql, Tcp/Ip, Html, UML. Currently, he is mainly active in the area of custom software development (new projects, maintenance, audits, BDE migration, Delphi Xe_n migrations, refactoring), Delphi Consulting and Delph training. His web site features tutorials, technical papers about programming with full downloadable source code, and the description and calendar of forthcoming Delphi, FireBird, Tcp/IP, Web Services, OOP  /  UML, Design Patterns, Unit Testing training sessions.
Created: nov-19. Last updated: oct-2019 - 102 articles, 237 .ZIP sources, 1263 figures
Copyright © Felix J. Colibri   http://www.felix-colibri.com 2004 - 2019. All rigths reserved
Back:    Home  Papers  Training  Delphi developments  Links  Download
the Pascal Institute

Felix J COLIBRI

+ Home
  + articles_with_sources
    + database
    + web_internet_sockets
    + oop_components
    + uml_design_patterns
    + debug_and_test
    + graphic
    + controls
    + colibri_utilities
    + colibri_helpers
    + delphi
    + IDE
    + firemonkey
    + compilers
    + vcl
      – devx_tdx_to_tcx
      – devx_tdx_to_tcx_migration
      – delphi_sorted_tlistview
  + delphi_training
  + delphi_developments
  + sweet_home
  – download_zip_sources
  + links
Contacts
Site Map
– search :

RSS feed  
Blog