什么是更新从任务出厂线程在WPF UI中的正确方法是什么?(What is the correct

2019-10-18 01:35发布

我想从我的线程通过派出的一个更新UI Task.Factory 。 我已经有很难正确地更新我的UI。

这里是我观察的行为:

Task.Factory.StartNew(() =>
    {
        // UI does get updated from here.
    }).ContinueWith(task =>
        {
            // UI does *not* get updated from here.
        });

什么是更新与派遣一个线程中UI的正确方法Task Factory

这是我给大家参考实际的代码:

private string CurrentProcess
{
    set { _eventAggregator.GetEvent<CurrentProcessUpdatedEvent>().Publish(value); }
}

private double ProgressPercentage
{
    set
    {
        _eventAggregator.GetEvent<ProgressPercentageUpdatedEvent>()
                        .Publish(Utilities.GetProgressPercentage(value));
    }
}

TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var tasks = new List<Task<DataTable>>();

string siteCollectionUrl;
string connectionString;

try
{
    Dictionary<string, object> session = ApplicationContext.Current.Session;

    try
    {
        if ((double) session["ProgressPercentage"] > 0) return;
    }
    catch
    {
    }

    siteCollectionUrl = (string) session["SiteCollection"];
    connectionString = (string) session["Database"];
}
catch
{
    return;
}

_eventAggregator.GetEvent<IsProcessingChangedEvent>().Publish(true);
CurrentProcess = "Loading resources.";

Task<DataTable> spTask = Task<DataTable>.Factory.StartNew(() =>
    {
        using (ChannelFactory<ISharePointService> service = Utilities.GetSharePointService())
        {
            ISharePointService sharePointService = service.CreateChannel();
            DataTable spDatatable = sharePointService.GetResources(siteCollectionUrl);

            Task.Factory.StartNew(() => { ProgressPercentage = 10; }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);

            return spDatatable;
        }
    });

tasks.Add(spTask);

Task<DataTable> buildTableTask = Task<DataTable>.Factory.ContinueWhenAll(tasks.ToArray(), t =>
    {
        DataTable spDatatable = t[0].Result;

        double percent = 10/spDatatable.Rows.Count;

        var columnMap = new Dictionary<string, string>
            {
                {"IsValid", null},
                {"Reason", null},
                {"SPID", "ID"},
                {"DBID", "EXTID"},
                {"Name", "Title"},
                {"Account", "SharePointAccount"},
                {"Email", "Email"},
                {"Generic", "Generic"},
                {"Department", "Department"},
                {"TempDept", "TempDept"},
                {"Role", "Role"},
                {"TempRole", "TempRole"},
                {"HolidaySchedule", "HolidaySchedule"},
                {"WorkHours", "WorkHours"}
            };

        DataTable spResources = BuildDataTable(columnMap);

        foreach (DataRow dataRow in spDatatable.Rows)
        {
            DataRow row = spResources.NewRow();

            foreach (var pair in columnMap)
            {
                try
                {
                    row[pair.Key] = dataRow[pair.Value];
                }
                catch
                {
                }
            }

            spResources.Rows.Add(row);

            Task.Factory.StartNew(() => { ProgressPercentage = percent; }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
        }

        return spResources;
    });

tasks.Add(buildTableTask);

Task<DataTable> dbTask = Task<DataTable>.Factory.StartNew(() =>
    {
        using (var sqlConnection = new SqlConnection(connectionString))
        {
            using (var sqlCommand = new SqlCommand(SQL, sqlConnection))
            {
                sqlConnection.Open();
                using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader())
                {
                    var dataTable = new DataTable();
                    dataTable.Load(sqlDataReader);

                    Task.Factory.StartNew(() => { ProgressPercentage = 10; }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);

                    return dataTable;
                }
            }
        }
    });

tasks.Add(dbTask);

Task.Factory.ContinueWhenAll(tasks.ToArray(), t =>
    {
        DatabaseResources = t[2].Result;
        DataTable sharePointResources = t[1].Result;

        if (sharePointResources != null)
        {
            int resourceIndex = 1;
            int totalResources = sharePointResources.Rows.Count;
            double percentPoint = 70/totalResources;

            foreach (DataRow row in sharePointResources.Rows)
            {
                DataRow currentRow = row;

                Task.Factory.StartNew(() =>
                    {
                        CurrentProcess = string.Format("[{0}/{1}] Processing: {2}",
                                                        resourceIndex++, totalResources,
                                                        currentRow["Name"]);
                    }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);

                bool isValid = true;
                var reasons = new List<string>();

                DataRow[] dataRows =
                    _databaseResources.Select(string.Format("ResourceID = {0}", row["DBID"]));
                if (dataRows.Any())
                {
                    DataRow dataRow = dataRows[0];

                    string tempDept = (row["TempDept"] ?? string.Empty).ToString();
                    string dept = (row["Department"] ?? string.Empty).ToString();

                    string tempRole = (row["TempRole"] ?? string.Empty).ToString();
                    string role = (row["Role"] ?? string.Empty).ToString();

                    string hs = (row["HolidaySchedule"] ?? string.Empty).ToString();
                    string dbhs = (dataRow["HolidaySchedule"] ?? string.Empty).ToString();

                    string wh = (row["WorkHours"] ?? string.Empty).ToString();
                    string dbwh = (dataRow["WorkHours"] ?? string.Empty).ToString();

                    if (string.IsNullOrEmpty(dept))
                    {
                        if (!dept.Equals(tempDept))
                        {
                            isValid = false;
                            reasons.Add("Department does not match Temp Dept");
                        }
                    }

                    if (string.IsNullOrEmpty(role))
                    {
                        if (!role.Equals(tempRole))
                        {
                            isValid = false;
                            reasons.Add("Role does not match Temp Role");
                        }
                    }

                    if (string.IsNullOrEmpty(hs))
                    {
                        if (!hs.Equals(dbhs))
                        {
                            isValid = false;
                            reasons.Add("Holiday Schedule does not match Holiday Schedule from database");
                        }
                    }

                    if (string.IsNullOrEmpty(wh))
                    {
                        if (!wh.Equals(dbwh))
                        {
                            isValid = false;
                            reasons.Add("Work Hours does not match Work Hours from database");
                        }
                    }
                }
                else
                {
                    isValid = false;
                    reasons.Add("Resource does not exist in database");
                }

                row["IsValid"] = isValid;
                row["Reason"] = string.Join("\n", reasons.ToArray());

                Task.Factory.StartNew(() => { ProgressPercentage = percentPoint; }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
            }

            SharePointResources = sharePointResources;
        }

        _eventAggregator.GetEvent<ProgressPercentageUpdatedEvent>()
                        .Publish(Utilities.ResetProgressPercentage());
        _eventAggregator.GetEvent<IsProcessingChangedEvent>().Publish(false);
    });

Answer 1:

// UI不会获取从这里更新

您应该推出一个new Action(() =>通过DispatcherObject在WPF

Task.Factory.StartNew(() =>
    {
        // UI does get updated from here
        this.Dispatcher.BeginInvoke(new Action(() => 
        {

请搜索在最后一行的“ 第1部分-入门 ”“在.NET Framework 4的并行编程”亚历山德拉Rusina的系列

我相信你会享受所有的续集从这个裁判进一步上。

第2部分-任务取消演示如何使用任务调度程序来代替:

var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.ContinueWhenAll(tasks.ToArray(),
    result =>
    {
        var time = watch.ElapsedMilliseconds;
        label1.Content += time.ToString();
    }, CancellationToken.None, TaskContinuationOptions.None, ui);

代替:

Task.Factory.ContinueWhenAll(tasks.ToArray(),
      result =>
      {
          var time = watch.ElapsedMilliseconds;
          this.Dispatcher.BeginInvoke(new Action(() =>
              label1.Content += time.ToString()));
      });  

在回应评论

“首先,我使用PRISM。所以,在我ViewModwl我必须使用Dispatcher.Current.BeginInvoke ---我试过了。它没有帮助”

请检查答案“是WPF调度的多线程处理问题的解决?” 相关Prism中使用的调度员和访问用户界面:

// Not a UI component
public class MyDomainService : IMyDomainService
{
   private readonly IDispatcher _dispatcher;

   public MyDomainService(IDispatcher dispatcher) 
   {
      _dispatcher = dispatcher;
   }

   private void GotResultFromBackgroundThread()
   {
       _dispatcher.Dispatch(() => DoStuffOnForegroundThread());
   }
}

“ 你需要确保你正在调用实际UI调度,不一定是当前 ”

你可以参加的PRISM事件聚集 ,以确保您的UI线程或基本就Dispatcher.CheckAccess方法

如果你使用的TaskScheduler,那么你应该得到TaskScheduler.FromCurrentSynchronizationContext在UI线程(例如,在Window.Loaded事件处理程序,你会得到双击您的形式),并通过/股/与任务。



Answer 2:

看看这个视频。 DNRTV情节以斯蒂芬Toub 。 它集中了当时即将.NET 4.5的功能,但在最初的概括,它涵盖了GUI的使用taskschedulers很好地任务编组的话题。



Answer 3:

更新UI的最简单方法就是通过SynchronizationContext.Post / 发送方法。 的SynchronizationContext隐藏了底层的UI库(WPF或WinForms的),并确保您指定的操作正确的线程上执行。

发送块,直到UI处理完动作后,同时异步执行的操作。

要以异步方式,你可以使用这样的代码更新UI:

SyncrhonizationContext.Current.Post(value=>myTextBox.Text=(string)value,"23455");

类似的选项是指定在ContinueWith TaskScheduler.FromCurrentSynchronizationContext()来让你继续在UI线程中执行:

.ContinueWith(t=>{
    myTextBox.Text="Some Value";
 });

这等同于调用SynchronizationContext.Current.Send

你还别说,你使用MVVM和PRISM和异步操作的视图模型执行。 更改视图模型属性将不会出现在视图中,除非属性或您的异步代码也引起了NotifyPropertyChanged事件。 这是一个问题PRISM虽然不是TPL问题。

我还注意到,您发布更改属性,而不是提高NotifyPropertyChanged事件的事件。 数据绑定取决于从源属性接收NotifyPropertyChanged。 除非你添加代码以某种方式提高正确的事件,视图控件将不会被更新。

在PRISM的ViewModels通常从继承NotificationObject它实现INotifyPropertyChanged接口。 尝试调用RaisePropertyChanged方法来提高你的proeprties内NotifyPropertyChanged事件,看看这是否解决您的问题。



文章来源: What is the correct way to update the UI in WPF from a thread dispatched from Task Factory?