【问题标题】:C# ListView ItemSelectionChanged Event Multi Select get ONLY last item selectedC# ListView ItemSelectionChanged 事件多选仅获取最后一个被选中的项目
【发布时间】:2013-09-28 02:10:59
【问题描述】:

我在 C# .NET 4.5 中使用多选 ListView 选择多个项目时会出现此问题(即 Shift + End 或 Shift + Click 等)。当然,这些只是用于多选的许多不同鼠标/键盘组合的几个示例。

这是我在选择列表中的项目时的事件处理程序:

private void lvTitles_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
    MessageBox.Show(e.Item.Text.ToString());
    //MessageBox just for testing I am actually running a SQL query here
}

我的问题是,如果我选择 500 个项目,则会触发 500 次事件。目的是获取用户选择的最后一个项目(通过上面提到的键盘/鼠标组合),并对其进行处理......在我的情况下,我需要对其运行 SQL 查询。

如果我首先单击列表视图中的第 0 项,则可以运行查询,然后当您 shift+end 时,它会突出显示所有其余部分,并且我希望它仅对选择的最后一个项目运行查询。相反,它在其间的每个项目上运行。

编辑:另一方面,事件在取消选择时也会触发,在这种情况下,取消选择时它真的不应该做任何事情。

【问题讨论】:

    标签: c# events listview multi-select


    【解决方案1】:

    您是否考虑过在按下按钮时执行该操作?这样他们还可以使用 Ctrl-Click 来选择他们想要的任何单个项目?

    否则您必须等待一段时间才能触发动作,称为去抖动,您可以在此处阅读有关去抖动的更多信息:https://stackoverflow.com/a/4517995/984780

    我创建了一个可用于去抖动的类:

    public class Debounce {
        private Action _action;
        private bool _isThreadRunning;
        private Thread _thread;
        private DateTime _runAt;
        private double _waitSeconds;
    
        private Debounce(double waitSeconds, Action action) {
            _action = action;
            _waitSeconds = waitSeconds;
        }
    
        private void Invoke() {
            _runAt = DateTime.Now.AddSeconds(_waitSeconds);
    
            lock(this) {
                if(!_isThreadRunning) {
                    _isThreadRunning = true;
    
                    _thread = new Thread(() => {
                        while(true) {
                            Thread.Sleep(100);
    
                            lock(this) {
                                if(DateTime.Now > _runAt) {
                                    _action();
                                    _isThreadRunning = false;
                                    _thread = null;
                                    break;
                                }
                            }
                        }
                    });
    
                    _thread.Start();
                }
            }
        }
    
        private static Dictionary<Action, Debounce> __debounces;
        private static Dictionary<Action, Debounce> _debounces {
            get {
                if(__debounces == null) {
                    __debounces = new Dictionary<Action, Debounce>();
                }
    
                return __debounces;
            }
        }
    
        public static void Run(double waitSeconds, Action action) {
            Debounce debounce;
    
            if(!_debounces.TryGetValue(action, out debounce)) {
                debounce = new Debounce(waitSeconds, action);
                _debounces.Add(action, debounce);
            }
    
            debounce._waitSeconds = waitSeconds;
            debounce.Invoke();
        }
    }
    

    然后你可以把你的代码改成这样:

    private void lvTitles_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
    {
        Debounce.Run(5, () => MessageBox.Show(e.Item.Text.ToString()));
    }
    

    无论他们如何选择项目,这都应该有效,它会在他们最后一次选择操作后 5 秒运行您的代码。

    我刚刚写了这门课并做了一个快速测试,建议进行更彻底的测试。无论如何,希望这足以让您了解这个想法。

    【讨论】:

    • 我需要一系列用户通常期望的操作,例如键盘 Ctrl-A、Shift+End、Shift+Down Arrow... 等以及鼠标单击 + Shift 和鼠标单击 + Ctrl... 以及所有其他注意事项
    • 当运行并进行单个选择时,消息框不会在 5 秒内弹出。这只有在您事先知道运行需要多长时间时才有效...我想知道是否有什么可以从我有帮助的其他 SO 帖子中提取:stackoverflow.com/questions/3666678/…
    • 那个 SO 帖子看起来绝对是一个有效的选项。我建议,既然你有这两个的代码,你为什么不试试看,看看是否适合你。我以 5 秒为例,您可以将 1 秒、半秒更改为您想要的任何内容。它的作用是在最后一次触发 lvTitles_ItemSelectionChanged() 后调用 MessageBox.Show(e.Item.Text.ToString()) "X" 秒。
    • 谢谢,我会继续努力的!
    猜你喜欢
    • 2019-05-04
    • 1970-01-01
    • 2012-11-06
    • 1970-01-01
    • 1970-01-01
    • 2018-02-26
    • 1970-01-01
    • 1970-01-01
    • 2010-09-23
    相关资源
    最近更新 更多