diff --git a/XLAB2/App.xaml.cs b/XLAB2/App.xaml.cs index 4da3e08..1d78001 100644 --- a/XLAB2/App.xaml.cs +++ b/XLAB2/App.xaml.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Markup; +using System.Windows.Threading; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using XLAB2.Infrastructure; @@ -17,6 +18,7 @@ namespace XLAB2 public App() { ApplyRussianCulture(); + RegisterGlobalExceptionHandlers(); } protected override async void OnStartup(StartupEventArgs e) @@ -40,19 +42,29 @@ namespace XLAB2 protected override async void OnExit(ExitEventArgs e) { - if (_host != null) + try { - try + if (_host != null) { - await _host.StopAsync(TimeSpan.FromSeconds(5)).ConfigureAwait(true); - } - finally - { - _host.Dispose(); + try + { + await _host.StopAsync(TimeSpan.FromSeconds(5)).ConfigureAwait(true); + } + finally + { + _host.Dispose(); + } } } - - base.OnExit(e); + catch (Exception ex) + { + ShowUnhandledException(ex, true); + } + finally + { + UnregisterGlobalExceptionHandlers(); + base.OnExit(e); + } } private static void ApplyRussianCulture() @@ -68,5 +80,77 @@ namespace XLAB2 typeof(FrameworkElement), new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(culture.IetfLanguageTag))); } + + private void RegisterGlobalExceptionHandlers() + { + DispatcherUnhandledException += OnDispatcherUnhandledException; + AppDomain.CurrentDomain.UnhandledException += OnCurrentDomainUnhandledException; + TaskScheduler.UnobservedTaskException += OnUnobservedTaskException; + } + + private void UnregisterGlobalExceptionHandlers() + { + DispatcherUnhandledException -= OnDispatcherUnhandledException; + AppDomain.CurrentDomain.UnhandledException -= OnCurrentDomainUnhandledException; + TaskScheduler.UnobservedTaskException -= OnUnobservedTaskException; + } + + private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) + { + ShowUnhandledException(e.Exception, false); + e.Handled = true; + } + + private void OnCurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e) + { + var exception = e.ExceptionObject as Exception; + if (exception != null) + { + ShowUnhandledException(exception, e.IsTerminating); + return; + } + + MessageBox.Show( + e.ExceptionObject == null ? "Произошла необработанная ошибка." : e.ExceptionObject.ToString(), + e.IsTerminating ? "XLAB2 - критическая ошибка" : "XLAB2", + MessageBoxButton.OK, + MessageBoxImage.Error); + } + + private void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) + { + ShowUnhandledException(e.Exception, false); + e.SetObserved(); + } + + private static void ShowUnhandledException(Exception exception, bool isCritical) + { + var actualException = UnwrapException(exception); + var message = string.IsNullOrWhiteSpace(actualException.Message) + ? actualException.ToString() + : actualException.Message; + + MessageBox.Show( + message, + isCritical ? "XLAB2 - критическая ошибка" : "XLAB2", + MessageBoxButton.OK, + MessageBoxImage.Error); + } + + private static Exception UnwrapException(Exception exception) + { + if (exception is AggregateException aggregateException) + { + var flattened = aggregateException.Flatten(); + if (flattened.InnerExceptions.Count == 1) + { + return UnwrapException(flattened.InnerExceptions[0]); + } + + return flattened; + } + + return exception; + } } } diff --git a/XLAB2/EkzDirectoryWindow.xaml b/XLAB2/EkzDirectoryWindow.xaml index 61c7235..6f696f8 100644 --- a/XLAB2/EkzDirectoryWindow.xaml +++ b/XLAB2/EkzDirectoryWindow.xaml @@ -65,11 +65,42 @@ + Command="{Binding AddEkzCommand}"> + + + + + + + + + + + Command="{Binding EditEkzCommand}"> + + + + + + + + + + Command="{Binding DeleteEkzCommand}"> + + + + + + + + + + + + diff --git a/XLAB2/FrpdDirectoryWindow.xaml b/XLAB2/FrpdDirectoryWindow.xaml index 61876c1..de870b1 100644 --- a/XLAB2/FrpdDirectoryWindow.xaml +++ b/XLAB2/FrpdDirectoryWindow.xaml @@ -44,11 +44,42 @@ + Command="{Binding AddFrpdCommand}"> + + + + + + + + + + + Command="{Binding EditFrpdCommand}"> + + + + + + + + + + Command="{Binding DeleteFrpdCommand}"> + + + + + + + + + + + + @@ -76,11 +107,42 @@ + Command="{Binding AddFrpdvdCommand}"> + + + + + + + + + + + Command="{Binding EditFrpdvdCommand}"> + + + + + + + + + + Command="{Binding DeleteFrpdvdCommand}"> + + + + + + + + + + + + diff --git a/XLAB2/MainWindow.xaml b/XLAB2/MainWindow.xaml index d3e3a31..c9e11bd 100644 --- a/XLAB2/MainWindow.xaml +++ b/XLAB2/MainWindow.xaml @@ -97,12 +97,44 @@ + Command="{Binding AddDocumentCommand}"> + + + + + + + + + + + Command="{Binding PrintDocumentCommand}"> + + + + + + + + + + + Command="{Binding DeleteDocumentCommand}"> + + + + + + + + + + + + @@ -339,12 +371,49 @@ + Command="{Binding OpenInstrumentPickerCommand}"> + + + + + + + + + + + + + + Command="{Binding OpenInstrumentTypePickerCommand}"> + + + + + + + + + + + + + Command="{Binding DeleteSelectedGroupsCommand}"> + + + + + + + + + + + + @@ -440,17 +509,64 @@ + Command="{Binding CloneLineVerificationCommand}"> + + + + + + + + + + Command="{Binding PrintVerificationDocumentCommand}"> + + + + + + + + + + + + + Command="{Binding MarkLinePassedCommand}"> + + + + + + + + + + Command="{Binding MarkLineRejectedCommand}"> + + + + + + + + + + Command="{Binding ResetLineVerificationCommand}"> + + + + + + + + diff --git a/XLAB2/PrsnDirectoryWindow.xaml b/XLAB2/PrsnDirectoryWindow.xaml index 3e2ba41..f4f90cd 100644 --- a/XLAB2/PrsnDirectoryWindow.xaml +++ b/XLAB2/PrsnDirectoryWindow.xaml @@ -45,11 +45,42 @@ + Command="{Binding AddPrsnCommand}"> + + + + + + + + + + + Command="{Binding EditPrsnCommand}"> + + + + + + + + + + Command="{Binding DeletePrsnCommand}"> + + + + + + + + + + + + @@ -82,11 +113,42 @@ + Command="{Binding AddPrfrCommand}"> + + + + + + + + + + + Command="{Binding EditPrfrCommand}"> + + + + + + + + + + Command="{Binding DeletePrfrCommand}"> + + + + + + + + + + + + @@ -127,11 +189,42 @@ + Command="{Binding AddPrfrvdCommand}"> + + + + + + + + + + + Command="{Binding EditPrfrvdCommand}"> + + + + + + + + + + Command="{Binding DeletePrfrvdCommand}"> + + + + + + + + + + + + @@ -158,11 +251,42 @@ + Command="{Binding AddPrdspvCommand}"> + + + + + + + + + + + Command="{Binding EditPrdspvCommand}"> + + + + + + + + + + Command="{Binding DeletePrdspvCommand}"> + + + + + + + + + + + + diff --git a/XLAB2/PsvDataService.cs b/XLAB2/PsvDataService.cs index 1ed744a..ea1dc3c 100644 --- a/XLAB2/PsvDataService.cs +++ b/XLAB2/PsvDataService.cs @@ -88,7 +88,7 @@ ORDER BY fr.NMFRPD;"; return customers; } - public Task> LoadCustomersAsync(CancellationToken cancellationToken = default) + public async Task> LoadCustomersAsync(CancellationToken cancellationToken = default) { const string sql = @" SELECT @@ -100,7 +100,7 @@ WHERE z.IDFRPDV IS NOT NULL GROUP BY z.IDFRPDV, fr.NMFRPD ORDER BY fr.NMFRPD;"; - return SqlServerConnectionFactory.Current.QueryOpenConnectionAsync( + return await SqlServerConnectionFactory.Current.QueryOpenConnectionAsync( sql, delegate(SqlDataReader reader) { @@ -111,10 +111,7 @@ ORDER BY fr.NMFRPD;"; }; }, ConfigureCommandTimeout, - cancellationToken).ContinueWith>(delegate(Task> task) - { - return task.Result; - }, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + cancellationToken).ConfigureAwait(false); } public IReadOnlyList LoadVerifiers() @@ -151,7 +148,7 @@ ORDER BY p.PRFIO;"; return verifiers; } - public Task> LoadVerifiersAsync(CancellationToken cancellationToken = default) + public async Task> LoadVerifiersAsync(CancellationToken cancellationToken = default) { const string sql = @" SELECT @@ -161,7 +158,7 @@ FROM dbo.PRSN p WHERE NULLIF(LTRIM(RTRIM(p.PRFIO)), N'') IS NOT NULL ORDER BY p.PRFIO;"; - return SqlServerConnectionFactory.Current.QueryOpenConnectionAsync( + return await SqlServerConnectionFactory.Current.QueryOpenConnectionAsync( sql, delegate(SqlDataReader reader) { @@ -172,10 +169,7 @@ ORDER BY p.PRFIO;"; }; }, ConfigureCommandTimeout, - cancellationToken).ContinueWith>(delegate(Task> task) - { - return task.Result; - }, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + cancellationToken).ConfigureAwait(false); } public IReadOnlyList LoadVerificationDocumentForms(bool isPassed) @@ -220,7 +214,7 @@ ORDER BY fr.NMFRD, v.IDVDODVDD;"; return forms; } - public Task> LoadVerificationDocumentFormsAsync(bool isPassed, CancellationToken cancellationToken = default) + public async Task> LoadVerificationDocumentFormsAsync(bool isPassed, CancellationToken cancellationToken = default) { const string sql = @" SELECT DISTINCT @@ -235,7 +229,7 @@ WHERE v.IDSPVDOD = 2 AND fr.IDSPVDD = @DocumentTypeId ORDER BY fr.NMFRD, v.IDVDODVDD;"; - return SqlServerConnectionFactory.Current.QueryOpenConnectionAsync( + return await SqlServerConnectionFactory.Current.QueryOpenConnectionAsync( sql, delegate(SqlDataReader reader) { @@ -252,10 +246,7 @@ ORDER BY fr.NMFRD, v.IDVDODVDD;"; ConfigureCommandTimeout(command); command.Parameters.Add("@DocumentTypeId", SqlDbType.Int).Value = isPassed ? 6 : 2; }, - cancellationToken).ContinueWith>(delegate(Task> task) - { - return task.Result; - }, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + cancellationToken).ConfigureAwait(false); } public IReadOnlyList LoadSpoiItems() @@ -293,7 +284,7 @@ ORDER BY s.NMOI, s.KDOI, s.IDSPOI;"; return items; } - public Task> LoadSpoiItemsAsync(CancellationToken cancellationToken = default) + public async Task> LoadSpoiItemsAsync(CancellationToken cancellationToken = default) { const string sql = @" SELECT @@ -303,7 +294,7 @@ SELECT FROM dbo.SPOI s ORDER BY s.NMOI, s.KDOI, s.IDSPOI;"; - return SqlServerConnectionFactory.Current.QueryOpenConnectionAsync( + return await SqlServerConnectionFactory.Current.QueryOpenConnectionAsync( sql, delegate(SqlDataReader reader) { @@ -315,10 +306,7 @@ ORDER BY s.NMOI, s.KDOI, s.IDSPOI;"; }; }, ConfigureCommandTimeout, - cancellationToken).ContinueWith>(delegate(Task> task) - { - return task.Result; - }, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + cancellationToken).ConfigureAwait(false); } public int AddSpoiItem(SpoiDirectoryItem item) @@ -499,7 +487,7 @@ ORDER BY s.NMTP, s.IDSPNMTP;"; return items; } - public Task> LoadSpnmtpItemsAsync(CancellationToken cancellationToken = default) + public async Task> LoadSpnmtpItemsAsync(CancellationToken cancellationToken = default) { const string sql = @" SELECT @@ -509,7 +497,7 @@ SELECT FROM dbo.SPNMTP s ORDER BY s.NMTP, s.IDSPNMTP;"; - return SqlServerConnectionFactory.Current.QueryOpenConnectionAsync( + return await SqlServerConnectionFactory.Current.QueryOpenConnectionAsync( sql, delegate(SqlDataReader reader) { @@ -521,10 +509,7 @@ ORDER BY s.NMTP, s.IDSPNMTP;"; }; }, ConfigureCommandTimeout, - cancellationToken).ContinueWith>(delegate(Task> task) - { - return task.Result; - }, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + cancellationToken).ConfigureAwait(false); } public int AddSpnmtpItem(SpnmtpDirectoryItem item) @@ -1267,7 +1252,7 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, z.NNZV;"; return instruments; } - public Task> LoadCustomerInstrumentsAsync(int customerId, CancellationToken cancellationToken = default) + public async Task> LoadCustomerInstrumentsAsync(int customerId, CancellationToken cancellationToken = default) { const string sql = @" SELECT @@ -1343,7 +1328,7 @@ OUTER APPLY WHERE z.IDFRPDV = @CustomerId ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, z.NNZV;"; - return SqlServerConnectionFactory.Current.QueryOpenConnectionAsync( + return await SqlServerConnectionFactory.Current.QueryOpenConnectionAsync( sql, delegate(SqlDataReader reader) { @@ -1371,10 +1356,7 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, z.NNZV;"; ConfigureCommandTimeout(command); command.Parameters.Add("@CustomerId", SqlDbType.Int).Value = customerId; }, - cancellationToken).ContinueWith>(delegate(Task> task) - { - return task.Result; - }, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + cancellationToken).ConfigureAwait(false); } public IReadOnlyList LoadInstrumentTypes() @@ -1468,7 +1450,7 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, sizeInfo.NNGSRS;"; return instrumentTypes; } - public Task> LoadInstrumentTypesAsync(CancellationToken cancellationToken = default) + public async Task> LoadInstrumentTypesAsync(CancellationToken cancellationToken = default) { const string sql = @" SELECT @@ -1522,7 +1504,7 @@ OUTER APPLY ) periodByType ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, sizeInfo.NNGSRS;"; - return SqlServerConnectionFactory.Current.QueryOpenConnectionAsync( + return await SqlServerConnectionFactory.Current.QueryOpenConnectionAsync( sql, delegate(SqlDataReader reader) { @@ -1546,10 +1528,7 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, sizeInfo.NNGSRS;"; }; }, ConfigureCommandTimeout, - cancellationToken).ContinueWith>(delegate(Task> task) - { - return task.Result; - }, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + cancellationToken).ConfigureAwait(false); } public IReadOnlyList FindOpenDocumentConflicts(int customerId, string currentDocumentNumber, IEnumerable candidateLines) diff --git a/XLAB2/TpvdklWindow.xaml b/XLAB2/TpvdklWindow.xaml index c44c65e..06a0e55 100644 --- a/XLAB2/TpvdklWindow.xaml +++ b/XLAB2/TpvdklWindow.xaml @@ -30,11 +30,42 @@ + Command="{Binding AddCommand}"> + + + + + + + + + + + Command="{Binding EditCommand}"> + + + + + + + + + + Command="{Binding DeleteCommand}"> + + + + + + + + + + + + diff --git a/XLAB2/TypeSizeDirectoryWindow.xaml b/XLAB2/TypeSizeDirectoryWindow.xaml index 9f03b1b..2ce2c06 100644 --- a/XLAB2/TypeSizeDirectoryWindow.xaml +++ b/XLAB2/TypeSizeDirectoryWindow.xaml @@ -57,14 +57,54 @@ + Command="{Binding AddTipsCommand}"> + + + + + + + + + + + Command="{Binding EditTipsCommand}"> + + + + + + + + + + Command="{Binding DeleteTipsCommand}"> + + + + + + + + + + + + + Command="{Binding ManageTpvdklCommand}"> + + + + + + + + + @@ -92,9 +132,40 @@ HeadersVisibility="Column"> - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -129,9 +200,40 @@ HeadersVisibility="Column"> - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -158,9 +260,40 @@ HeadersVisibility="Column"> - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +