public PacketDocumentViewModel(PacketDocumentSolutionItem solutionItem, IMainThread mainThread, MostRecentlySearchedService mostRecentlySearchedService, IDatabaseProvider databaseProvider, Func <INativeTextDocument> nativeTextDocumentCreator, IMessageBoxService messageBoxService, IPacketFilteringService filteringService, IDocumentManager documentManager, ITextDocumentService textDocumentService, IWindowManager windowManager, IPacketViewerSettings packetSettings, IEnumerable <IPacketDumperProvider> dumperProviders, ISniffLoader sniffLoader) { this.solutionItem = solutionItem; this.mainThread = mainThread; this.messageBoxService = messageBoxService; this.filteringService = filteringService; this.sniffLoader = sniffLoader; packetViewModelCreator = new PacketViewModelFactory(databaseProvider); MostRecentlySearched = mostRecentlySearchedService.MostRecentlySearched; Title = "Sniff " + Path.GetFileNameWithoutExtension(solutionItem.File); SolutionItem = solutionItem; FilterText = nativeTextDocumentCreator(); SelectedPacketPreview = nativeTextDocumentCreator(); Watch(this, t => t.FilteringProgress, nameof(ProgressUnknown)); filteredPackets = AllPackets; ApplyFilterCommand = new AsyncCommand(async() => { if (inApplyFilterCommand) { return; } inApplyFilterCommand = true; MostRecentlySearchedItem = FilterText.ToString(); inApplyFilterCommand = false; if (!string.IsNullOrEmpty(FilterText.ToString())) { mostRecentlySearchedService.Add(FilterText.ToString()); } if (currentActionToken != filteringToken) { throw new Exception("Invalid concurrent access!"); } filteringToken?.Cancel(); filteringToken = null; currentActionToken = null; await FilterPackets(FilterText.ToString()); }); SaveToFileCommand = new AsyncCommand(async() => { var path = await windowManager.ShowSaveFileDialog("Text file|txt"); if (path == null) { return; } LoadingInProgress = true; FilteringInProgress = true; try { await using StreamWriter writer = File.CreateText(path); int i = 0; int count = FilteredPackets.Count; foreach (var packet in FilteredPackets) { await writer.WriteLineAsync(packet.Text); if ((i % 100) == 0) { Report(i * 1.0f / count); } } } catch (Exception e) { Console.WriteLine(e); await this.messageBoxService.ShowDialog(new MessageBoxFactory <bool>() .SetTitle("Fatal error") .SetMainInstruction("Fatal error during saving file") .SetContent(e.Message) .WithOkButton(false) .Build()); } LoadingInProgress = false; FilteringInProgress = false; }); OpenHelpCommand = new DelegateCommand(() => windowManager.OpenUrl("https://github.com/BAndysc/WoWDatabaseEditor/wiki/Packet-Viewer")); On(() => SelectedPacket, doc => { if (doc != null) { SelectedPacketPreview.FromString(doc.Text); } }); On(() => SplitUpdate, _ => { SplitPacketsIfNeededAsync().Wait(); ((ICommand)ApplyFilterCommand).Execute(null); }); foreach (var dumper in dumperProviders) { Processors.Add(new(dumper)); } RunProcessors = new AsyncAutoCommand(async() => { var processors = Processors.Where(s => s.IsChecked).ToList(); if (processors.Count == 0) { return; } LoadingInProgress = true; FilteringInProgress = true; try { var tokenSource = new CancellationTokenSource(); AssertNoOnGoingTask(); currentActionToken = tokenSource; var output = await RunProcessorsThreaded(processors, tokenSource.Token).ConfigureAwait(true); if (!tokenSource.IsCancellationRequested) { var extension = processors.Select(p => p.Extension).Distinct().Count() == 1 ? processors[0].Extension : "txt"; documentManager.OpenDocument(textDocumentService.CreateDocument($"{processors[0].Name} ({Title})", output, extension)); } } catch (Exception e) { Console.WriteLine(e); await messageBoxService.ShowDialog(new MessageBoxFactory <bool>() .SetIcon(MessageBoxIcon.Error) .SetTitle("Fatal error") .SetMainInstruction("Fatal error during processing") .SetContent( "Sorry, fatal error occured, this is probably a bug in processors, please report it in github\n\n" + e) .WithOkButton(true) .Build()); } LoadingInProgress = false; FilteringInProgress = false; currentActionToken = null; }, _ => Processors.Any(c => c.IsChecked)); foreach (var proc in Processors) { AutoDispose(proc.ToObservable(p => p.IsChecked) .SubscribeAction(_ => RunProcessors.RaiseCanExecuteChanged())); } wrapLines = packetSettings.Settings.WrapLines; splitUpdate = packetSettings.Settings.AlwaysSplitUpdates; if (!string.IsNullOrEmpty(packetSettings.Settings.DefaultFilter)) { FilterText.FromString(packetSettings.Settings.DefaultFilter); } LoadSniff().ListenErrors(); AutoDispose(new ActionDisposable(() => currentActionToken?.Cancel())); }