如何让我的主 code/form 等待另一个 class' 函数在 C# 中完成,而不冻结?

How can I make my main code/form to wait for another class' function to finish in C#, without any freezing?

所以我有一个从 SQL 中提取 string/url 的代码,然后我的代码是这样的:

WebScrape browser1 = new WebScrape();
browser1.StartBrowser(weblink);
richTextBox1.Text = browser1.wholeText;

WebScrape 是一个包含浏览器的 class,StartBrowser 启动它并将其导航到 url/weblink,然后在文档完成时更新 "wholeText" 值。

这是 StartBrowser:

    public void StartBrowser(string address)
    {
        url = address;
        wb = new WebBrowser();
        wb.DocumentCompleted += Web_DocumentCompleted;
        wb.ScriptErrorsSuppressed = true;
        Navigate(url);
    }

转到我的导航功能进行更新url

    private void Navigate(String address)
    {
        if (String.IsNullOrEmpty(address)) return;
        if (address.Equals("about:blank")) return;
        if (!address.StartsWith("http://") && !address.StartsWith("https://"))
        {
            address = "http://" + address;
        }
        try
        {
            wb.Navigate(new Uri(address));
        }
        catch (System.UriFormatException)
        {
            return;
        }
    }

然后当 WebDocument 完成时我想要的变量被分配,如下所示:

    private void Web_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        htmlCode = wb.Document;
        wb.Dispose();
        wholeText = getTexts();
        plainText = splitText();
        numberOfWords = plainText.Length;
        webLinks = getLinks();
        imageLinks = getImages();
        imageSizes = getImageSize();
    }

这里的问题是,在 StartBrowser() 之后,它直接进入 richTextBox1.Text 而不是等待 class 自己完成它正在做的事情。

如何让我的代码等待我的 class 完成导航并在不冻结 UI/Form 的情况下执行此操作?

updates the "wholeText" value WHEN Document is completed.

那为什么不把 richTextBox1.Text = browser1.wholeText;在你的 Web_DocumentCompleted 方法中?

   private void Web_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    htmlCode = wb.Document;
    wb.Dispose();
    wholeText = getTexts();
    plainText = splitText();
    numberOfWords = plainText.Length;
    webLinks = getLinks();
    imageLinks = getImages();
    imageSizes = getImageSize();
    richTextBox1.Text = browser1.wholeText;
}

这应该可以解决第一个问题,但所有这些都在主线程上运行。

你可以这样做:

Thread backgroundThread = new Thread(new ThreadStart(new Action(() => browser1.StartBrowser(weblink))));
backgroundThread.Start();

这应该将您的 StartBrowser 方法移动到一个新线程,然后是您的 Web_DocumentCompleted 事件。我对此不是 100%,所以也许有人能比我更清楚地解决这个问题。请记住调用 UI 元素,因为您无法从另一个线程访问它们。

直接的方法是为您的 WebScrape 提供回调函数,以便在文档解析完成后调用:

public class WebScrape() {
    // ...
    public Action DocumentCompletedCallback;
}

然后在文档完成时调用它:

private void Web_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    // ...
    DocumentCompletedCallback?.Invoke();
}

然后你这样使用它:

WebScrape browser1 = new WebScrape();
browser1.DocumentCompletedCallback = () => {
    // may need to return to UI thread here via Invoke
    richTextBox1.Text = browser1.wholeText;
};
browser1.StartBrowser(weblink);    

如有必要,您可以使用 async\await 和任务(更多 "modern" 方式)做类似的事情。