C# - Remove Event From Browser

本秂侑毒 提交于 2020-03-27 07:02:06

问题


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

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