0

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;
};
Saman Salehi
  • 1,995
  • 1
  • 22
  • 36

2 Answers2

2

You just need a reference to your delegate. Something like this:

WebBrowserDocumentCompletedEventHandler complete = null;
complete = (s, e) => {
    webBrowser.DocumentComplete -= complete;
};
webBrowser.DocumentComplete += complete;
vcsjones
  • 138,677
  • 31
  • 291
  • 286
1

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.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • It's such a great informative answer... But is hard to implement... Thank you for share your information with me – Saman Salehi Jan 30 '16 at 22:15
  • @K2evil - Thanks, but why do you say it is hard to implement? I've provided all of the code. – Enigmativity Jan 30 '16 at 23:03
  • I mean if I reference handler, It add only 2 line to my code... But your solution needs tons of new line :D I will use your codes when there is no way to reference Handler – Saman Salehi Jan 31 '16 at 00:47
  • @K2evil - That's the point of creating libraries. You should see how many lines are in the .NET Framework. Granted my code is fairly complicated, but it means you only have to write a single line of code when you want to use it. – Enigmativity Jan 31 '16 at 06:42
  • Correct... It seems that you are professional in c#. I have a strange issue that couldn't solve it with my knowledge... Can you please see this post and help me if you know about this problem: [Question](http://stackoverflow.com/questions/35119590/application-run-cause-to-hang-program) – Saman Salehi Jan 31 '16 at 21:20
  • @K2evil - I've put a comment on your other question. – Enigmativity Feb 01 '16 at 01:20