C# Отключить событие на некоторое время
У меня есть этот набор функций для отмены регистрации события listBox1_SelectedValueChanged в течение некоторого времени.
Функция называется: Pause().
Есть ли способ сделать это вообще как функцию, которая может сделать то же самое для различных событий, таких как:
Пауза (listBox1.SelectedValueChanged)
или же
Пауза (button1.Click)
так далее.?
private System.Windows.Forms.Timer disableEvent = new System.Windows.Forms.Timer();
private void Pause(int forTime = 200)
{
listBox1.SelectedValueChanged -= new EventHandler(listBox1_SelectedValueChanged);
disableEvent.Tick += new EventHandler(disableEvent_Tick);
disableEvent.Interval = (forTime);
disableEvent.Enabled = true;
disableEvent.Start();
}
private void disableEvent_Tick(object sender, EventArgs e)
{
if (disableEvent.Enabled == true)
{
disableEvent.Tick -= new EventHandler(disableEvent_Tick);
disableEvent.Stop();
disableEvent.Enabled = false;
listBox1.SelectedValueChanged += new EventHandler(listBox1_SelectedValueChanged);
}
}
5 ответов
Я нашел способ (использовал некоторый код с этого форума), он работает, но он немного сложный (хорошо, может быть, очень сложный) вот код:
Использование (приостановить на некоторое время):
listBox1.Pause("listBox1_SelectedValueChanged", 3000);
listBox1.Pause(3000); // to pause all events of listbox1
button3.Pause("button3_Click", 10000);
Использование (для подавления до возобновления):
cEventSuppressor temp = listBox1.Suppress("listBox1_SelectedValueChanged");
cEventSuppressor temp = listBox1.Suppress(); //to suppress all
Использование (после подавления - для возобновления):
temp.Resume("listBox1_SelectedValueChanged");
temp.Resume(); //To resume all
Остальные:
#region Events
public static class Events
{
public static void Pause(this Control control, string eventName, int forTime)
{
EventTimers et = new EventTimers();
et.PauseEvent(control, eventName, forTime);
}
public static void Pause(this Control control, int forTime)
{
EventTimers et1 = new EventTimers();
et1.PauseEvent(control, forTime);
}
public static cEventSuppressor Suppress(this Control control, string eventName)
{
cEventSuppressor newControl = null;
newControl = new cEventSuppressor(control);
newControl.Suppress(eventName);
return newControl;
}
public static cEventSuppressor Suppress(this Control control)
{
cEventSuppressor newControl = null;
newControl = new cEventSuppressor(control);
newControl.Suppress();
return newControl;
}
}
public class EventTimers
{
private System.Windows.Forms.Timer disableEvent = new System.Windows.Forms.Timer();
private cEventSuppressor suppressedControl { get; set; }
private static string eventName { get; set; }
//Pause specific Event
public void PauseEvent(Control control, string eventName, int forTime)
{
suppressedControl = new cEventSuppressor(control);
suppressedControl.Suppress(eventName);
disableEvent.Tick += new EventHandler(disableEvent_Tick);
disableEvent.Interval = (forTime);
disableEvent.Enabled = true;
disableEvent.Start();
}
private void disableEvent_Tick(object sender, EventArgs e)
{
if (disableEvent.Enabled == true)
{
disableEvent.Tick -= new EventHandler(disableEvent_Tick);
disableEvent.Stop();
disableEvent.Enabled = false;
suppressedControl.Resume(eventName);
}
}
//Pause All Events
public void PauseEvent(Control control, int forTime)
{
suppressedControl = new cEventSuppressor(control);
suppressedControl.Suppress();
disableEvent.Tick += new EventHandler(disableEvent_Tick2);
disableEvent.Interval = (forTime);
disableEvent.Enabled = true;
disableEvent.Start();
}
private void disableEvent_Tick2(object sender, EventArgs e)
{
if (disableEvent.Enabled == true)
{
disableEvent.Tick -= new EventHandler(disableEvent_Tick2);
disableEvent.Stop();
disableEvent.Enabled = false;
suppressedControl.Resume();
}
}
}
public class cEventSuppressor
{
Control _source;
EventHandlerList _sourceEventHandlerList;
FieldInfo _headFI;
Dictionary<object, Delegate[]> suppressedHandlers = new Dictionary<object, Delegate[]>();
PropertyInfo _sourceEventsInfo;
Type _eventHandlerListType;
Type _sourceType;
public cEventSuppressor(Control control)
{
if (control == null)
throw new ArgumentNullException("control", "An instance of a control must be provided.");
_source = control;
_sourceType = _source.GetType();
_sourceEventsInfo = _sourceType.GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic);
_sourceEventHandlerList = (EventHandlerList)_sourceEventsInfo.GetValue(_source, null);
_eventHandlerListType = _sourceEventHandlerList.GetType();
_headFI = _eventHandlerListType.GetField("head", BindingFlags.Instance | BindingFlags.NonPublic);
}
private Dictionary<object, Delegate[]> BuildList()
{
Dictionary<object, Delegate[]> retval = new Dictionary<object, Delegate[]>();
object head = _headFI.GetValue(_sourceEventHandlerList);
if (head != null)
{
Type listEntryType = head.GetType();
FieldInfo delegateFI = listEntryType.GetField("handler", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo keyFI = listEntryType.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo nextFI = listEntryType.GetField("next", BindingFlags.Instance | BindingFlags.NonPublic);
retval = BuildListWalk(retval, head, delegateFI, keyFI, nextFI);
}
return retval;
}
private Dictionary<object, Delegate[]> BuildListWalk(Dictionary<object, Delegate[]> dict,
object entry, FieldInfo delegateFI, FieldInfo keyFI, FieldInfo nextFI)
{
if (entry != null)
{
Delegate dele = (Delegate)delegateFI.GetValue(entry);
object key = keyFI.GetValue(entry);
object next = nextFI.GetValue(entry);
if (dele != null)
{
Delegate[] listeners = dele.GetInvocationList();
if (listeners != null && listeners.Length > 0)
{
dict.Add(key, listeners);
}
}
if (next != null)
{
dict = BuildListWalk(dict, next, delegateFI, keyFI, nextFI);
}
}
return dict;
}
public void Resume()
{
Resume(null);
}
public void Resume(string pMethodName)
{
//if (_handlers == null)
// throw new ApplicationException("Events have not been suppressed.");
Dictionary<object, Delegate[]> toRemove = new Dictionary<object, Delegate[]>();
// goes through all handlers which have been suppressed. If we are resuming,
// all handlers, or if we find the matching handler, add it back to the
// control's event handlers
foreach (KeyValuePair<object, Delegate[]> pair in suppressedHandlers)
{
for (int x = 0; x < pair.Value.Length; x++)
{
string methodName = pair.Value[x].Method.Name;
if (pMethodName == null || methodName.Equals(pMethodName))
{
_sourceEventHandlerList.AddHandler(pair.Key, pair.Value[x]);
toRemove.Add(pair.Key, pair.Value);
}
}
}
// remove all un-suppressed handlers from the list of suppressed handlers
foreach (KeyValuePair<object, Delegate[]> pair in toRemove)
{
for (int x = 0; x < pair.Value.Length; x++)
{
suppressedHandlers.Remove(pair.Key);
}
}
//_handlers = null;
}
public void Suppress()
{
Suppress(null);
}
public void Suppress(string pMethodName)
{
//if (_handlers != null)
// throw new ApplicationException("Events are already being suppressed.");
Dictionary<object, Delegate[]> dict = BuildList();
foreach (KeyValuePair<object, Delegate[]> pair in dict)
{
for (int x = pair.Value.Length - 1; x >= 0; x--)
{
//MethodInfo mi = pair.Value[x].Method;
//string s1 = mi.Name; // name of the method
//object o = pair.Value[x].Target;
// can use this to invoke method pair.Value[x].DynamicInvoke
string methodName = pair.Value[x].Method.Name;
if (pMethodName == null || methodName.Equals(pMethodName))
{
_sourceEventHandlerList.RemoveHandler(pair.Key, pair.Value[x]);
suppressedHandlers.Add(pair.Key, pair.Value);
}
}
}
}
}
#endregion
Я бы использовал DateTime
поле для этого. Я бы проверил в SelectedValuedChanged()
событие, если разрешено запускать. (не отмахивайтесь от события)
Например: (псевдо)
public class Class1
{
private DateTime _isEnabledAfter = DateTime.MinValue;
public Class()
{
listBox1.SelectedValueChanged += new EventHandler(listBox1_SelectedValueChanged);
}
public void Pause(int timeMS)
{
// set the _isEnabledAfter in the future.
_isEnabledAfter = DateTime.Now.AddMilliseconds(timeMS);
}
public void listBox1_SelectedValueChanged(object sender, EventArgs e)
{
// is it after _isEnabledAfter?
if(DateTime.Now < _isEnabledAfter)
// nope... do nothing.
return;
// do your thing.
}
}
Это сэкономит вам немного времени и сложности.
Может быть что-то вроде этого:
public class Class1
{
private TimeEnabledEvent _selectedValueChanged = new TimeEnabledEvent();
public Class1()
{
listBox1.SelectedValueChanged += (s, e) =>
{
if (_selectedValueChanged.IsEnabled)
listBox1_SelectedValueChanged(s, e);
};
_selectedValueChanged.Pause(200);
}
public void listBox1_SelectedValueChanged(object sender, EventArgs e)
{
// do your thing.
}
}
public class TimeEnabledEvent
{
private DateTime _isEnabledAfter = DateTime.MinValue;
public void Pause(int timeMS)
{
// set the _isEnabledAfter in the future.
_isEnabledAfter = DateTime.Now.AddMilliseconds(timeMS);
}
public bool IsEnabled
{
get { return (DateTime.Now >= _isEnabledAfter); }
}
}
Обновление 2:
public partial class Form1 : Form
{
private TimeEnabledEvent _event = new TimeEnabledEvent();
public Form1()
{
InitializeComponent();
listBox1.SelectedValueChanged += _event.Check(ListBox1_SelectedValueChanged);
_event.Pause(1000);
}
private void ListBox1_SelectedValueChanged(object sender, EventArgs e)
{
// do your thing
}
}
internal class TimeEnabledEvent
{
internal EventHandler Check(EventHandler listBox1_SelectedValueChanged)
{
return new EventHandler((ss, ee) =>
{
if (DateTime.Now >= _isEnabledAfter)
listBox1_SelectedValueChanged(ss, ee);
});
}
private DateTime _isEnabledAfter = DateTime.MinValue;
public void Pause(int timeMS)
{
// set the _isEnabledAfter in the future.
_isEnabledAfter = DateTime.Now.AddMilliseconds(timeMS);
}
}
Я бы использовал WaitHandle
чтобы сделать это, например, ManualResetEvent
, Если вы хотите приостановить несколько событий независимо, я бы использовал разные ManualResetEvent
s.
Я бы реализовал это так:
private ManualResetEvent pauseListBox1;
private ManualResetEvent pauseButton1;
Чтобы начать паузу, я бы использовал:
pauseListBox1.Set();
Чтобы закончить паузу, я бы использовал:
pauseListBox1.Reset();
В обработчике событий я бы использовал это
// Return from the event handler of the even is set
if (WaitHandle.WaitOne(1))
return;
Я отвечу конкретно о том, как вы можете сделать это в целом. Как улучшить саму процедуру паузы уже было дано ответом.
Это правда, что вы не можете передать ссылку на событие другому методу, но вы можете сделать это с небольшим размышлением. Например
private static void Pause<TSource, TEvent>(TSource source, Expression<Func<TSource, TEvent>> eventRef, TEvent handler, int forTime = 200) {
var ev = source.GetType().GetEvent(((MemberExpression)eventRef.Body).Member.Name);
// source.eventRef -= handler;
ev.RemoveMethod.Invoke(source, new object[] { handler });
// do some stuff
// source.eventRef += handler;
ev.AddMethod.Invoke(source, new object[] { handler });
}
Использование
Pause(listBox1, c => c.SelectedValueChanged, listBox1_SelectedValueChanged);
К сожалению, это работает, только если событие реализовано так:
public event SomeDelegate MyEvent;
Если это реализовано так (и все события управления winform реализованы так)
public event SomeDelegate MyEvent {
add {
// do something
}
remove {
// do something
}
}
Это больше не работает, потому что вы не можете передать такую ссылку на событие через выражение. Однако выражение используется только для удобства, чтобы получить имя события. Таким образом, вы можете передать имя события явно:
private static void Pause<TSource, TEvent>(TSource source, string eventName, TEvent handler, int forTime = 200) {
var ev = source.GetType().GetEvent(eventName);
// source.eventRef -= handler;
ev.RemoveMethod.Invoke(source, new object[] { handler });
// do some stuff
// source.eventRef += handler;
ev.AddMethod.Invoke(source, new object[] { handler });
}
Использование тогда становится
Pause<ListBox, EventHandler>(listBox1, nameof(listBox1.SelectedValueChanged), listBox1_SelectedValueChanged);
Менее красиво, но все еще работает.
Я бы использовал Microsoft Reactive Framework - NuGet "System.Reactive" - тогда вы можете сделать это:
bool pause = false;
IObservable<EventPattern<EventArgs>> observable =
Observable
.FromEventPattern<EventHandler, EventArgs>(
h => listBox1.SelectedIndexChanged += h,
h => listBox1.SelectedIndexChanged -= h)
.Where(ep => pause != true);
IDisposable subscription =
observable
.Subscribe(ep => listBox1_SelectedValueChanged(ep.Sender, ep.EventArgs));
Сейчас просто меняем значение pause
от false
в true
приостановит обработку события.
Когда вы хотите отсоединить обработчик, просто позвоните subscription.Dispose()
,