Ich habe Probleme mit der Verwendung von EventAggregator in meiner Anwendung. Das Problem, mit dem ich konfrontiert bin, ist, dass die Benutzeroberfläche erst aktualisiert wird, wenn die aktuelle Verarbeitung gestoppt wurde. Ich hatte den Eindruck, dass EventAggregator in einem eigenen Thread lief und daher in der Lage sein sollte, die Benutzeroberfläche zu aktualisieren, sobald ein Ereignis veröffentlicht wurde. Habe ich dieses Konzept missverstanden?WPF, PRISM und EventAggregor
unten ist mein Code
Bootstrapper.cs
class Bootstraper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return ServiceLocator.Current.GetInstance<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
}
App.xmal.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var bs = new Bootstraper();
bs.Run();
}
}
MainWindow.xmal
<Window x:Class="TransactionAutomationTool.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TransactionAutomationTool"
xmlns:views="clr-namespace:TransactionAutomationTool.Views"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="800">
<Grid>
<views:HeaderView x:Name="HeaderViewCntl" Margin="20,21,10,0" Height="70" Width="740" HorizontalAlignment="Left" VerticalAlignment="Top" />
<views:ProcessSelectionView x:Name="ProcessSelectionViewControl" Margin="20,105,0,0" Height="144" Width="257" HorizontalAlignment="Left" VerticalAlignment="Top" />
<views:ProcessInputView x:Name="ProcessInputViewControl" Margin="20,280,0,0" Height="218" Width="257" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<views:ProcessLogView x:Name="ProcessLogViewControl" Margin="298,105,0,0" Height="445" Width="462" HorizontalAlignment="Left" VerticalAlignment="Top" />
<views:ButtonsView x:Name="ButtonViewControl" Margin="0,513,0,0" Height="37" Width="300" HorizontalAlignment="Left" VerticalAlignment="Top" />
</Grid>
ProcessLogView.xaml
<UserControl x:Class="TransactionAutomationTool.Views.ProcessLogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TransactionAutomationTool.Views"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"
d:DesignHeight="445" d:DesignWidth="462">
<UserControl.Resources>
<DataTemplate x:Key="TwoLinkMessage">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Message}" />
<TextBlock>
<Hyperlink NavigateUri="{Binding Link}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="HyperLinkClicked">
<ei:CallMethodAction MethodName="HyperLinkClicked" TargetObject="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock Text="{Binding Link}"/>
</Hyperlink>
</TextBlock>
<TextBlock>
<Hyperlink NavigateUri="{Binding SecondLink}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="HyperLinkClicked">
<ei:CallMethodAction MethodName="HyperLinkClicked" TargetObject="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock Text="{Binding SecondLink}"/>
</Hyperlink>
</TextBlock>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="LinkMessage">
<TextBlock>
<Hyperlink NavigateUri="{Binding Link}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="HyperLinkClicked">
<ei:CallMethodAction MethodName="HyperLinkClicked" TargetObject="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock Text="{Binding Message}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
<DataTemplate x:Key="Default">
<TextBlock Text="{Binding Message}" />
</DataTemplate>
</UserControl.Resources>
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="15">
<!--<ListBox x:Name="lbxProgress" HorizontalAlignment="Left" Height="408" Margin="5,5,0,0" VerticalAlignment="Top" Width="431" Foreground="Black" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding LogMessage}" BorderThickness="0" />-->
<ListView Name="lvProgress" ItemsSource="{Binding LogMessage}" Margin="9" BorderThickness="0">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ContentTemplate" Value="{StaticResource Default}" />
<Style.Triggers>
<DataTrigger Binding="{Binding LinkNum}" Value="0">
<Setter Property="ContentTemplate" Value="{StaticResource Default}" />
</DataTrigger>
<DataTrigger Binding="{Binding LinkNum}" Value="1">
<Setter Property="ContentTemplate" Value="{StaticResource LinkMessage}" />
</DataTrigger>
<DataTrigger Binding="{Binding LinkNum}" Value="2">
<Setter Property="ContentTemplate" Value="{StaticResource TwoLinkMessage}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</Border>
ProcessLogViewModel.cs
class ProcessLogViewModel: EventsBase
{
private ObservableCollection<LogPayload> logMessage;
public ObservableCollection<LogPayload> LogMessage
{
get { return logMessage; }
set { SetProperty(ref logMessage, value); }
}
public ProcessLogViewModel()
{
//If statement is required for viewing the MainWindow in design mode otherwise errors are thrown
//as the ProcessLogViewModel has parameters which only resolve at runtime. I.E. events
if (!(bool)DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue)
{
events.GetEvent<LogUpdate>().Subscribe(UpdateProgressLog);
LogMessage = new ObservableCollection<LogPayload>();
}
}
public void HyperLinkClicked(object sender, RequestNavigateEventArgs e)
{
System.Diagnostics.Process.Start(e.Uri.AbsoluteUri);
}
private void UpdateProgressLog(LogPayload msg)
{
LogMessage.Add(msg);
}
}
EventsBase.cs
public class EventsBase: BindableBase
{
public static IServiceLocator svc = ServiceLocator.Current;
public static IEventAggregator events = svc.GetInstance<IEventAggregator>();
}
LogEvents.cs
public class LogUpdate: PubSubEvent {}
public class LogEvents : EventsBase
{
public static void UpdateProcessLogUI(LogPayload msg)
{
events.GetEvent<LogUpdate>().Publish(msg);
}
}
LogEvent struct
public struct LogPayload
{
public string Message { get; set; }
public int LinkNum { get; set; }
public string Link { get; set; }
public string SecondLink { get; set; }
}
Dann, wenn ich per Drag & Drop eine Tabelle auf den ProcessInputView der folgende Code in meinem ProcessInputViewModel.cs getroffen wird
public void FileDropped(object sender, DragEventArgs e)
{
string[] files;
string[] cols;
TextBox txtFileName = (TextBox)sender;
SpreadsheetCheck result = new SpreadsheetCheck();
DDQEnums.TranTypes tranType;
List<string> fileFormats = new List<string>();
fileFormats.Add(Constants.FileFormats.XLS);
fileFormats.Add(Constants.FileFormats.XLSX);
if (e.Data.GetDataPresent(DataFormats.FileDrop, true))
{
files = e.Data.GetData(DataFormats.FileDrop, true) as string[];
if (files.GetLength(0) > 1)
{
result.IsValid = false;
result.Message = "Only drop one file per input box";
}
else
{
result = Utils.CheckIfSpreadsheetIsValidForInput(files[0], fileFormats, (DDQEnums.TranTypes)txtFileName.Tag, out tranType);
LogEvents.UpdateProcessLogUI(Utils.BuildLogPayload(string.Format("Checking {0} Spreadsheet Column Format", tranType)));
if (result.IsValid)
{
cols = Utils.GetSpreadsheetColumns(tranType);
if (cols.GetLength(0) > 0)
{
result = CheckSpreadsheetColumnFormat(files[0], cols, tranType);
txtFileName.Text = Path.GetFileName(files[0]);
}
else
{
result.IsValid = false;
result.Message = "Unable to get column definations to be used";
}
}
}
IsInputValid = result.IsValid;
LogEvents.UpdateProcessLogUI(Utils.BuildLogPayload(result.Message));
ProcessInputViewEventsPublish.SendInputValidStatus(IsInputValid, SelectedProcess, files[0]);
}
else
{
LogEvents.UpdateProcessLogUI(Utils.BuildLogPayload("Unable to get the file path for the dropped file"));
}
}
Dies funktioniert alles gut, außer die ProcessList-Listview wird nicht aktualisiert, bis die FileDropped-Methode abgeschlossen ist. Dies wird durch das Hinzufügen einer thread.sleep in die FileDropped-Methode unmittelbar nach der LogEvents.UpdateProcessLogUI-Methode deutlicher.
Habe ich das falsch implementiert und wenn ja, wie bekomme ich Echtzeit-Updates in der ProcessLogView-Listenansicht während der Verwendung von IEventAggregator?