ListView控件中的LVN_ITEMCHANGED优化 [英] LVN_ITEMCHANGED optimization in ListView control

查看:212
本文介绍了ListView控件中的LVN_ITEMCHANGED优化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我当前正在使用以下代码来更新对话框中的控件,具体取决于列表视图控件中选择的行:

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;
}

此方法有效,除了它真的很慢

例如,如果我的列表中包含约100个项目,然后单击第一个项目,然后按住Shift键单击最后一个项目(以选择所有项目),则它会将我的应用程序锁定约几秒钟.如果我在上面的示例中注释了updateDlgControls,则不会发生这种情况.

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.

有没有一种方法可以优化LVN_ITEMCHANGED的处理?

Is there a way to optimize the processing of LVN_ITEMCHANGED?

例如,对于100个选定的项目,每一项都会被调用.

For instance, for 100 selected items, it's called for each and every one of them.

编辑:按照 Jonathan Potter的建议,我将其更改为此,这似乎可以做到这项工作很好:

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
}

在这种方法中,我只看到一个警告.需要确保在updateDlgControls()方法可以禁用的UI控件的处理方法中另外检查禁用条件,因为引入了延迟,当可以调用UI控件的处理方法时,我们可能会遇到问题.在计时器过程有机会调用通常禁用UI控件的updateDlgControls()方法之前. (例如,如果用户反复单击键盘上的快捷键,可能会发生这种情况.)通过对处理方法进行二次检查,我们确保禁用的方法不会导致崩溃.

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.

推荐答案

我将使用标志和计时器来完成此操作.当您收到状态更改消息时,请设置一个指示状态已更改的标志,并使用SetTimer启动一个简短的计时器(甚至可能1ms).由于计时器的优先级较低,因此其他消息(例如WM_NOTIFY)在您的队列中时不会触发.计时器到期后,将其终止,然后更新您的UI状态.

I would do this using a flag and a timer. When you get a state change message, set a flag that indicates the state has changed, and use SetTimer to kick off a short (even 1ms would probably do) timer. Since timers are low priority it won't fire while other messages, like WM_NOTIFY are in your queue. When the timer expires, kill it and then update your UI state.

(仅使用该标志是为了避免您一次又一次地重新创建计时器-计时器启动后,请使用KillTimer杀死它并清除标志,以备下次使用.)

(The flag is simply used so that you don't recreate the timer over and over again - once the timer has fired, use KillTimer to kill it and clear the flag ready for next time).

这篇关于ListView控件中的LVN_ITEMCHANGED优化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆