1

I have scanario where I am reading data from disk in multi threaded way using TPL. I have WPF GUI as processes's main thread which is displaying data read from these threads.

How can I update data on WPF GUI with real time as they processed? How should I use concurrentQueue in this regard that will allow both worker and UI threads to produce and consume data concurrently?

Following is the worker thread code :

   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);
   }
  }

And my main WPF GUI is simply a normal Grid which is hosting other 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>

So how can I update above simple grid by using concurrent queue so that my worker thread finish its execution and UI thread must concurrently update the results.

svick
  • 236,525
  • 50
  • 385
  • 514
Usman
  • 2,742
  • 4
  • 44
  • 82
  • Is WndFileContents responsible for calling ProcessFile? The worker thread needs a way of posting information back to the UI Thread. I find the IProgress / Progress Implementation to be good for this:https://msdn.microsoft.com/en-us/library/hh193692(v=vs.110).aspx – William Dec 04 '15 at 23:36

2 Answers2

1

You could forget about everything and let current dispatcher handle it, for example

Application.Current.Dispatcher.Invoke(() => {
   //Modify Ui here
});
bto.rdz
  • 6,636
  • 4
  • 35
  • 52
0
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);
}
}

Then in your main UI Class

private Progress<string> Reporter = new Progress<string>(OnReport);

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

There are a few ways of going about it, i'm a little bit confused towards what role ConcurrentQueue is taking in your code. A ConcurrentQueue is designed to allow multiple threads to read and write from it with less concern about synchronization. In that sence if you are using the information pulled from the ConcurrentQueue to report back to the UI thread it will not matter as the OnReport method can only be called by one thread at a time (the UI Thread)

William
  • 1,837
  • 2
  • 22
  • 36
  • Regarding ConcurrentQueue : I have just got it's hint that it will allow you to produced data from thread and consumed concurrently from UI. No idea how to use it. One Question : OnReport(start){ ...how to update WPF UI with this 'data' } How to update gui immediately as soon as we got data as a result of OnReport call? – Usman Dec 04 '15 at 23:51
  • Moreover, in case if multiple threads finished their processing, OnReport can be called concurrently? like multiple threads would call OnReport in that case? and they all would hav their own instance of Progress. What If, UI thread is busy in updating GUI? then where woker threads concurrent results will go? In that case Queue is necassary to hold the results. Would that make sense? – Usman Dec 04 '15 at 23:59
  • I've updated the code slightly because it's better that they share an instance of `Progress`. Yes multiple threads can call `reporter.Report("data)` and this will get marshalled onto the UI Thread. BUT only one thread will get into `OnReport` at a time (the UI Thread) the reports will be automatically be queued up by the dispatcher. Remember it has to be the UI Thread that updates the UI, so the queuing of updates is unavoidable – William Dec 05 '15 at 00:02
  • I can only speculate about your code, but looking at the xaml i'm going to assume your datagrid has some relation to the data which is pulled back. I'm also assuming `SelectedFileContents` is the source of which data needs adding? So assuming it is an `ObseravableCollection` you could add your data to it when `OnReport` is called, this will update your UI automatically – William Dec 05 '15 at 00:08