I'm currently using the following code to update controls in my dialog depending on the rows selected in the list-view control:
void CMyDialog::OnLvnItemchangedListTasks(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
// TODO: Add your control notification handler code here
ASSERT(pNMLV);
if(pNMLV->uChanged & LVIF_STATE)
{
if((pNMLV->uNewState ^ pNMLV->uOldState) & LVIS_SELECTED)
{
//List selection changed
updateDlgControls();
}
}
*pResult = 0;
}
This method works, except that it's REALLY slow!
For instance, if my list has about 100 items in it and then I click on the first item and Shift-click on the last item (to select all items) it locks up my app for about several seconds. This won't happen if I comment out my updateDlgControls
in the example above.
Is there a way to optimize the processing of LVN_ITEMCHANGED?
For instance, for 100 selected items, it's called for each and every one of them.
EDIT: Following Jonathan Potter's suggestion I changed it to this, which seems to do the job quite nicely:
void CMyDialog::OnLvnItemchangedListTasks(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
// TODO: Add your control notification handler code here
ASSERT(pNMLV);
if(pNMLV->uChanged & LVIF_STATE)
{
if((pNMLV->uNewState ^ pNMLV->uOldState) & LVIS_SELECTED)
{
//Use the timer to optimize processing of multiple notifications
//'LVN_CHANGE_OPTIMIZ_TIMER_ID' = non-zero timer ID
//If SetTimer is called when timer was already set, it will be reset without firing it
::SetTimer(this->GetSafeHwnd(), LVN_CHANGE_OPTIMIZ_TIMER_ID, 1, OnLvnItemchangedListTasksTimerProc);
}
}
*pResult = 0;
}
static VOID CMyDialog::OnLvnItemchangedListTasksTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
VERIFY(::KillTimer(hwnd, idEvent));
ASSERT(hwnd);
::PostMessage(hwnd, WM_APP_PROCESS_LVN_ITEMCHANGED, 0, 0);
}
ON_MESSAGE(WM_APP_PROCESS_LVN_ITEMCHANGED, OnDelayed_updateDlgControls)
LRESULT CMyDialog::OnDelayed_updateDlgControls(WPARAM, LPARAM)
{
//List selection changed
updateDlgControls();
#ifdef _DEBUG
static int __n = 0;
TRACE(CStringA(EasyFormat(L"Updated list count=%d\n", __n++)));
#endif
}
There's only one caveat that I see in this approach. One needs to make sure to check for disabled conditions additionally in processing methods for UI controls that can be disabled by the updateDlgControls()
method, since having introduced a delay we can run into a problem when the processing method for a UI control can be called before the timer procedure had a chance to call updateDlgControls()
method that normally disables UI controls. (This may happen, for instance, if a user repeatedly clicks shortcut keys on the keyboard.) By doing secondary checks in processing methods we make sure that disabled methods don't cause a crash.