问题
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