private async Task LoadImage(string filename, Rect?startRect) { _image = await BetterImage.LoadBitmapSourceAsync(filename); OriginalImage.SetCurrentValue(Image.SourceProperty, _image.ImageSource); CommandManager.InvalidateRequerySuggested(); if (_image.IsBroken) { NonfatalError.Notify(AppStrings.Dialogs_ImageEditor_CantLoadImage); CloseWithResult(MessageBoxResult.Cancel); } else { _model.TotalWidth = MainGrid.Width = _image.Width; _model.TotalHeight = MainGrid.Height = _image.Height; _model.CalculateMinAndMax(); if (startRect.HasValue) { var rect = startRect.Value; _model.OffsetX = rect.X; _model.OffsetY = rect.Y; _model.ImageWidth = rect.Width; _model.ImageHeight = rect.Height; } else { _model.CenterAndFit(); } UpdateView(); } }
public static Task <IReadOnlyList <UpdatePreviewError> > Run(this IEnumerable <ToUpdatePreview> toUpdate, UpdatePreviewMode?mode = null, string presetFilename = null) { var list = toUpdate.ToIReadOnlyListIfItIsNot(); var actualMode = mode ?? GetAutoUpdatePreviewsDialogMode(); if (SettingsHolder.CustomShowroom.CustomShowroomPreviews) { switch (actualMode) { case UpdatePreviewMode.Options: return(CmPreviewsWrapper.Run(list, presetFilename)); case UpdatePreviewMode.StartManual: NonfatalError.Notify("Can’t update previews in Manual Mode with Custom Showroom", "Why would you need it anyway? Just open Custom Previews settings, locate camera the way you want and update one or all previews."); return(Task.FromResult <IReadOnlyList <UpdatePreviewError> >(null)); default: return(CmPreviewsTools.UpdatePreview(list, presetFilename)); } } var dialog = new CarUpdatePreviewsDialog(list, actualMode); return(Task.FromResult <IReadOnlyList <UpdatePreviewError> >(dialog.ShowDialog() ? dialog.Errors.ToList() : null)); }
private void CreateNewIcon() { if (Car == null) { return; } ValuesStorage.Set(_key, NewIconLabel.Text); // TODO: Save style? var size = new Size(CommonAcConsts.UpgradeIconWidth, CommonAcConsts.UpgradeIconHeight); NewIcon.Measure(size); NewIcon.Arrange(new Rect(size)); NewIcon.ApplyTemplate(); NewIcon.UpdateLayout(); var bmp = new RenderTargetBitmap(CommonAcConsts.UpgradeIconWidth, CommonAcConsts.UpgradeIconHeight, 96, 96, PixelFormats.Pbgra32); bmp.Render(NewIcon); try { bmp.SaveTo(Car.UpgradeIcon); BetterImage.Refresh(Car.UpgradeIcon); } catch (IOException ex) { NonfatalError.Notify(AppStrings.UpgradeIcon_CannotChange, AppStrings.UpgradeIcon_CannotChange_Commentary, ex); } catch (Exception ex) { NonfatalError.Notify(AppStrings.UpgradeIcon_CannotChange, ex); } }
private void ApplyFile(string filename) { var cropped = ImageEditor.Proceed(filename, new Size(128, 128)); if (cropped == null) { return; } try { cropped.SaveAsPng(Car.BrandBadge); } catch (IOException ex) { NonfatalError.Notify(AppStrings.BrandBadge_CannotChange, AppStrings.BrandBadge_CannotChange_Commentary, ex); return; } catch (Exception ex) { NonfatalError.Notify(AppStrings.BrandBadge_CannotChange, ex); return; } var saveAs = Prompt.Show(AppStrings.BrandBadge_AddAs, AppStrings.Common_AddToLibrary, Path.GetFileNameWithoutExtension(filename)); if (saveAs == null) { return; } try { FilesStorage.Instance.AddUserContentToDirectory(ContentCategory.BrandBadges, Car.BrandBadge, saveAs); } catch (Exception e) { NonfatalError.Notify(AppStrings.Common_CannotAddToLibrary, e); } Close(); }
private void RecalculateAndScaleCurves(object sender, RoutedEventArgs e) { double maxPower, maxTorque; if (!FlexibleParser.TryParseDouble(PowerInput.Text, out maxPower) || !FlexibleParser.TryParseDouble(TorqueInput.Text, out maxTorque)) { NonfatalError.Notify(ToolsStrings.Common_CannotDo_Title, AppStrings.CarSpecs_SpecifyPowerAndTorqueFirst); return; } var data = Car.AcdData; if (data == null) { NonfatalError.Notify(ToolsStrings.Common_CannotDo_Title, "Data is damaged"); return; } Lut torque; try { torque = TorquePhysicUtils.LoadCarTorque(data); } catch (Exception ex) { NonfatalError.Notify(ToolsStrings.Common_CannotDo_Title, ex); return; } torque.ScaleToSelf(maxTorque); TorqueGraph = new GraphData(torque); torque.TransformSelf(x => x.X * x.Y); torque.ScaleToSelf(maxPower); PowerGraph = new GraphData(torque); }
private async void ProcessArguments() { if (OptionLiteModeSupported) { Visibility = Visibility.Hidden; } var cancelled = true; foreach (var arg in AppArguments.Values) { Logging.Write("Input: " + arg); var result = await _argumentsHandler.ProcessArgument(arg); if (result == ArgumentHandleResult.FailedShow) { NonfatalError.Notify(AppStrings.Main_CannotProcessArgument, AppStrings.Main_CannotProcessArgument_Commentary); } if (result == ArgumentHandleResult.SuccessfulShow || result == ArgumentHandleResult.FailedShow) { Visibility = Visibility.Visible; cancelled = false; } } if (OptionLiteModeSupported && cancelled) { Close(); } }
private void UpdateMetaInformation() { var information = Information; try { var list = new List <string>(); information?.Export(list); if (File.Exists(Filename)) { using (var sr = new StreamReader(Filename, Encoding.UTF8)) { var skip = true; string line; while ((line = sr.ReadLine()) != null) { if (skip && !_metaInformation.IsMatch(line)) { skip = false; } if (!skip) { list.Add(line); } } } } File.WriteAllText(Filename, list.JoinToString('\n')); _lastDateTime = new FileInfo(Filename).LastWriteTime; } catch (Exception e) { NonfatalError.Notify("Can’t save list information", e); } }
protected override void OnClick() { if (Command is IAsyncCommand asyncCommand) { _busy.Task(async() => { try { SetValue(IsProcessingPropertyKey, true); using (_cancellation = new CancellationTokenSource()) using (_taskbar = TaskbarService.Create(1200)) { await _commandInvoke.Invoke(asyncCommand, this, _cancellation.Token, CommandParameter); if (_commandPercentageProgress) { // Report(new AsyncProgressEntry(Progress.Message, 1d)); Report(AsyncProgressEntry.Finished); } } } catch (Exception e) when(e.IsCanceled()) { } catch (Exception e) { NonfatalError.Notify("Unhandled error", e); } finally { _cancellation = null; SetValue(IsProcessingPropertyKey, false); } }).Forget(); } else { base.OnClick(); } }
public Task EnableMod(GenericMod mod) { return(_busy.Task(async() => { if (_enabler == null || mod.IsEnabled) { return; } var conflicts = await _enabler.CheckConflictsAsync(mod); if (conflicts.Length > 0 && ModernDialog.ShowMessage( conflicts.Select(x => $@"• “{Path.GetFileName(x.RelativeName)}” has already been altered by the “{x.ModName}” mod;") .JoinToString("\n").ToSentence () + $"\n\nEnabling {BbCodeBlock.Encode(mod.DisplayName)} may have adverse effects. Are you sure you want to enable this mod?", "Conflict", MessageBoxButton.YesNo, "genericMods.conflict") != MessageBoxResult.Yes) { return; } try { using (var waiting = WaitingDialog.Create("Enabling mod…")) { await _enabler.EnableAsync(mod, waiting, waiting.CancellationToken); Changed?.Invoke(this, EventArgs.Empty); if (waiting.CancellationToken.IsCancellationRequested) { waiting.Report(AsyncProgressEntry.FromStringIndetermitate("Cancellation…")); await _enabler.DisableAsync(mod); } } } catch (Exception e) { NonfatalError.Notify("Can’t enable mod", e); } })); }
public static async Task <SharedEntry> GetSharedAsync(string id, CancellationToken cancellation = default(CancellationToken)) { InternalUtils.SharedEntryLoaded loaded; try { loaded = await InternalUtils.GetSharedEntryAsync(id, CmApiProvider.UserAgent, cancellation); if (loaded == null || cancellation.IsCancellationRequested) { return(null); } } catch (Exception e) { NonfatalError.Notify(ToolsStrings.SharingHelper_CannotGetShared, ToolsStrings.Common_CannotDownloadFile_Commentary, e); return(null); } SharedEntryType entryType; try { entryType = (SharedEntryType)Enum.Parse(typeof(SharedEntryType), loaded.EntryType); } catch (Exception) { NonfatalError.Notify(string.Format(ToolsStrings.SharingHelper_NotSupported, loaded.EntryType), ToolsStrings.SharingHelper_NotSupported_Commentary); Logging.Warning("Unsupported entry type: " + loaded.EntryType); return(null); } return(new SharedEntry { Id = id, EntryType = entryType, Name = loaded.Name, Target = loaded.Target, Author = loaded.Author, Data = loaded.Data }); }
public async Task <IReadOnlyList <UpdatePreviewError> > Run() { try { if (_options.Showroom != null && ShowroomsManager.Instance.GetById(_options.Showroom) == null) { if (_options.Showroom == @"at_previews" && MissingShowroomHelper != null) { await MissingShowroomHelper.OfferToInstall("Kunos Previews Showroom (AT Previews Special)", "at_previews", "http://www.assettocorsa.net/assetto-corsa-v1-5-dev-diary-part-33/"); if (ShowroomsManager.Instance.GetById(_options.Showroom) != null) { return(await RunReady()); } } throw new InformativeException("Can’t update preview", $"Showroom “{_options.Showroom}” is missing"); } return(await RunReady()); } catch (Exception e) { NonfatalError.Notify("Can’t update preview", e); return(null); } finally { _waiting?.Dispose(); _dispatcherTimer?.Stop(); if (_localUpdater) { _updater.Dispose(); } } }
public static bool LoadPreset(string key, string filename, string serialized, bool changed) { ValuesStorage.Set("__userpresets_p_" + key, filename); ValuesStorage.Set("__userpresets_c_" + key, changed); var r = false; foreach (var c in GetInstance(key)) { c.UpdateSavedPresets(); var entry = c.SavedPresets.FirstOrDefault(x => FileUtils.ArePathsEqual(x.VirtualFilename, filename)); c.CurrentUserPreset = entry; try { c.UserPresetable?.ImportFromPresetData(serialized); } catch (Exception e) { NonfatalError.Notify("Can’t load preset", e); } c.SetChanged(changed); r = true; } return(r); }
public override async Task <bool> PrepareAsync(WebClient client, CancellationToken cancellation) { var downloadPage = await client.DownloadStringTaskAsync(Url); if (cancellation.IsCancellationRequested) { return(false); } var match = Regex.Match(downloadPage, @"<p class=""download""><a href=""([^""]+)"); if (!match.Success) { NonfatalError.Notify(ToolsStrings.Common_CannotDownloadFile, ToolsStrings.DirectLoader_AcClubChanged); return(false); } Url = HttpUtility.HtmlDecode(match.Groups[1].Value); Logging.Write("AssettoCorsa.club download link: " + Url); _innerLoader = FlexibleLoader.CreateLoader(Url); if (_innerLoader is AcClubLoader) { throw new Exception(ToolsStrings.DirectLoader_RecursionDetected); } return(await _innerLoader.PrepareAsync(client, cancellation)); }
private static void LaunchReport(string filename) { try { using (var stream = File.OpenRead(Path.Combine(Path.GetDirectoryName(filename) ?? "", Path.GetFileNameWithoutExtension(filename) + ".zip"))) using (var archive = new ZipArchive(stream)) { // Apply video config var currentVideo = new IniFile(AcPaths.GetCfgVideoFilename()); var currentWidth = currentVideo["VIDEO"].GetInt("WIDTH", 1920); var currentHeight = currentVideo["VIDEO"].GetInt("HEIGHT", 1080); var newVideo = IniFile.Parse((archive.GetEntry("video.ini") ?? throw new Exception("Video config is missing")).Open().ReadAsStringAndDispose()); newVideo["VIDEO"].Set("WIDTH", Math.Min(currentWidth, newVideo["VIDEO"].GetInt("WIDTH", 1920))); newVideo["VIDEO"].Set("HEIGHT", Math.Min(currentHeight, newVideo["VIDEO"].GetInt("HEIGHT", 1080))); File.WriteAllText(AcPaths.GetCfgVideoFilename(), newVideo.ToString()); // Apply CSP config PatchSettingsModel.Create().ImportFromPresetData(archive.GetEntry("csp.ini")?.Open().ReadAsStringAndDispose() ?? string.Empty); // Apply race config File.WriteAllText(AcPaths.GetRaceIniFilename(), (archive.GetEntry("race.ini") ?? throw new Exception("Race config is missing")).Open().ReadAsStringAndDispose()); Process.Start(new ProcessStartInfo { FileName = Path.Combine(AcRootDirectory.Instance.Value ?? "", "acs.exe"), WorkingDirectory = AcRootDirectory.Instance.Value ?? "" }); } } catch (Exception e) { NonfatalError.Notify("Failed to launch report race", e); } }
private static void ShowFallbackMessage(Exception e, string fallbackMessage) { if (fallbackMessage != null) { NonfatalError.Notify(fallbackMessage, e); } }
private void ImportCsv(string filename) { try { if (_wrapper == null) { throw new InformativeException("Can’t import CSV-file", "LUTLibrary.dll missing or can’t be loaded."); } var name = Prompt.Show("Choose a name for the new LUT setting:", "New LUT", Path.GetFileNameWithoutExtension(filename) + ".lut", "?", required: true, maxLength: 120); if (string.IsNullOrWhiteSpace(name)) { return; } if (!name.EndsWith(@".lut", StringComparison.OrdinalIgnoreCase)) { name += @".lut"; } var lutData = new LutDataFile(); var lut = _wrapper.ToLut(filename); if (lut == null) { throw new Exception(@"Expected field or method is missing"); } lutData.Values.AddRange(lut); lutData.Save(Path.Combine(AcPaths.GetDocumentsCfgDirectory(), name)); SwitchFfPostProcessLutName(name); } catch (Exception e) { NonfatalError.Notify("Can’t import CSV-file", e); } }
public bool Revert() { if (AcRootDirectory.Instance.Value == null) { return(false); } try { var destination = GetAbsolutePath(_relativeDestination); var backup = GetAbsolutePath(_relativeBackup); if (File.Exists(backup)) { if (File.Exists(destination)) { File.Delete(destination); } File.Move(backup, destination); return(true); } } catch (Exception e) { NonfatalError.Notify("Can’t restore original files after replacing them with temporary ones", e); } return(false); }
private void ImportLut(string filename) { try { string name; if (!FileUtils.ArePathsEqual(Path.GetDirectoryName(filename), AcPaths.GetDocumentsCfgDirectory())) { name = Prompt.Show("Choose a name for the new LUT setting:", "New LUT", Path.GetFileNameWithoutExtension(filename) + ".lut", "?", required: true, maxLength: 120); if (string.IsNullOrWhiteSpace(name)) { return; } if (!name.EndsWith(".lut", StringComparison.OrdinalIgnoreCase)) { name += ".lut"; } File.Copy(filename, Path.Combine(AcPaths.GetDocumentsCfgDirectory(), name), true); } else { name = Path.GetFileName(filename); } SwitchFfPostProcessLutName(name); } catch (Exception e) { NonfatalError.Notify("Can’t import LUT-file", e); } }
public static async Task UpdateAsync(TrackObjectBase track) { if (!File.Exists(track.MapImage)) { ModernDialog.ShowMessage("Map not found"); return; } try { using (WaitingDialog.Create("Saving…")) { var maps = track.MainTrackObject.MultiLayouts?.Select(x => x.MapImage).ToArray() ?? new[] { track.MapImage }; await Task.Run(() => { using (var renderer = new TrackOutlineRenderer(maps, track.MapImage, track.PreviewImage) { LoadPreview = false }) { renderer.Initialize(); renderer.Width = CommonAcConsts.TrackOutlineWidth; renderer.Height = CommonAcConsts.TrackOutlineHeight; TrackOutlineRendererTools.LoadSettings(track, renderer); using (var holder = FileUtils.RecycleOriginal(track.OutlineImage)) { renderer.Shot(holder.Filename); } } }); } } catch (Exception e) { NonfatalError.Notify("Can’t update outline", e); } }
protected virtual async void Save() { if (IsSaving || IsLoading || Ini == null) { return; } if (!_canSave) { _hasToSave = true; return; } IsSaving = true; await Task.Delay(500); if (!IsSaving) { return; } try { SetToIni(); IgnoreChangesForAWhile(); IsReloading = false; Ini.Save(Filename); } catch (Exception e) { NonfatalError.Notify(ToolsStrings.AcSettings_CannotSave, ToolsStrings.AcSettings_CannotSave_Commentary, e); } finally { IsSaving = false; _hasToSave = false; } }
private static async Task <ArgumentHandleResult> ProcessRaceQuick(CustomUriRequest custom) { var preset = GetSettings(custom.Params, @"preset") ?? throw new Exception(@"Settings are not specified"); var assists = GetSettings(custom.Params, @"assists"); if (assists != null && !UserPresetsControl.LoadSerializedPreset(AssistsViewModel.Instance.PresetableKey, assists)) { AssistsViewModel.Instance.ImportFromPresetData(assists); } if (custom.Params.GetFlag("loadPreset")) { QuickDrive.Show(serializedPreset: preset, forceAssistsLoading: custom.Params.GetFlag("loadAssists")); return(ArgumentHandleResult.SuccessfulShow); } if (!await QuickDrive.RunAsync(serializedPreset: preset, forceAssistsLoading: custom.Params.GetFlag("loadAssists"))) { NonfatalError.Notify(AppStrings.Common_CannotStartRace, AppStrings.Arguments_CannotStartRace_Commentary); return(ArgumentHandleResult.Failed); } return(ArgumentHandleResult.Successful); }
protected bool Apply([NotNull] string source) { if (AcRootDirectory.Instance.Value == null || !File.Exists(source)) { return(false); } var destination = GetAbsolutePath(_relativeDestination); var backup = GetAbsolutePath(_relativeBackup); if (File.Exists(destination)) { if (File.Exists(backup)) { File.Move(backup, FileUtils.EnsureUnique(backup)); } Logging.Debug($"{destination} → {backup}"); File.Move(destination, backup); } try { Logging.Debug($"{source} → {destination}"); FileUtils.HardlinkOrCopy(source, destination); } catch (Exception e) { // this exception should be catched here so original clouds folder still // will be restored even when copying a new one has been failed NonfatalError.Notify("Can’t replace files", e); } return(true); }
private void BrandBadgeEditor_Closing(object sender, CancelEventArgs e) { FilesStorage.Instance.Watcher(ContentCategory.BrandBadges).Update -= BrandBadgeEditor_Update; if (MessageBoxResult != MessageBoxResult.OK) { return; } if (Selected == null) { return; } try { if (File.Exists(Car.BrandBadge)) { FileUtils.Recycle(Car.BrandBadge); } File.Copy(Selected.Filename, Car.BrandBadge); } catch (IOException ex) { NonfatalError.Notify(AppStrings.BrandBadge_CannotChange, AppStrings.BrandBadge_CannotChange_Commentary, ex); } catch (Exception ex) { NonfatalError.Notify(AppStrings.BrandBadge_CannotChange, ex); } }
public async Task <bool> TryToPack(AcCommonObjectPackerParams packerParams) { try { using (var waiting = packerParams.Progress == null ? WaitingDialog.Create("Packing…") : null) { var progress = waiting ?? packerParams.Progress; var cancellation = waiting?.CancellationToken ?? packerParams.Cancellation; await Task.Run(() => { var destination = packerParams.Destination ?? Path.Combine(Location, $"{Id}-{(this as IAcObjectVersionInformation)?.Version ?? "0"}-{DateTime.Now.ToUnixTimestamp()}.zip"); using (var output = File.Create(destination)) { Pack(output, packerParams, new Progress <string>(x => progress?.Report(AsyncProgressEntry.FromStringIndetermitate($"Packing: {x}…"))), cancellation); } if (cancellation.IsCancellationRequested) { return; } if (packerParams.ShowInExplorer) { WindowsHelper.ViewFile(destination); } }); } return(true); } catch (Exception e) { NonfatalError.Notify("Can’t pack", e); return(false); } }
public async Task ToggleAsync(IEnumerable <string> ids, bool?enabled = null) { if (!Directories.Actual) { return; } if (ids == null) { throw new ArgumentNullException(nameof(ids)); } var objs = ids.Select(GetWrapperById).ToList(); if (objs.Contains(null)) { throw new ArgumentException(ToolsStrings.AcObject_IdIsWrong, nameof(ids)); } try { foreach (var wrapper in objs) { if (enabled == wrapper.Value.Enabled) { continue; } await RenameAsync(wrapper.Id, wrapper.Id, enabled ?? !wrapper.Value.Enabled); } } catch (Exception ex) { NonfatalError.Notify(ToolsStrings.AcObject_CannotDelete, ToolsStrings.AcObject_CannotToggle_Commentary, ex); } }
private static void ShowMessage(Exception e) { NonfatalError.Notify("Looks like app can’t load native library", "Visual C++ Redistributable might be missing or damaged. Would you like to install the package prepared specially for CM?", e, new[] { new NonfatalErrorSolution("Download and install", null, DownloadAndInstall, "DownloadIconData"), }); }
public static async Task DownloadCarAsync([NotNull] string id, string version = null) { try { var entry = await CmApiProvider.GetContentAsync <AcContentEntry>($"car/{id}"); if (entry != null) { if (version != null && entry.UiVersion != version && entry.OriginVersion != version) { throw new InformativeException($"Can’t download car “{id}”", $"Not the required version: indexed is {entry.UiVersion ?? entry.OriginVersion}, while required is {version}."); } if (!entry.ImmediateDownload && entry.OriginUrl != null && (Keyboard.Modifiers & ModifierKeys.Control) == 0) { WindowsHelper.ViewInBrowser(entry.OriginUrl); } else { await ContentInstallationManager.Instance.InstallAsync(entry.DownloadUrl, new ContentInstallationParams(false) { DisplayName = entry.UiName, Version = entry.UiVersion, InformationUrl = entry.OriginUrl }); } } else { throw new InformativeException($"Can’t download car “{id}”", "Attempt to find a description failed."); } } catch (Exception e) { NonfatalError.Notify($"Can’t download car “{id}”", e); } }
public static bool OnException([CanBeNull] Exception e, string fallbackMessage) { if (IsVisualCppRelatedException(e)) { var msg = e?.ToString(); if (!PreviousMessages.Contains(msg)) { PreviousMessages.Add(msg); Logging.Error(msg); } if (_shown) { ShowFallbackMessage(e, fallbackMessage); } else { _shown = true; if (IsVisualCppInstalled()) { NonfatalError.Notify("Looks like app can’t load native library, even though it’s installed", "Maybe it’s damaged? Or, possibly, some system libraries are missing.", e); } else { ShowMessage(e); } } return(true); } ShowFallbackMessage(e, fallbackMessage); return(false); }
private async Task <ArgumentHandleResult> ProcessInputFile(string filename) { var isDirectory = FileUtils.IsDirectory(filename); if (!isDirectory && filename.EndsWith(@".acreplay", StringComparison.OrdinalIgnoreCase) || Path.GetDirectoryName(filename)?.Equals(FileUtils.GetReplaysDirectory(), StringComparison.OrdinalIgnoreCase) == true) { await GameWrapper.StartReplayAsync(new Game.StartProperties(new Game.ReplayProperties { Filename = filename })); return(ArgumentHandleResult.Successful); } if (!isDirectory && filename.EndsWith(@".kn5", StringComparison.OrdinalIgnoreCase)) { await CustomShowroomWrapper.StartAsync(filename); return(ArgumentHandleResult.Successful); } try { new InstallAdditionalContentDialog(filename).ShowDialog(); } catch (Exception e) { NonfatalError.Notify(AppStrings.Arguments_CannotInstallAdditionalContent, e); return(ArgumentHandleResult.Failed); } return(ArgumentHandleResult.Successful); }
private async void ShotAsync(Action <IProgress <Tuple <string, double?> >, CancellationToken> action) { if (_busy) { return; } _busy = true; try { using (var waiting = new WaitingDialog { WindowStartupLocation = WindowStartupLocation.CenterScreen, Owner = null }) { waiting.Report(AsyncProgressEntry.Indetermitate); var cancellation = waiting.CancellationToken; Renderer.IsPaused = true; try { await Task.Run(() => { // ReSharper disable once AccessToDisposedClosure action(waiting, cancellation); }); } finally { Renderer.IsPaused = false; } } } catch (Exception e) { NonfatalError.Notify("Can’t build image", e); } finally { _busy = false; UpdateSize(); } }