C# ListView ItemSelectionChanged Event Multi Select get ONLY last item selected

此生再无相见时 提交于 2019-12-10 21:24:14

问题


Im using a multi-select ListView in C# .NET 4.5 The issue occurs when selecting multiple items (ie. Shift + End or Shift + Click, etc.) These are just a few examples of many different mouse/keyboard combinations for multi-selecting of course..

This is my event handler for when selecting items in a list:

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
}

My problem is that if I select 500 items the event is triggered 500 times. The intent is to get that last item the user selected (via keyboard/mouse combinations mentioned above), on and do something with it ... in my case I need to run a SQL query against it.

If I click first on item 0 in the listview it is ok to run the query, then when you shift+end it highlights all the rest, and I want it to run the query only on the last item selected. Instead it is running on every item in between.

EDIT: On another note, the event triggers as it unselects as well, in which case it really shouldn't do anything when deselecting.


回答1:


Have you considered performing the action on a button press instead? That way they can also use Ctrl-Click to select any individual items they want?

Otherwise what you would have to do is wait a certain amount of time before firing the action, known as debouncing, you can read a more about debouncing here: https://stackoverflow.com/a/4517995/984780

I created a class you can use for debouncing:

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

Then you can change your code to this:

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

This should work no matter how they select items, it will run your code 5 seconds after their last selection action.

I just wrote this class and did a quick test, a more thorough test would be advised. In any case hopefully it's enough to get the idea.



来源:https://stackoverflow.com/questions/19062474/c-sharp-listview-itemselectionchanged-event-multi-select-get-only-last-item-sele

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!