wxListCtrl very slow inserting items with wxWidgets 3.3.0 (Issue #24011)

98 views
Skip to first unread message

rocrail

unread,
Oct 29, 2023, 7:28:03 AM10/29/23
to wx-...@googlegroups.com, Subscribed

A wxListCtrl with 300 items will fill under wxWidgets 3.2.3 within 2 seconds, and under wxWidgets 3.3.0 it takes about a minute.

Platform: Windows 11-64 Intel Darkmode


Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/24011@github.com>

PB

unread,
Oct 29, 2023, 8:52:14 AM10/29/23
to wx-...@googlegroups.com, Subscribed

I cannot reproduce this (tried the default LC_LIST as well as LC_REPORT, with 500 items) either in the listctrl sample (can you?) or with a simple minimal application such as this

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

class MyFrame : public wxFrame
{
public:
    MyFrame(bool isReport, wxWindow* parent = nullptr) : wxFrame(parent, wxID_ANY, "Test")
    {
        const long itemCount = 300;

        int flags = 0;

        if ( isReport )
            flags |= wxLC_REPORT;
        else
            flags |= wxLC_LIST;

        wxListCtrl* listCtrl = new wxListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, flags);

        if ( isReport )
        {
            listCtrl->AppendColumn("Item");
            listCtrl->AppendColumn("SubItem 1");
            listCtrl->AppendColumn("SubItem 2");

            for ( long i = 0; i < itemCount; ++i )
            {
                listCtrl->InsertItem(i, wxString::Format("Item %ld", i));
                listCtrl->SetItem(i, 1, wxString::Format("SubItem 1 %ld", i));
                listCtrl->SetItem(i, 2, wxString::Format("SubItem 2 %ld", i));
            }
        }
        else
        {
            for ( long i = 0; i < itemCount; ++i )
                listCtrl->InsertItem(i, wxString::Format("Item %ld", i));
        }
    }
};

class MyApp : public wxApp
{
public:
    bool OnInit() override
    {
        const bool isReport = wxMessageBox("Create wxListCtrl in report mode?", "wxListCtrl mode", wxYES_NO) == wxYES;

        (new MyFrame(isReport))->Show();
        return true;
    }
}; wxIMPLEMENT_APP(MyApp);

Unfortunately, you did not provide any information that could be helpful to reproduce the issue. For example, the styles used to create the control, the number of columns it has (if in report mode), whether images are used, how exactly do you add the items, does Windows being in the light mode make a difference, or other circumstances (e.g., wxNotebook or wxGLCanvas in the same top level window as your wxListCtrl).

IMO, adding 300 items taking a noticeable time already in 3.2 indicates that you are not doing something simple, IME, this should be practically instantaneous.

I tested with:
wxWidgets current master (8fc73d4) built with MSVC 2022 v17.7.6 as 64-bit Debug DLL
Windows 10 22H2 build 19045.3570 dark mode (Ryzen 5 2600 3.4 GHz, 48 GB RAM)
Windows 11 22H2 build 22621.2428 dark mode (i7 8850H 2.6 GHz, 32 GB RAM)
Relevant system variables: wx_msw_dark_mode set to 1, wx_msw_window_no_composited not set

BTW, scrolling the report list down in dark mode on Windows 11 in the listctrl sample often produces serious visual artifact. This probably requires its own Issue but I do not have time to look into it at the moment.


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/24011/1784099310@github.com>

PB

unread,
Oct 29, 2023, 11:09:18 AM10/29/23
to wx-...@googlegroups.com, Subscribed

Actually, using a more complex test, I can observe a difference between dark and light mode on Windows 10 when using the master, with wxListCtrl in wxLC_REPORT mode. The list control has 500 items and 10 columns, filling it takes about 0.5 s in light mode but over 6 s in dark mode. IIRC, in dark mode, the list control content is custom drawn by wxWidgets?

#include <wx/wx.h>
#include <wx/listctrl.h>
#include <wx/stopwatch.h>
#include <wx/wupdlock.h>

class MyFrame : public wxFrame
{
public:
    MyFrame(bool isReport, wxWindow* parent = nullptr) : wxFrame(parent, wxID_ANY, "Test"
)
    {
        m_mainPanel = new wxPanel(this);
        wxBoxSizer* mainPanelSizer = new wxBoxSizer(wxVERTICAL);

        
int flags = 0;

        if ( isReport )
            flags |= wxLC_REPORT;
        else
            flags |= wxLC_LIST;

        m_listCtrl = new wxListCtrl(m_mainPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, flags);
        mainPanelSizer->Add(m_listCtrl, wxSizerFlags().Proportion(4).Expand().Border());

        wxTextCtrl* logCtrl = new wxTextCtrl(m_mainPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2);
        wxLog::SetActiveTarget(new wxLogTextCtrl(logCtrl));
        mainPanelSizer->Add(logCtrl, wxSizerFlags().Proportion(1).Expand().Border());

        m_addListItemsTimer.Bind(wxEVT_TIMER, [this](wxTimerEvent&) {AddListItems();} );
        m_addListItemsTimer.StartOnce(500);

        SetSize(FromDIP(wxSize(1024, 800)));
        m_mainPanel->SetSizer(mainPanelSizer);
    }
private:
    wxTimer     m_addListItemsTimer;
    wxPanel*    m_mainPanel;
    wxListCtrl* m_listCtrl;

    void AddListItems()
    {
        const long itemCount = 500;
        const bool isReport = m_listCtrl->GetWindowStyleFlag() & wxLC_REPORT;

        wxStopWatch stopWatch;

        wxLogMessage("Adding items...");
        stopWatch.Start();
        if ( isReport )
        {
            const long subItemCount = 9;

            m_listCtrl->AppendColumn("Item");
            for ( long i = 1; i <= subItemCount; ++i )
                m_listCtrl->AppendColumn(wxString::Format("SubItem %ld", i));
#if 0
            wxWindowUpdateLocker lock(this);
#endif
            for ( long i = 0
; i < itemCount; ++i )
            {
                long idx = m_listCtrl->InsertItem(i, wxString::Format("Item %ld", i));
                for ( long j = 1; j <= subItemCount; ++j )
                    m_listCtrl->SetItem(idx, j, wxString::Format("SubItem %ld %ld", j, i));
            }

            m_listCtrl->SetColumnWidth(0, wxLIST_AUTOSIZE);
            for ( long i = 1; i <= subItemCount; ++i )
                m_listCtrl->SetColumnWidth(i, wxLIST_AUTOSIZE);
        }
        
else
        {
            for ( long i = 0
; i < itemCount; ++i )
                m_listCtrl->InsertItem(i, wxString::Format("Item %ld", i));
        }
        m_mainPanel->Layout();
        wxLogMessage("Adding %ld items took %ld ms", itemCount, stopWatch.Time());
    }
};

class MyApp : public wxApp
{
    
bool OnInit() override
    {
        const bool isReport = wxMessageBox("Create wxListCtrl in report mode?", "wxListCtrl mode", wxYES_NO) == wxYES;

        (new MyFrame(isReport))->Show();
        return true;
    }
}; wxIMPLEMENT_APP(MyApp);

Using wxWindowUpdateLocker when adding items shaves off about 1.5 s, so while using wxWindowUpdateLocker brings a significant improvement, the difference between dark and light modes is still VERY observable...

I did not test on Windows 11.


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/24011/1784139351@github.com>

rocrail

unread,
Oct 29, 2023, 11:26:39 AM10/29/23
to wx-...@googlegroups.com, Subscribed

Two options I use: Report and SingleSel.

`
void BaseDialog::fillIndex( iONode Items, const char* itemtype, bool sort, const char* find) {
if(m_ItemList == NULL || Items == NULL)
return;
m_ItemType = itemtype;

m_Items = Items;
m_ItemList->Freeze();
m_ItemList->DeleteAllItems();
iOList sortlist = ListOp.inst();
int size = NodeOp.getChildCnt(Items);
for( int index = 0; index < size; index++ ) {
iONode Item = NodeOp.getChild( Items, index );
if( wItem.getid(Item) != NULL ) {
if( m_ItemType != NULL && !StrOp.equals(m_ItemType, NodeOp.getName(Item)) )
continue;
if( find != NULL && StrOp.len(find) > 0 ) {
if( StrOp.find(wItem.getid(Item), find) == NULL && StrOp.find(wItem.getdesc(Item), find) == NULL )
continue;
}
ListOp.add(sortlist, (obj)Item);
}
}

bool initSortOrder = false;
if( m_SortOrder > 0 ) {
ms_SortOrder = m_SortOrder==1?true:false;
m_SortOrder = 0;
initSortOrder = true;
}

if( m_SortCol == m_colID ) {
if( !initSortOrder ) {
if(sort) m_sortID = !m_sortID;
ms_SortOrder = m_sortID;
}
ListOp.sort(sortlist, &__sortID);
}
else {
ListOp.sort(sortlist, &__sortID);
}

if( m_SortCol == m_colIID ) {
if( !initSortOrder ) {
if(sort) m_sortIID = !m_sortIID;
ms_SortOrder = m_sortIID;
}
ListOp.sort(sortlist, &__sortIID);
}
else if( m_SortCol == m_colAddr ) {
if( !initSortOrder ) {
if(sort) m_sortAddr = !m_sortAddr;
ms_SortOrder = m_sortAddr;
}
ListOp.sort(sortlist, &__sortAddr);
}
else if( m_SortCol == m_colDesc ) {
if( !initSortOrder ) {
if(sort) m_sortDesc = !m_sortDesc;
ms_SortOrder = m_sortDesc;
}
ListOp.sort(sortlist, &__sortDesc);
}
else if( m_SortCol == m_colShow ) {
if( !initSortOrder ) {
if(sort) m_sortShow = !m_sortShow;
ms_SortOrder = m_sortShow;
}
ListOp.sort(sortlist, &__sortShow);
}
else if( m_SortCol == m_colPos ) {
if( !initSortOrder ) {
if(sort) m_sortPos = !m_sortPos;
ms_SortOrder = m_sortPos;
}
ListOp.sort(sortlist, &__sortPos);
}
else if( m_SortCol == m_colOri ) {
if( !initSortOrder ) {
if(sort) m_sortOri = !m_sortOri;
ms_SortOrder = m_sortOri;
}
ListOp.sort(sortlist, &__sortOri);
}
else if( m_SortCol == m_colLen ) {
if( !initSortOrder ) {
if(sort) m_sortLen = !m_sortLen;
ms_SortOrder = m_sortLen;
}
ListOp.sort(sortlist, &__sortLen);
}
else if( m_SortCol == m_colType ) {
if( !initSortOrder ) {
if(sort) m_sortType = !m_sortType;
ms_SortOrder = m_sortType;
}
ListOp.sort(sortlist, &__sortType);
}
else if( m_SortCol == m_colRTime ) {
if( !initSortOrder ) {
if(sort) m_sortRTime = !m_sortRTime;
ms_SortOrder = m_sortRTime;
}
ListOp.sort(sortlist, &__sortRTime);
}
else if( m_SortCol == m_colRDate ) {
if( !initSortOrder ) {
if(sort) m_sortRDate = !m_sortRDate;
ms_SortOrder = m_sortRDate;
}
ListOp.sort(sortlist, &__sortRDate);
}
else if( m_SortCol == m_colMTime ) {
if( !initSortOrder ) {
if(sort) m_sortMTime = !m_sortMTime;
ms_SortOrder = m_sortMTime;
}
ListOp.sort(sortlist, &__sortMTime);
}
else if( m_SortCol == m_colMDate ) {
if( !initSortOrder ) {
if(sort) m_sortMDate = !m_sortMDate;
ms_SortOrder = m_sortMDate;
}
ListOp.sort(sortlist, &__sortMDate);
}
else if( m_SortCol == m_colHome ) {
if( !initSortOrder ) {
if(sort) m_sortHome = !m_sortHome;
ms_SortOrder = m_sortHome;
}
ListOp.sort(sortlist, &__sortHome);
}
else if( m_SortCol == m_colSchedule ) {
if( !initSortOrder ) {
if(sort) m_sortSchedule = !m_sortSchedule;
ms_SortOrder = m_sortSchedule;
}
ListOp.sort(sortlist, &__sortSchedule);
}
else if( m_SortCol == m_colTour ) {
if( !initSortOrder ) {
if(sort) m_sortTour = !m_sortTour;
ms_SortOrder = m_sortTour;
}
ListOp.sort(sortlist, &__sortTour);
}
else if( m_SortCol == m_colFrom ) {
if( !initSortOrder ) {
if(sort) m_sortFrom = !m_sortFrom;
ms_SortOrder = m_sortFrom;
}
ListOp.sort(sortlist, &__sortFrom);
}
else if( m_SortCol == m_colTo ) {
if( !initSortOrder ) {
if(sort) m_sortTo = !m_sortTo;
ms_SortOrder = m_sortTo;
}
ListOp.sort(sortlist, &__sortTo);
}
else if( m_SortCol == m_colCrossing ) {
if( !initSortOrder ) {
if(sort) m_sortCrossing = !m_sortCrossing;
ms_SortOrder = m_sortCrossing;
}
ListOp.sort(sortlist, &__sortCrossing);
}
else if( m_SortCol == m_colState ) {
if( !initSortOrder ) {
if(sort) m_sortState = !m_sortState;
ms_SortOrder = m_sortState;
}
ListOp.sort(sortlist, &__sortState);
}

size = ListOp.size( sortlist );

for( int i = 0; i < size; i++ ) {
iONode Item = (iONode)ListOp.get( sortlist, i );
if( i + 1 == size )
appendItem(Item, true);
else
appendItem(Item, false);
}
m_ItemList->Thaw();

/* clean up the temp. list */
ListOp.base.del(sortlist);
}

void BaseDialog::appendItem( iONode Item, bool adjustColumnWidth) {
if(m_ItemList == NULL)
return;

m_ItemList->Freeze();
int index = m_ItemList->GetItemCount();
m_ItemList->InsertItem( index, wxString( wItem.getid(Item), wxConvUTF8));

bool isAutoGen = wItem.isautogen(Item) | wItem.isgenerated(Item);

if( !AddrOp.isIDvalid( wItem.getid(Item), TRCLEVEL_INFO ) ) {
//m_ItemList->SetItemBackgroundColour(index, Base::getRed2());
}

int genRGB = wGui.getgenRGB(wxGetApp().getIni());
if( isAutoGen && genRGB > 0 ) {
wxColour genColor( (unsigned long)genRGB );
//m_ItemList->SetItemBackgroundColour(index, Base::getDarkGrey2());
m_ItemList->SetItemTextColour(index, genColor );
isAutoGen = false;
}

if( m_ShowAddr ) {
m_ItemList->SetItem( index, m_colIID, wxString( wItem.getiid(Item), wxConvUTF8));
m_ItemList->SetItem( index, m_colAddr, __getAddrStr(Item, &m_longaddr));
m_ItemList->SetColumnWidth(m_colIID, wxLIST_AUTOSIZE_USEHEADER);
m_ItemList->SetColumnWidth(m_colAddr, wxLIST_AUTOSIZE);
}

bool isUsed = wItem.isused(Item);
if( StrOp.equals( NodeOp.getName(Item), wSwitch.name() ) )
m_ItemList->SetItem( index, m_colDesc, wxString( wItem.getdesc(Item), wxConvUTF8) + wxT("(") + wxString::Format( wxT("%d"), wSwitch.getswitched(Item)) + wxT(")") );
else if( StrOp.equals( NodeOp.getName(Item), wDec.name() ) )
m_ItemList->SetItem( index, m_colDesc, wxT("(") + wxString( wItem.getstate(Item), wxConvUTF8) + wxT(") ") + wxString( wItem.getdesc(Item), wxConvUTF8) );
else if( StrOp.equals( NodeOp.getName(Item), wAction.name() ) && StrOp.len(wItem.getdesc(Item)) == 0 )
m_ItemList->SetItem( index, m_colDesc, (isUsed?wxT("# "):wxT("")) + wxString( wAction.getcmd(Item), wxConvUTF8) + wxT("::") + wxString( wAction.getparam(Item), wxConvUTF8) );
else
m_ItemList->SetItem( index, m_colDesc, (isAutoGen?wxT(""):wxT("")) + wxString(isUsed?wxT("#"):wxT("")) + wxString( wItem.getdesc(Item), wxConvUTF8));
if( m_ShowLen ) {
if( StrOp.equals( NodeOp.getName(Item), wLoc.name() ) && wLoc.getwheelbase(Item) > 0 )
m_ItemList->SetItem( index, m_colLen, wxString::Format(_T("%d (%d)"), wItem.getlen(Item), wLoc.getwheelbase(Item)));
else
m_ItemList->SetItem( index, m_colLen, wxString::Format(_T("%d"), wItem.getlen(Item)));
if( adjustColumnWidth )
m_ItemList->SetColumnWidth(m_colLen, wxLIST_AUTOSIZE_USEHEADER);
}
if( m_ShowShow ) {
if( StrOp.equals( NodeOp.getName(Item), wLoc.name() ) ) {
char
showactive = StrOp.fmt("%s%c", wItem.isshow(Item)?"true":"false", wLoc.isactive(Item)?'+':'-');
m_ItemList->SetItem( index, m_colShow, wxString( showactive, wxConvUTF8));
StrOp.free(showactive);
}
else
m_ItemList->SetItem( index, m_colShow, wxString( wItem.isshow(Item)?"true":"false", wxConvUTF8));
}
if( m_ShowPos ) {
if( StrOp.equals( NodeOp.getName(Item), wLocation.name() ) ) {
char* s = StrOp.fmt("%d", wLocation.getsectionpos(Item) );
m_ItemList->SetItem( index, m_colPos, wxString(s, wxConvUTF8) );
StrOp.free(s);
}
else {
m_ItemList->SetItem( index, m_colPos, wxString::Format(_T("%d: %d,%d"), wItem.getz(Item), wItem.getx(Item), wItem.gety(Item)) );
if( m_ShowOri )
m_ItemList->SetItem( index, m_colOri, wxString( wItem.getori(Item)!=NULL?wItem.getori(Item):wItem.west, wxConvUTF8));
}
if( adjustColumnWidth ) {
m_ItemList->SetColumnWidth(m_colPos, wxLIST_AUTOSIZE_USEHEADER);
if( m_ShowOri )
m_ItemList->SetColumnWidth(m_colOri, wxLIST_AUTOSIZE_USEHEADER);
}
}
m_ItemList->SetItemPtrData(index, (wxUIntPtr)Item);
if( adjustColumnWidth ) {
m_ItemList->SetColumnWidth(m_colID, wxLIST_AUTOSIZE);
m_ItemList->SetColumnWidth(m_colDesc, wxLIST_AUTOSIZE);
}

if( m_ShowShow && adjustColumnWidth )
m_ItemList->SetColumnWidth(m_colShow, wxLIST_AUTOSIZE_USEHEADER);

if( m_ItemList->GetColumnWidth(m_colDesc) < 60 && adjustColumnWidth )
m_ItemList->SetColumnWidth(m_colDesc, wxLIST_AUTOSIZE_USEHEADER);

if( m_ShowType ) {
m_ItemList->SetItem( index, m_colType, wxString( wItem.gettype(Item), wxConvUTF8));
if( adjustColumnWidth )
m_ItemList->SetColumnWidth(m_colType, wxLIST_AUTOSIZE);
}

if( m_ShowTime ) {
// Loco
long runtime = wLoc.getruntime(Item);
long mtime = wLoc.getmtime(Item);
long deltatime = runtime-mtime;

char mdate[32] = {'\0'};
if( wLoc.getmdate( Item ) > 0 ) {
  time_t ltime = wLoc.getmdate( Item );
  struct tm* ltm = localtime( (const time_t*)&ltime );
  StrOp.fmtb( mdate, "%02d-%02d-%d", ltm->tm_mday, ltm->tm_mon+1, ltm->tm_year + 1900);
}
char rdate[32] = {'\0'};
if( wLoc.getrdate( Item ) > 0 ) {
  time_t ltime = wLoc.getrdate( Item );
  struct tm* ltm = localtime( (const time_t*)&ltime );
  StrOp.fmtb( rdate, "%02d-%02d-%d", ltm->tm_mday, ltm->tm_mon+1, ltm->tm_year + 1900);
}

m_ItemList->SetItem( index, m_colRTime, wxString::Format(_T("%d:%02d.%02d"), (int)(runtime/3600), (int)((runtime%3600)/60), (int)((runtime%3600)%60)));
m_ItemList->SetItem( index, m_colRDate, wxString(rdate, wxConvUTF8) );
m_ItemList->SetItem( index, m_colMTime, wxString::Format(_T("%d:%02d.%02d (%d:%02d.%02d)"),
    (int)(mtime/3600), (int)((mtime%3600)/60), (int)((mtime%3600)%60), (int)(deltatime/3600), (int)((deltatime%3600)/60), (int)((deltatime%3600)%60) ) );
m_ItemList->SetItem( index, m_colMDate, wxString(mdate, wxConvUTF8) );
m_ItemList->SetItem( index, m_colHome, wxString(wLoc.gethome(Item), wxConvUTF8) );
m_ItemList->SetItem( index, m_colSchedule, wxString(wLoc.getstartupscid(Item), wxConvUTF8) );
m_ItemList->SetItem( index, m_colTour, wxString(wLoc.getstartuptourid(Item), wxConvUTF8) );


if( adjustColumnWidth ) {
  m_ItemList->SetColumnWidth(m_colRTime, wxLIST_AUTOSIZE_USEHEADER);
  int autoheadersize = m_ItemList->GetColumnWidth(m_colRTime);
  m_ItemList->SetColumnWidth(m_colRTime, wxLIST_AUTOSIZE);
  int autosize = m_ItemList->GetColumnWidth(m_colRTime);
  if(autoheadersize > autosize )
    m_ItemList->SetColumnWidth(m_colRTime, wxLIST_AUTOSIZE_USEHEADER);

  m_ItemList->SetColumnWidth(m_colRDate, wxLIST_AUTOSIZE_USEHEADER);
  autoheadersize = m_ItemList->GetColumnWidth(m_colRDate);
  m_ItemList->SetColumnWidth(m_colRDate, wxLIST_AUTOSIZE);
  autosize = m_ItemList->GetColumnWidth(m_colRDate);
  if(autoheadersize > autosize )
    m_ItemList->SetColumnWidth(m_colRDate, wxLIST_AUTOSIZE_USEHEADER);

  m_ItemList->SetColumnWidth(m_colMTime, wxLIST_AUTOSIZE_USEHEADER);
  autoheadersize = m_ItemList->GetColumnWidth(m_colMTime);
  m_ItemList->SetColumnWidth(m_colMTime, wxLIST_AUTOSIZE);
  autosize = m_ItemList->GetColumnWidth(m_colMTime);
  if(autoheadersize > autosize )
    m_ItemList->SetColumnWidth(m_colMTime, wxLIST_AUTOSIZE_USEHEADER);

  m_ItemList->SetColumnWidth(m_colMDate, wxLIST_AUTOSIZE_USEHEADER);
  autoheadersize = m_ItemList->GetColumnWidth(m_colMDate);
  m_ItemList->SetColumnWidth(m_colMDate, wxLIST_AUTOSIZE);
  autosize = m_ItemList->GetColumnWidth(m_colMDate);
  if(autoheadersize > autosize )
    m_ItemList->SetColumnWidth(m_colMDate, wxLIST_AUTOSIZE_USEHEADER);

  m_ItemList->SetColumnWidth(m_colHome, wxLIST_AUTOSIZE_USEHEADER);
  autoheadersize = m_ItemList->GetColumnWidth(m_colHome);
  m_ItemList->SetColumnWidth(m_colHome, wxLIST_AUTOSIZE);
  autosize = m_ItemList->GetColumnWidth(m_colHome);
  if(autoheadersize > autosize )
    m_ItemList->SetColumnWidth(m_colHome, wxLIST_AUTOSIZE_USEHEADER);

  m_ItemList->SetColumnWidth(m_colSchedule, wxLIST_AUTOSIZE_USEHEADER);
  autoheadersize = m_ItemList->GetColumnWidth(m_colSchedule);
  m_ItemList->SetColumnWidth(m_colSchedule, wxLIST_AUTOSIZE);
  autosize = m_ItemList->GetColumnWidth(m_colSchedule);
  if(autoheadersize > autosize )
    m_ItemList->SetColumnWidth(m_colSchedule, wxLIST_AUTOSIZE_USEHEADER);

  m_ItemList->SetColumnWidth(m_colTour, wxLIST_AUTOSIZE_USEHEADER);
  autoheadersize = m_ItemList->GetColumnWidth(m_colTour);
  m_ItemList->SetColumnWidth(m_colTour, wxLIST_AUTOSIZE);
  autosize = m_ItemList->GetColumnWidth(m_colTour);
  if(autoheadersize > autosize )
    m_ItemList->SetColumnWidth(m_colTour, wxLIST_AUTOSIZE_USEHEADER);
}

}

if( m_ShowFromTo ) {
// Route
m_ItemList->SetItem( index, m_colFrom, wxString( wRoute.getbka(Item), wxConvUTF8));
m_ItemList->SetItem( index, m_colTo, wxString( wRoute.getbkb(Item), wxConvUTF8));
if( m_ShowCrossing ) {
m_ItemList->SetItem( index, m_colCrossing, wxString( wRoute.getbkc(Item), wxConvUTF8));
m_ItemList->SetItem( index, m_colState, wxString( __getRouteState(wRoute.getstatus(Item)), wxConvUTF8));
}

if( adjustColumnWidth ) {
  int autoheadersize = m_ItemList->GetColumnWidth(m_colFrom);
  m_ItemList->SetColumnWidth(m_colFrom, wxLIST_AUTOSIZE);
  int autosize = m_ItemList->GetColumnWidth(m_colFrom);
  if(autoheadersize > autosize )
    m_ItemList->SetColumnWidth(m_colFrom, wxLIST_AUTOSIZE_USEHEADER);

  autoheadersize = m_ItemList->GetColumnWidth(m_colTo);
  m_ItemList->SetColumnWidth(m_colTo, wxLIST_AUTOSIZE);
  autosize = m_ItemList->GetColumnWidth(m_colTo);
  if(autoheadersize > autosize )
    m_ItemList->SetColumnWidth(m_colTo, wxLIST_AUTOSIZE_USEHEADER);

  if( m_ShowCrossing ) {
    autoheadersize = m_ItemList->GetColumnWidth(m_colCrossing);
    m_ItemList->SetColumnWidth(m_colCrossing, wxLIST_AUTOSIZE);
    autosize = m_ItemList->GetColumnWidth(m_colCrossing);
    if(autoheadersize > autosize )
      m_ItemList->SetColumnWidth(m_colCrossing, wxLIST_AUTOSIZE_USEHEADER);

    autoheadersize = m_ItemList->GetColumnWidth(m_colState);
    m_ItemList->SetColumnWidth(m_colState, wxLIST_AUTOSIZE);
    autosize = m_ItemList->GetColumnWidth(m_colState);
    if(autoheadersize > autosize )
      m_ItemList->SetColumnWidth(m_colState, wxLIST_AUTOSIZE_USEHEADER);
  }
}

}

m_ItemList->Thaw();
}

`


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/24011/1784143536@github.com>

VZ

unread,
Oct 29, 2023, 12:07:23 PM10/29/23
to wx-...@googlegroups.com, Subscribed

(How to add a code section at github???)

There is a "M" button in the lower right corner of the text box where you're writing comments which opens the full help, which includes the answer to this question.

Maybe more importantly, it's best to minimize the code as much as possible as it's really hard to know what is relevant and what isn't in the example you provide. The example provided by @PBfordev (thanks!) is much simpler, so I'm going to use it for debugging this issue, but please keep this in mind for the future bug reports. TIA!


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/24011/1784153966@github.com>

VZ

unread,
Oct 29, 2023, 12:34:34 PM10/29/23
to wx-...@googlegroups.com, Subscribed

It's actually not adding the items which takes time, but setting the column widths using wxLIST_AUTOSIZE, as this results in 500 calls to our item drawing function for each of the columns.

It looks like we need to use wxListCtrlMaxWidthCalculator in native wxMSW wxListCtrl version too.


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/24011/1784160990@github.com>

PB

unread,
Oct 29, 2023, 1:09:52 PM10/29/23
to wx-...@googlegroups.com, Subscribed

Oops, I am sorry for not realizing that setting the column width is the culprit (and the major difference between my first and second codes), perhaps because I could observe the items actually being drawn during adding. BTW, the control's size also plays a major role here (i.e., the larger the the slower).

FWIW, here is the patch to the minimal sample which can be used to reproduce the issue:

diff --git a/samples/minimal/minimal.cpp b/samples/minimal/minimal.cpp
index 5f32257c6b..2aff99c4b2 100644
--- a/samples/minimal/minimal.cpp
+++ b/samples/minimal/minimal.cpp
@@ -25,6 +25,9 @@
 #ifndef WX_PRECOMP
     #include "wx/wx.h"
 #endif
+#include "wx/listctrl.h"
+#include "wx/stopwatch.h"
+#include "wx/wupdlock.h"
 
 // ----------------------------------------------------------------------------
 // resources
@@ -65,6 +68,9 @@ public:
     void OnAbout(wxCommandEvent& event);
 
 private:
+    wxListCtrl* m_listCtrl;
+
+    void AddListItems();
     // any class wishing to process wxWidgets events must use this macro
     wxDECLARE_EVENT_TABLE();
 };
@@ -175,8 +181,39 @@ MyFrame::MyFrame(const wxString& title)
     CreateStatusBar(2);
     SetStatusText("Welcome to wxWidgets!");
 #endif // wxUSE_STATUSBAR
+     m_listCtrl = new wxListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT);
+     Maximize();
+     CallAfter(&MyFrame::AddListItems);
 }
 
+void MyFrame::AddListItems()
+{
+    const long rowCount = 500;
+    const long columnCount = 10;
+
+    wxStopWatch stopWatch;
+#if 0
+     wxWindowUpdateLocker lock(this);
+#endif
+
+    for ( long i = 0; i < columnCount; ++i )
+        m_listCtrl->AppendColumn(wxString::Format("Column %ld", i));
+
+    SetTitle("Adding items...");
+    stopWatch.Start();
+   
+    for ( long row = 0; row < rowCount; ++row )
+    {
+        m_listCtrl->InsertItem(row,"");
+        for ( long column = 0; column < columnCount; ++column )
+            m_listCtrl->SetItem(row, column, wxString::Format("col %02ld row %04ld", column, row));
+    }
+
+    for ( long column = 0; column < columnCount; ++column )
+        m_listCtrl->SetColumnWidth(column, wxLIST_AUTOSIZE);
+
+    SetTitle(wxString::Format("Adding %ld items took %ld ms", rowCount, stopWatch.Time()));
+}
 
 // event handlers
 

BTW, I think my #23846 should also have the "dark mode" label.


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/24011/1784170299@github.com>

rocrail

unread,
Oct 30, 2023, 4:11:46 AM10/30/23
to wx-...@googlegroups.com, Subscribed

I corrected an unwanted wxLIST_AUTOSIZE in the insert loop and now its OK.
It remains a bit slower then the light mode.

Thanks for the helps and tips.


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/24011/1784678194@github.com>

VZ

unread,
Nov 20, 2024, 2:42:30 PM11/20/24
to wx-...@googlegroups.com, Subscribed

This is arguably not 3.3.0-critical, although still would be nice to fix, of course.


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/24011/2489407406@github.com>

Reply all
Reply to author
Forward
0 new messages