Как обновить поток WPF UI одновременно?
У меня есть сканарио, где я читаю данные с диска многопоточным способом, используя TPL. У меня WPF GUI в качестве основного потока процессов, который отображает данные, прочитанные из этих потоков.
Как я могу обновлять данные в WPF GUI в реальном времени по мере их обработки? Как я должен использовать concurrentQueue в этом отношении, который позволит рабочим потокам и потокам пользовательского интерфейса одновременно генерировать и потреблять данные?
Ниже приведен код рабочего потока:
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 - это просто обычный 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>
Итак, как я могу обновить вышеуказанную простую сетку, используя параллельную очередь, чтобы мой рабочий поток завершил свое выполнение, а поток пользовательского интерфейса должен одновременно обновлять результаты.
2 ответа
Вы можете забыть обо всем и позволить текущему диспетчеру справиться с этим, например
Application.Current.Dispatcher.Invoke(() => {
//Modify Ui here
});
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);
}
}
Тогда в вашем основном классе пользовательского интерфейса
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, для сообщения в поток пользовательского интерфейса, это не имеет значения, так как OnReport
метод может вызываться только одним потоком за раз (поток пользовательского интерфейса)