问题
I add an event to webBrowser
as below code:
webBrowser.DocumentCompleted += (s, e) => {};
Now How can I remove this event inside of it? Somthing like this:
webBrowser.DocumentCompleted += (s, e) => {
webBrowser.DocumentCompleted -= this;
};
回答1:
You just need a reference to your delegate. Something like this:
WebBrowserDocumentCompletedEventHandler complete = null;
complete = (s, e) => {
webBrowser.DocumentComplete -= complete;
};
webBrowser.DocumentComplete += complete;
回答2:
I'm putting this answer in as an adjunct to the accepted answer.
It is possible to do some delegate magic to avoid explicitly taking a reference for detaching a handler.
Here's the final code that can be written (in your webBrowser
case):
IDisposable subscription =
Disposable
.HandleOnce(
h => webBrowser.DocumentCompleted += h,
h => webBrowser.DocumentCompleted -= h,
(s, e) =>
{
Console.WriteLine("DocumentCompleted!");
});
Keeping a reference to subscription
is optional, but calling subscription.Dispose()
allows the handler to be cancelled before it runs once.
To start with you need this code:
public static class Disposable
{
public static IDisposable Create(Action dispose)
{
if (dispose == null)
throw new ArgumentNullException("dispose");
return (IDisposable)new AnonymousDisposable(dispose);
}
private sealed class AnonymousDisposable : IDisposable
{
private volatile Action _dispose;
public AnonymousDisposable(Action dispose)
{
_dispose = dispose;
}
public void Dispose()
{
Action action = Interlocked.Exchange<Action>(ref _dispose, (Action)null);
if (action != null)
{
action();
}
}
}
}
This allows any action to be turned into a IDisposable
that will call the action once and only once when .Dispose()
is called.
So, this code:
var subscription = Disposable.Create(() => Console.WriteLine("Done."));
subscription.Dispose();
subscription.Dispose();
...causes Done.
to be printed on the console only once.
Now, the HandleOnce
method can be added to the Disposable
class:
public static IDisposable HandleOnce(
Action<WebBrowserDocumentCompletedEventHandler> addHandler,
Action<WebBrowserDocumentCompletedEventHandler> removeHandler,
WebBrowserDocumentCompletedEventHandler handler)
{
if (addHandler == null)
throw new ArgumentNullException("addHandler");
if (removeHandler == null)
throw new ArgumentNullException("removeHandler");
if (handler == null)
throw new ArgumentNullException("handler");
WebBrowserDocumentCompletedEventHandler nested = null;
nested = (s, e) =>
{
removeHandler(nested);
handler(s, e);
};
addHandler(nested);
return Disposable.Create(() => removeHandler(nested));
}
Now, it is possible to make completely generic version of this code that doesn't have to be coded to know about delegates like WebBrowserDocumentCompletedEventHandler
. Here it is:
public static class Disposable
{
public static IDisposable Create(Action dispose)
{
if (dispose == null)
throw new ArgumentNullException("dispose");
return (IDisposable)new AnonymousDisposable(dispose);
}
public static IDisposable Handle<TDelegate, TEventArgs>(
Action<TDelegate> addHandler,
Action<TDelegate> removeHandler,
TDelegate handler)
{
if (addHandler == null)
throw new ArgumentNullException("addHandler");
if (removeHandler == null)
throw new ArgumentNullException("removeHandler");
if (handler == null)
throw new ArgumentNullException("handler");
addHandler(handler);
return Disposable.Create(() => removeHandler(handler));
}
public static IDisposable HandleOnce<TDelegate, TEventArgs>(
Action<TDelegate> addHandler,
Action<TDelegate> removeHandler,
TDelegate handler)
{
if (addHandler == null)
throw new ArgumentNullException("addHandler");
if (removeHandler == null)
throw new ArgumentNullException("removeHandler");
if (handler == null)
throw new ArgumentNullException("handler");
Action<object, TEventArgs> inner =
CreateDelegate<Action<object, TEventArgs>>(
handler,
typeof(TDelegate).GetMethod("Invoke"));
TDelegate outer = default(TDelegate);
IDisposable detach = Disposable.Create(() => removeHandler(outer));
Action<object, TEventArgs> nested = (s, e) =>
{
removeHandler(outer);
inner(s, e);
};
outer = CreateDelegate<TDelegate>(
nested,
typeof(Action<object, TEventArgs>).GetMethod("Invoke"));
addHandler(outer);
return detach;
}
public static TDelegate CreateDelegate<TDelegate>(object o, MethodInfo method)
{
return (TDelegate)(object)Delegate.CreateDelegate(typeof(TDelegate), o, method);
}
public static IDisposable Handle(
Action<EventHandler> addHandler,
Action<EventHandler> removeHandler,
EventHandler handler)
{
return Disposable.Handle<EventHandler, EventArgs>(addHandler, removeHandler, handler);
}
public static IDisposable HandleOnce(
Action<EventHandler> addHandler,
Action<EventHandler> removeHandler,
EventHandler handler)
{
if (addHandler == null)
throw new ArgumentNullException("addHandler");
if (removeHandler == null)
throw new ArgumentNullException("removeHandler");
if (handler == null)
throw new ArgumentNullException("handler");
EventHandler nested = null;
IDisposable detach = Disposable.Create(() => removeHandler(nested));
nested = (s, e) =>
{
detach.Dispose();
handler(s, e);
};
addHandler(nested);
return detach;
}
private sealed class AnonymousDisposable : IDisposable
{
private volatile Action _dispose;
public AnonymousDisposable(Action dispose)
{
_dispose = dispose;
}
public void Dispose()
{
Action action = Interlocked.Exchange<Action>(ref _dispose, (Action)null);
if (action != null)
{
action();
}
}
}
}
The above class also contains code for handling multiple event occurrences, not just HandleOnce
.
Now the code to call this looks like this:
IDisposable subscription =
Disposable
.HandleOnce<
WebBrowserDocumentCompletedEventHandler,
WebBrowserDocumentCompletedEventArgs>(
h => webBrowser.DocumentCompleted += h,
h => webBrowser.DocumentCompleted -= h,
(s, e) =>
{
Console.WriteLine("DocumentCompleted!");
});
Again there's no need to keep the reference to subscription
unless you want to detach before the DocumentCompleted
event fires.
来源:https://stackoverflow.com/questions/35074591/c-sharp-remove-event-from-browser