从任务中访问 UI 元素

所有 UI 元素都创建并驻留在程序的主线程中。 .net 框架运行时禁止从另一个线程访问这些。基本上,这是因为所有 UI 元素都是**线程敏感资源,**并且在多线程环境中访问资源需要是线程安全的。如果允许此跨线程对象访问,则首先会影响一致性。

考虑这种情况:

我们在任务中进行了计算。任务在主线程之外的另一个线程中运行。在计算继续时,我们需要更新进度条。去做这个:

//Prepare the action
Action taskAction = new Action( () => {   
    int progress = 0;
    Action invokeAction = new Action( () => { progressBar.Value = progress; });
    while (progress <= 100) {
        progress = CalculateSomething();
        progressBar.Dispatcher.Invoke( invokeAction );
    }
} );

//After .net 4.5
Task.Run( taskAction );

//Before .net 4.5
Task.Factory.StartNew( taskAction ,
    CancellationToken.None, 
    TaskCreationOptions.DenyChildAttach, 
    TaskScheduler.Default);

每个 UI 元素都有一个 Dispatcher 对象,它来自 DispatcherObject 祖先(在 System.Windows.Threading 名称空间内)。Dispatcher 在与 Dispatcher 关联的线程上以指定的优先级同步执行指定的委托。由于执行是同步的,因此调用者任务应该等待其结果。这使我们有机会在调度员代表中使用 int progress

我们可能想要异步更新 UI 元素然后更改 invokeAction

//Prepare the action
Action taskAction = new Action( () => {   
    int progress = 0;
    Action<int> invokeAction = new Action<int>( (i) => { progressBar.Value = i; } )
    while (progress <= 100) {
        progress = CalculateSomething();
        progressBar.Dispatcher.BeginInvoke( 
            invokeAction,
            progress );
    }
} );

//After .net 4.5
Task.Run( taskAction );

//Before .net 4.5
Task.Factory.StartNew( taskAction ,
    CancellationToken.None, 
    TaskCreationOptions.DenyChildAttach, 
    TaskScheduler.Default);

这次我们打包 int progress 并将其用作委托的参数。