源项目地址:https://github.com/Microsoft/...
以下是把样例转换为简要说明,同时给出实际运行效果及关键代码:

1 MultiThreadingWebBrowser

clipboard.png

private void Browse(object sender, RoutedEventArgs e)
{
    placeHolder.Source = new Uri(newLocation.Text);
}

private void NewWindowHandler(object sender, RoutedEventArgs e)
{
    var newWindowThread = new Thread(ThreadStartingPoint);
    newWindowThread.SetApartmentState(ApartmentState.STA);
    newWindowThread.IsBackground = true;
    newWindowThread.Start();
}

private void ThreadStartingPoint()
{
    var tempWindow = new MainWindow();
    tempWindow.Show();
    Dispatcher.Run();
    
    //Dispatcher.BeginInvoke(DispatcherPriority.Normal,
    //(ThreadStart)delegate ()
    //{
    //    var temWindow = new MainWindow();
    //    temWindow.Show();
    //});
}

2 SingleThreadedApplication

clipboard.png

调用自身线程循环查找素数
private void StartOrStop(object sender, EventArgs e)
{
    if (_continueCalculating)
    {
        _continueCalculating = false;
        startStopButton.Content = "Resume";
    }
    else
    {
        _continueCalculating = true;
        startStopButton.Content = "Stop";
        startStopButton.Dispatcher.BeginInvoke(
            DispatcherPriority.Normal,
            new NextPrimeDelegate(CheckNextNumber));
    }
}

public void CheckNextNumber()
{
    Stopwatch x = new Stopwatch();
    x.Start();

    // Reset flag.
    _notAPrime = false;

    for (long i = 3; i <= Math.Sqrt(_num); i++)
    {
        if (_num%i == 0)
        {
            // Set not a prime flag to ture.
            _notAPrime = true;
            break;
        }
    }

    // If a prime number.
    if (!_notAPrime)
    {
        x.Stop();
        elapsed.Text = x.ElapsedTicks.ToString();                
        bigPrime.Text = _num.ToString();
    }

    _num += 2;
    if (_continueCalculating)
    {
        startStopButton.Dispatcher.BeginInvoke(
            DispatcherPriority.SystemIdle,
            new NextPrimeDelegate(CheckNextNumber));
    }
}

3 UsingDispatcher天气预报

clipboard.png

在本示例中,模拟检索天气预报的远程过程调用。 使用一个单独的辅助线程来执行此调用,并在完成后在 UI 线程的 Dispatcher 中调度一个更新方法。

  • 创建按钮处理程序
private void ForecastButtonHandler(object sender, RoutedEventArgs e)
{
    // Change the status image and start the rotation animation.
    fetchButton.IsEnabled = false;
    fetchButton.Content = "Contacting Server";
    weatherText.Text = "";
    _hideWeatherImageStoryboard.Begin(this);

    // Start fetching the weather forecast asynchronously.
    var fetcher = new NoArgDelegate(
        FetchWeatherFromServer);

    fetcher.BeginInvoke(null, null);
}

当单击按钮时,显示时钟图并开始显示它的动画效果。 禁用该按钮, 在一个新线程中调用 FetchWeatherFromServer 方法,然后返回,这样 Dispatcher 就可以在我们等待收集天气预报时处理事件。

  • 获取天气预报
private void FetchWeatherFromServer()
{
    // Simulate the delay from network access.
    Thread.Sleep(4000);

    // Tried and true method for weather forecasting - random numbers.
    var rand = new Random();
    string weather;

    weather = rand.Next(2) == 0 ? "rainy" : "sunny";

    // Schedule the update function in the UI thread.
    tomorrowsWeather.Dispatcher.BeginInvoke(
        DispatcherPriority.Normal,
        new OneArgDelegate(UpdateUserInterface),
        weather);
}

为简单起见,此示例中实际没有任何网络代码。 通过使新线程休眠四秒钟来模拟网络访问的延迟。 此时,原始的 UI 线程仍然正在运行并响应事件。为了对此进行说明,我们使一个动画保持运行,并使最小化和最大化按钮也继续工作。

当延迟结束,并且我们已随机选择了天气预报时,是时候向 UI 线程返回报告了。为此,我们在 UI 线程中使用该线程的 Dispatcher 安排一个对 UpdateUserInterface 的调用。我们将一个描述天气的字符串传递给安排的此方法调用。

  • 更新 UI
private void UpdateUserInterface(string weather)
{
    //Set the weather image
    if (weather == "sunny")
    {
        weatherIndicatorImage.Source = (ImageSource) Resources[
            "SunnyImageSource"];
    }
    else if (weather == "rainy")
    {
        weatherIndicatorImage.Source = (ImageSource) Resources[
            "RainingImageSource"];
    }

    //Stop clock animation
    _showClockFaceStoryboard.Stop(this);
    _hideClockFaceStoryboard.Begin(this);

    //Update UI text
    fetchButton.IsEnabled = true;
    fetchButton.Content = "Fetch Forecast";
    weatherText.Text = weather;
}

当 UI 线程中的 Dispatcher 有时间时,会对 UpdateUserInterface 执行预定调用。此方法停止时钟动画并选择一个图像来描述天气。它显示此图像并还原“fetch forecast”(获取预报)按钮。

以上。

技术细节和难点

  • 使用线程编写组件

《Microsoft .NET Framework 开发人员指南》介绍了组件向其客户端公开异步行为的一种模式(请参见 基于事件的异步模式概述)。例如,假定我们希望将 FetchWeatherFromServer 方法打包到一个可重用的非图形组件中。如果采用标准的 Microsoft .NET Framework 模式,那么代码应与下面的内容类似。

public class WeatherComponent : Component
{
    //gets weather: Asynchronous 
    public string GetWeather()
    {
        string weather = "";

        //predict the weather

        return weather;
    }

    //get weather: Asynchronous 
    public void GetWeatherAsync()
    {
        //get the weather
    }

    public event GetWeatherCompletedEventHandler GetWeatherCompleted;
}

public class GetWeatherCompletedEventArgs : AsyncCompletedEventArgs
{
    public GetWeatherCompletedEventArgs(Exception error, bool canceled,
        object userState, string weather)
        :
        base(error, canceled, userState)
    {
        _weather = weather;
    }

    public string Weather
    {
        get { return _weather; }
    }
    private string _weather;
}

public delegate void GetWeatherCompletedEventHandler(object sender,
    GetWeatherCompletedEventArgs e);

GetWeatherAsync 将使用前面介绍的一种技术(如创建后台线程)来异步执行工作,同时不阻止调用线程。

此模式的最重要部分之一是最初在调用方法名称 Async 方法的线程上调用方法名称 Completed 方法。 通过存储 CurrentDispatcher,您可以使用 WPF 轻松地实现这一点。但是,之后只能在 WPF应用程序中使用该非图形组件,而不能在 Windows Forms或 ASP.NET 程序中使用该组件。

DispatcherSynchronizationContext 类可满足这一需求。可以将该类视为还使用其他 UI 框架的 Dispatcher 的简化版本。

public class WeatherComponent2 : Component
{
    public string GetWeather()
    {
        return fetchWeatherFromServer();
    }

    private DispatcherSynchronizationContext requestingContext = null;

    public void GetWeatherAsync()
    {
        if (requestingContext != null)
            throw new InvalidOperationException("This component can only handle 1 async request at a time");

        requestingContext = (DispatcherSynchronizationContext)DispatcherSynchronizationContext.Current;

        NoArgDelegate fetcher = new NoArgDelegate(this.fetchWeatherFromServer);

        // Launch thread
        fetcher.BeginInvoke(null, null);
    }

    private void RaiseEvent(GetWeatherCompletedEventArgs e)
    {
        if (GetWeatherCompleted != null)
            GetWeatherCompleted(this, e);
    }

    private string fetchWeatherFromServer()
    {
        // do stuff
        string weather = "";

        GetWeatherCompletedEventArgs e =
            new GetWeatherCompletedEventArgs(null, false, null, weather);

        SendOrPostCallback callback = new SendOrPostCallback(DoEvent);
        requestingContext.Post(callback, e);
        requestingContext = null;

        return e.Weather;
    }

    private void DoEvent(object e)
    {
        //do stuff
    }

    public event GetWeatherCompletedEventHandler GetWeatherCompleted;
    public delegate string NoArgDelegate();
}

此处在MSDN或Microsoft Help查看器中WPF线程处理模型提及。


李志玮
22 声望34 粉丝

求索~~