【问题标题】:How to update WPF UI Thread concurrently?如何同时更新 WPF UI 线程?
【发布时间】:2015-12-04 23:31:28
【问题描述】:

我有 scanario,我正在使用 TPL 以多线程方式从磁盘读取数据。 我有 WPF GUI 作为进程的主线程,它显示从这些线程读取的数据。

如何在处理 WPF GUI 时实时更新数据? 在这方面我应该如何使用 concurrentQueue 以允许工作线程和 UI 线程同时生成和消费数据?

以下是工作线程代码:

   public void ProcessFile(string a_strFilePath)
   {
     try
     {
          var fileType = this.GetFileType(a_strFilePath);
          string assemblyToLoad = string.Format("DirectoryMonitoring.{0}Loader", fileType);
          Assembly assembly = Assembly.LoadFrom(assemblyToLoad + ".dll");

          if (assembly != null)
          {
            Type type = assembly.GetType(assemblyToLoad);
            dynamic instance = Activator.CreateInstance(type);
            FileSchema fileSchema = instance.Read(a_strFilePath);

           //ConcurrentQueue....how can it be used??
          }
   }
   catch (Exception ex)
   {
            //Log.Write(ex.Message);
   }
  }

我的主要 WPF GUI 只是一个普通的 Grid,它托管其他 Grid。

<Window x:Class="DirectoryMonitoring.UI.View.WndFileContents"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="FileContents" Height="500" Width="700" ResizeMode="NoResize">
<Grid Margin="0,0,0,0">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <!--<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="400" Grid.Row="0">
        <ItemsControl>-->
            <DataGrid  Grid.Row="0" x:Name="grdFilesContents" CanUserReorderColumns="True" CanUserResizeColumns="True" 
                       ItemsSource="{Binding SelectedFileContents}"  IsReadOnly="True" CanUserResizeRows="False" CanUserSortColumns="True" AutoGenerateColumns="False">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Date" Binding="{Binding Path=Date}"  Width="100"/>
                    <DataGridTextColumn Header="Open" Binding="{Binding Path=Open}"  Width="80" />
                    <DataGridTextColumn Header="High" Binding="{Binding Path=High}" Width="80" />
                    <DataGridTextColumn Header="Low" Binding="{Binding Path=Low}" Width="80" />
                    <DataGridTextColumn Header="Close" Binding="{Binding Path=Close}" Width="80" />
                    <DataGridTextColumn Header="Volume" Binding="{Binding Path=Volume}" Width="200" />
                </DataGrid.Columns>
            </DataGrid>
        <!--</ItemsControl>
    </ScrollViewer>-->

    <Grid Grid.Row="1">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" ></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Button VerticalAlignment="Center" HorizontalAlignment="Right" Name="Close" Width="100" Height="20" Grid.Row="0" Grid.Column="0" Margin="5" Click="Close_Click_1">Close</Button>
    </Grid>
</Grid>

那么我怎样才能通过使用并发队列来更新上面的简单网格,以便我的工作线程完成它的执行并且 UI 线程必须同时更新结果。

【问题讨论】:

标签: c# .net wpf task-parallel-library concurrent-queue


【解决方案1】:

例如,您可以忘记所有事情并让当前调度程序处理它

Application.Current.Dispatcher.Invoke(() => {
   //Modify Ui here
});

【讨论】:

    【解决方案2】:
    public void ProcessFile(string a_strFilePath, IProgress<string> reporter)
    {
     try
     {
          var fileType = this.GetFileType(a_strFilePath);
          string assemblyToLoad = string.Format("DirectoryMonitoring.{0}Loader", fileType);
          Assembly assembly = Assembly.LoadFrom(assemblyToLoad + ".dll");
    
          if (assembly != null)
          {
            Type type = assembly.GetType(assemblyToLoad);
            dynamic instance = Activator.CreateInstance(type);
            FileSchema fileSchema = instance.Read(a_strFilePath);
    
           //ConcurrentQueue....how can it be used??
           reporter.Report("Reporting data back to UI Thread")
          }
    }
    catch (Exception ex)
    {
            //Log.Write(ex.Message);
    }
    }
    

    然后在你的主 UI 类中

    private Progress<string> Reporter = new Progress<string>(OnReport);
    
    void StartProcessFile()
    {
         //Wrap is Task/Thread/etc
         new MyClass().ProcessFile(path, Reporter);
    }
    void OnReport(string data)
    { 
    }
    

    有几种方法可以解决这个问题,我对 ConcurrentQueue 在您的代码中所扮演的角色有点困惑。 ConcurrentQueue 旨在允许多个线程从中读取和写入,而不必担心同步。在这种情况下,如果您使用从 ConcurrentQueue 中提取的信息来报告回 UI 线程,这无关紧要,因为 OnReport 方法一次只能由一个线程(UI 线程)调用

    【讨论】:

    • 关于 ConcurrentQueue :我刚刚得到提示,它允许您从线程生成数据并从 UI 并发消费。不知道如何使用它。一个问题: OnReport(start){ ...how to update WPF UI with this 'data' } How to update gui 一旦我们通过 OnReport 调用获得数据后立即更新?
    • 另外,如果多个线程处理完毕,可以同时调用OnReport吗?在这种情况下,多个线程会调用 OnReport 吗?他们都有自己的 Progress 实例。如果 UI 线程忙于更新 GUI 怎么办?那么woker线程并发结果会去哪里呢?在这种情况下,队列是必要的来保存结果。这有意义吗?
    • 我稍微更新了代码,因为它们最好共享一个Progress 的实例。是的,多个线程可以调用reporter.Report("data),这将被编组到 UI 线程上。但是一次只有一个线程会进入OnReport(UI 线程),报告将由调度程序自动排队。请记住,更新 UI 的必须是 UI Thread,因此更新的排队是不可避免的
    • 我只能推测您的代码,但查看 xaml 我会假设您的数据网格与被拉回的数据有某种关系。我还假设SelectedFileContents 是需要添加哪些数据的来源?因此,假设它是 ObseravableCollection,您可以在调用 OnReport 时将数据添加到其中,这将自动更新您的 UI
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-08
    • 1970-01-01
    • 2014-08-24
    • 1970-01-01
    • 2010-10-18
    相关资源
    最近更新 更多