WxListCtrl sortable

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
wxJack
Experienced Solver
Experienced Solver
Posts: 83
Joined: Wed Jun 20, 2018 8:06 am

WxListCtrl sortable

Post by wxJack »

Hi everybody!
Is there a way to make a wxListCtrl sortable only if the user clicks on the header? I 've already tried with the style wxLC_SORT but it does not do what I want it to do..
Example: I have a wxListCtrl in report mode where in each line I have 3 cols: name, surname, age and I wait to sort the table looking at the Surname..
Thanks very much!
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7458
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: WxListCtrl sortable

Post by ONEEYEMAN »

Hi,
wxJack wrote: Hi everybody!
Is there a way to make a wxListCtrl sortable only if the user clicks on the header?
Does it sort the data if you click anywhere else?

Thank you.
wxJack
Experienced Solver
Experienced Solver
Posts: 83
Joined: Wed Jun 20, 2018 8:06 am

Re: WxListCtrl sortable

Post by wxJack »

For now no, it doesn't ..
I can do a button doing this but I was wondering if there is an easier way to do it:)
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7458
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: WxListCtrl sortable

Post by ONEEYEMAN »

Hi,
I don't think the control implements sorting.
You can catch the click on the column header, clean the control, resort the data and then re-append the data back.

Thank you.
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: WxListCtrl sortable

Post by PB »

The following example shows a simples way how to sort the list when clicking on a column. Please notice it has major flaws, such as there is no indicator showing by which column is the list sorted (I believe that listctrl sample shows how to add an image to the column header). The code could be also simplifed by having a sort column index stored in the data so that only one compare function would be necessary...

I would also prefer using virtual list ctrl and doing the sorting in the data (e.g. the std::vector in the code below) themselves.

Code: Select all

#include <wx/wx.h>
#include <wx/listctrl.h>
#include <vector>

struct Country
{    
    wxString name;
    wxString capital;
    wxString continent;
};

typedef std::vector<Country> CountryList;

class MyListCtrl : public wxListCtrl
{
public:     
    MyListCtrl(wxWindow* parent) : wxListCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT)
    {
        Country c;
        
        c.name = "Canada"; c.capital = "Ottawa"; c.continent = "America";
        m_countries.push_back(c);    
        c.name = "Germany"; c.capital = "Berlin"; c.continent = "Europe";
        m_countries.push_back(c);    
        c.name = "Japan"; c.capital = "Tokyo"; c.continent = "Asia";
        m_countries.push_back(c);
    
        InsertColumn(Column_Name, _("Name"));
        InsertColumn(Column_Capital, _("Capital"));
        InsertColumn(Column_Continent, _("Continent"));

        for ( size_t i = 0; i < m_countries.size(); ++i )
        {
            const Country& country = m_countries[i];        
            long result = InsertItem(i, country.name);
        
            SetItem(result, Column_Capital, country.capital);
            SetItem(result, Column_Continent, country.continent);
            SetItemData(result, (long)i);
        }

        Bind(wxEVT_LIST_COL_CLICK, &MyListCtrl::OnColClick, this);
    }
    
private:
    enum Columns 
    {
        Column_Name = 0,
        Column_Capital,
        Column_Continent
    };
    
    CountryList m_countries;

    void OnColClick(wxListEvent& evt)
    {
        int column = evt.GetColumn();

        if ( column < 0 )
            return;

        if ( column == Column_Name )
            SortItems(CompareName, reinterpret_cast<wxIntPtr>(&m_countries));
        else
        if ( column == Column_Capital )
            SortItems(CompareCapital, reinterpret_cast<wxIntPtr>(&m_countries));
        else
        if ( column == Column_Continent)
            SortItems(CompareContinent, reinterpret_cast<wxIntPtr>(&m_countries));
        else
            wxFAIL;                
    }

    static void GetCountries(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData,
                             Country& c1, Country& c2)
    {        
        CountryList* countries = reinterpret_cast<CountryList*>(sortData);        
        c1 = (*countries)[static_cast<size_t>(item1)];
        c2 = (*countries)[static_cast<size_t>(item2)];
    }

    static int wxCALLBACK CompareName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
    {
        Country c1, c2;

        GetCountries(item1, item2, sortData, c1, c2);
        return c1.name.CmpNoCase(c2.name);    
    }

    static int wxCALLBACK CompareCapital(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
    {
        Country c1, c2;

        GetCountries(item1, item2, sortData, c1, c2);
        return c1.capital.CmpNoCase(c2.capital);    
    }

    static int wxCALLBACK CompareContinent(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
    {
        Country c1, c2;

        GetCountries(item1, item2, sortData, c1, c2);
        return c1.continent.CmpNoCase(c2.continent);    
    }    
};

class MyApp : public wxApp
{
public:
    bool OnInit()
    {
        wxFrame* frame = new wxFrame(NULL, wxID_ANY, "Test", wxDefaultPosition, wxSize(600, 400));
        new MyListCtrl(frame);        
        frame->Show();
        
        return true;
    }
}; wxIMPLEMENT_APP(MyApp);
You may be better off using wxDataView(List)Ctrl instead of wxListCtrl...
wxJack
Experienced Solver
Experienced Solver
Posts: 83
Joined: Wed Jun 20, 2018 8:06 am

Re: WxListCtrl sortable

Post by wxJack »

many thanks to both of you guys, very appreciated your help :))
Post Reply