簡體   English   中英

可以使用任務並行庫一起運行長時間運行的方法和“正在工作...”對話框以允許將長時間任務寫入 BindingList 嗎?

[英]Can a long-running method and a “Working…” dialog be run together using the Task Parallel Library to allow long task to write to a BindingList?

我有一個 WPF(C# 和 .NET 4)應用程序,其中有一個長時間運行的任務,它阻止了 UI,給人一種它已經掛起的印象。 我決定使用BackgroundWorker線程將這個長時間運行的任務放到一個單獨的線程中,並在單獨的彈出窗口 window(下面命名為 WorkingDialog)中顯示一個BusyIndicator cator。 這工作正常,直到長時間運行的任務寫入 BindingList(綁定到 UI 上的網格),我得到以下異常:

這種類型的 CollectionView 不支持從與 Dispatcher 線程不同的線程對其 SourceCollection 的更改

這是業務層代碼(一個非常精簡的版本)......

public class CustomMessage
{
    public DateTime TimeStamp { get; private set; }
    public string Message { get; private set; }

    public CustomMessage(string message)
    {
        Message = message;
        TimeStamp = DateTime.Now;
    }
}

public class MyRandomBusinessClass
{
    public BindingList<CustomMessage> LoggingList { get; private set; }

    public MyRandomBusinessClass()
    {
        LoggingList = new BindingList<CustomMessage>();
    }

    public void SomeLongRunningTask()
    {
        System.Threading.Thread.Sleep(5000);

        LoggingList.Add(new CustomMessage("Completed long task!"));
    }
}

...用戶界面...

<Window x:Class="WpfApplication4.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Height="350"
        Width="525">

    <StackPanel>
        <DataGrid x:Name="logGrid"
                  AutoGenerateColumns="True"
                  ItemsSource="{Binding Path=LoggingList}" />

        <Button Click="ProcessLongTask_Click"
                Content="Do Long Task" />
    </StackPanel>

</Window>

...用戶界面代碼...

using System.ComponentModel;
using System.Windows;

namespace WpfApplication4
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MyRandomBusinessClass RandomBusinessClass { get; set; }
        public MainWindow()
        {
            InitializeComponent();

            RandomBusinessClass = new MyRandomBusinessClass();

            logGrid.DataContext = RandomBusinessClass;
        }

        private void ProcessLongTask_Click(object sender, RoutedEventArgs e)
        {
            ProcessTask1();
        }

        private void ProcessTask1()
        {
            WorkingDialog workingDialog = new WorkingDialog();
            workingDialog.Owner = this;

            BackgroundWorker worker = new BackgroundWorker();

            worker.DoWork += delegate(object s, DoWorkEventArgs args)
            {
                RandomBusinessClass.SomeLongRunningTask();
            };

            worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
            {
                workingDialog.Close();
            };

            worker.RunWorkerAsync();
            workingDialog.ShowDialog();
        }
    }
}

...工人對話...

<Window xmlns:extToolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit/extended"
        x:Class="WpfApplication4.WorkingDialog"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        mc:Ignorable="d"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="Working Dialog"
        WindowStartupLocation="CenterOwner"
        Height="116"
        Width="199">

    <Grid>

        <extToolkit:BusyIndicator x:Name="busyIndicator"
                                  IsBusy="True"
                                  BusyContent="Working ..."
                                  HorizontalAlignment="Left"
                                  VerticalAlignment="Top" />

    </Grid>
</Window>

我在這個論壇中發現了幾個線程指向Bea Stollnitz的工作),但我想知道是否有一種方法可以執行長時間運行的任務,顯示 WorkingDialog,然后在任務完成后使用任務並行庫? 任務並行庫能否在不修改業務層代碼的情況下達到預期的效果?

編輯 - 解決方法 1

感謝 Ben 下面的評論,如果您的長時間運行的任務與 UI 在同一個程序集中或使用 System.Windows 命名空間,則有一種解決方法。 使用當前的應用程序調度程序,我可以毫無例外地將消息添加到我的列表中:

public class MyRandomBusinessClass
{
    public BindingList<CustomMessage> LoggingList { get; private set; }

    public MyRandomBusinessClass()
    {
        LoggingList = new BindingList<CustomMessage>();
    }

    public void SomeLongRunningTask()
    {
        System.Threading.Thread.Sleep(5000);

        Dispatcher myDispatcher = Application.Current.Dispatcher;
        myDispatcher.BeginInvoke((Action)(() => LoggingList.Add(new CustomMessage("Completed long task!"))));
    }
}

我仍然很好奇這是否可以使用任務並行庫或者我吠叫錯誤的樹? 在我的實際應用程序中,長期運行的 class 位於單獨的 class 庫中。

編輯 2

我在下面接受了 Ben 的解決方案,因為它解決了我最初的問題,即在不阻塞 UI 或引發異常的情況下運行長時間運行的任務。 我很欣賞最初的問題是關於任務並行庫的,所以如果您正在閱讀這篇文章並認為它具有 TPL 解決方案,請原諒我。

您需要使用 調度程序將新元素添加到日志列表中。

public void SomeLongRunningTask()
{
    System.Threading.Thread.Sleep(5000);

    Application.Current.Dispatcher.BeginInvoke((Action)(() => LoggingList.Add(new CustomMessage("Completed long task!"))));
}

編輯

public class MyRandomBusinessClass
{
  public BindingList<CustomMessage> LoggingList { get; private set; }
  public Action<MyRandomBusinessClass, CustomMessage> CallBackAction { get; private set; }

  public MyRandomBusinessClass(Action<MyRandomBusinessClass, CustomMessage> cba) 
  {
      LoggingList = new BindingList<CustomMessage>();
      this.CallBackAction = cba;
  }

  public void SomeLongRunningTask()
  {
    System.Threading.Thread.Sleep(5000);

    if (cba != null)
      CallBackAction(this, new CustomMessage("Completed long task!"));
  }
}

然后在哪里創建您的業務 class:

MyRandomBusinessClass businessClass = new MyRandomBusinessClass((sender, msg) =>
   Application.Current.Dispatcher.BeginInvoke((Action)(() => 
      sender.LoggingList.Add(msg))));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM