Асинхронная / ожидающая реализация класса WebBrowser для .NET - PullRequest
14 голосов
/ 23 декабря 2011

Давний читатель, первый постер здесь.

Моя цель: иметь возможность использовать async / await при использовании класса WebBrowser.Поскольку WebBrowser.Navigate (url-строка) является асинхронным методом, и вы не можете просматривать html-документ до тех пор, пока не будет запущено событие LoadComplete.

Вот мой (рабочий) код:1006 *

И этот предыдущий класс теперь позволяет мне использовать следующее:

public async void NavigateToGoogle() {
    await browser.NavigateAsync("www.google.com");
    //Do any necessary actions on google.com
}

Однако мне интересно, есть ли более эффективный / правильный способ обработки этого.В частности, Task.Factory.CreateNew с блокировкой ManualResetEvent.Спасибо за ваш вклад!

Ответы [ 4 ]

12 голосов
/ 23 декабря 2011

Прежде всего, я думаю, что это отличное упражнение для изучения того, как работает async / await.

Вы, кажется, перепрыгиваете через обручи, чтобы NavigateAsync возвращал задачу.Но он не должен возвращать задание, чтобы быть ожидаемым!Метод, который содержит await , должен возвращать Task, но метод, который является ожидаемым , не должен возвращать Task;все, что нужно сделать, это вернуть какой-то тип, который вы можете вызвать GetAwaiter.

Вы могли бы рассмотреть возможность реализации небольшого типа, подобного этому:

public struct WebBrowserAwaiter<T>
{
    public bool IsCompleted { get { ... } }
    public void OnCompleted(Action continuation) { ... }
    public T GetResult() { ... }
}

и заставить NavigateAsync возвращать некоторый тип, для которого вы можете вызвать GetAwaiter, который возвращает WebBrowserAwaiter.Нет необходимости создавать задачу просто для того, чтобы получить ее метод GetAwaiter, когда вы можете создать свой собственный.

В более общем смысле вы можете подумать о том, что произойдет, если произойдет второй вызовNavigateAsync, пока первый еще находится в навигации?

2 голосов
/ 23 декабря 2011

Вы можете использовать TaskCompletionSource<T>, чтобы создать задачу и пометить ее как выполненную позже.

Я не вижу альтернативы для неуниверсальной задачи, но так как Task<T> происходит от Task, вы можете просто использовать TaskCompletionSource<object> и установить результат в null.

0 голосов
/ 27 сентября 2017

Я перевел VB код Vaibhav на C #. это удивительное решение, я не знаю, почему вы разочарованы им.

public class YourClassThatIsUsingWebBrowser : IDisposable
{
    private WebBrowser browser;

    private TaskCompletionSource<BrowserResult> tcs;

    public YourClassThatIsUsingWebBrowser()
    {
        this.browser.DocumentCompleted += AsyncBrowser_DocumentCompleted;

        this.browser.Document.Window.Error += (errorSender, errorEvent) =>
        {
            SetResult(BrowserResult.Exception, errorEvent.Description);
        };
        this.browser.PreviewKeyDown += Browser_PreviewKeyDown;
        this.browser.Navigating += Browser_Navigating;
    }

    private void Browser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
    {
        tcs = new TaskCompletionSource<BrowserResult>();
    }

    private void Browser_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
    {
        if (e.KeyCode == Keys.Escape)
        {
            this.browser.Stop();
            SetResult(BrowserResult.Cancelled);
        }
    }

    private void AsyncBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        SetResult();
    }


    public async Task<BrowserResult> NavigateAsync(string urlString)
    {
        this.browser.Navigate(urlString);

        return await tcs.Task;
    }

    private void SetResult(BrowserResult result = BrowserResult.Succeed, string error = null)
    {
        if (tcs == null)
        {
            return;
        }
        switch (result)
        {
            case BrowserResult.Cancelled:
                {
                    tcs.SetCanceled();
                    break;
                }
            case BrowserResult.Exception:
                {
                    tcs.SetException(new Exception(error));
                    break;
                }
            case BrowserResult.Succeed:
            default:
                {
                    tcs.SetResult(result);
                    break;
                }
        }

    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    bool disposed = false;
    protected void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                this.browser.Dispose();
            }
        }
        disposed = true;
    }
}
public enum BrowserResult
{
    Succeed,
    Cancelled,
    Exception,
}
0 голосов
/ 23 февраля 2014

Я создал этот класс сегодня, с помощью другого поста на stackoverflow, я хочу получить готовый элемент управления webbrowser без какой-либо блокировки потоков с помощью (Async / Await).

Dim bb = New wbBrowser
Dim wb = Await bb.GetBrowserAsync("http://www.msn.com")

Вот класс:

Imports System.Threading
Imports System.Threading.Tasks

Public Class wbBrowser
    Implements IDisposable

    Dim m_wbBrowser As New WebBrowser
    Dim m_tcs As TaskCompletionSource(Of WebBrowser)

    Public Sub New()
        m_wbBrowser.ScrollBarsEnabled = False
        m_wbBrowser.ScriptErrorsSuppressed = False
        AddHandler m_wbBrowser.DocumentCompleted, Sub(s, args) m_tcs.SetResult(m_wbBrowser)
    End Sub

    Public Async Function GetBrowserAsync(ByVal URL As String) As Task(Of WebBrowser)
        m_wbBrowser.Navigate(URL)
        Return Await WhenDocumentCompleted(m_wbBrowser)
    End Function

    Private Function WhenDocumentCompleted(browser As WebBrowser) As Task(Of WebBrowser)
        m_tcs = New TaskCompletionSource(Of WebBrowser)
        Return m_tcs.Task
    End Function

    Private disposedValue As Boolean
    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                m_wbBrowser.Dispose()
            End If
        End If
        Me.disposedValue = True
    End Sub
    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

End Class
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...