From 2661336d17d387915ac10fc46bb9964ea179ba8e Mon Sep 17 00:00:00 2001 From: Michael Steenbeek Date: Sun, 18 May 2025 14:03:25 +0200 Subject: [PATCH 1/6] Close #31: Add option to auto-update game --- .../ConfigService.cs | 11 ++ src/openlauncher/MainWindow.axaml | 11 +- src/openlauncher/MainWindow.axaml.cs | 120 +++++++++++------- .../Properties/Resources.Designer.cs | 6 + src/openlauncher/Properties/Resources.nl.resx | 3 + src/openlauncher/Properties/Resources.resx | 3 + 6 files changed, 106 insertions(+), 48 deletions(-) diff --git a/src/IntelOrca.OpenLauncher.Core/ConfigService.cs b/src/IntelOrca.OpenLauncher.Core/ConfigService.cs index 539fff0..d922241 100644 --- a/src/IntelOrca.OpenLauncher.Core/ConfigService.cs +++ b/src/IntelOrca.OpenLauncher.Core/ConfigService.cs @@ -58,6 +58,16 @@ public bool PreReleaseChecked SaveConfig(); } } + + public bool AutoUpdateGame + { + get => _config.AutoUpdateGame; + set + { + _config.AutoUpdateGame = value; + SaveConfig(); + } + } public int SelectingGame { @@ -73,6 +83,7 @@ public int SelectingGame internal struct Config { public bool PreReleaseChecked { get; set; } + public bool AutoUpdateGame { get; set; } public int SelectingGame { get; set; } } } diff --git a/src/openlauncher/MainWindow.axaml b/src/openlauncher/MainWindow.axaml index e6ad659..4e2e7a9 100644 --- a/src/openlauncher/MainWindow.axaml +++ b/src/openlauncher/MainWindow.axaml @@ -97,11 +97,16 @@ Content="{x:Static p:Resources.Download}" Click="downloadButton_Click" /> - + Checked="showDevelopmentVersionsCheckbox_Changed" + Unchecked="showDevelopmentVersionsCheckbox_Changed" /> + diff --git a/src/openlauncher/MainWindow.axaml.cs b/src/openlauncher/MainWindow.axaml.cs index 71c6ee7..2ddc459 100644 --- a/src/openlauncher/MainWindow.axaml.cs +++ b/src/openlauncher/MainWindow.axaml.cs @@ -47,8 +47,12 @@ public MainWindow() private async void Window_Opened(object sender, EventArgs e) { - showPreReleaseCheckbox.IsChecked = _configService.PreReleaseChecked; + showDevelopmentVersionsCheckbox.IsChecked = _configService.PreReleaseChecked; + autoUpdateGameCheckbox.IsChecked = _configService.AutoUpdateGame; gameListView.SelectedIndex = _configService.SelectingGame; + versionDropdown.IsEnabled = !_configService.AutoUpdateGame; + downloadButton.IsEnabled = !_configService.AutoUpdateGame; + _ready = true; var selectedItem = gameListView.SelectedItem as GameMenuItem; await SetPageAsync(selectedItem); @@ -105,19 +109,74 @@ private async void gameListView_SelectionChanged(object sender, SelectionChanged await SetPageAsync(gameListView.SelectedItem as GameMenuItem); } - private async void showPreReleaseCheckbox_Changed(object sender, RoutedEventArgs e) + private async void showDevelopmentVersionsCheckbox_Changed(object sender, RoutedEventArgs e) { if (!_ready) return; - _configService.PreReleaseChecked = showPreReleaseCheckbox.IsChecked ?? false; + _configService.PreReleaseChecked = showDevelopmentVersionsCheckbox.IsChecked ?? false; await RefreshAvailableVersionsAsync(); } - - private async void downloadButton_Click(object sender, RoutedEventArgs e) + + private async void autoUpdateGameCheckbox_Changed(object sender, RoutedEventArgs e) + { + if (!_ready) + return; + _configService.AutoUpdateGame = autoUpdateGameCheckbox.IsChecked ?? false; + SetAllInteractionEnabled(true); + } + + private async void DownloadBuild(Build build) { if (_selectedMenuItem == null) return; + + var currentVersion = await _selectedMenuItem.InstallService.GetCurrentVersionAsync(); + if (build.Version == currentVersion) + return; + + var assets = build.Assets + .Where(x => x.IsPortable) + .Where(x => x.IsApplicableForCurrentPlatform()) + .OrderBy(x => x, BuildAssetComparer.Default) + .ToArray(); + + var asset = assets.FirstOrDefault(); + if (asset == null) + { + return; + } + + var progress = new Progress(); + progress.ProgressChanged += (s, report) => + { + Dispatcher.UIThread.Post(() => + { + downloadButton.Content = report.Status; + if (report.Value is float value) + { + downloadProgress.IsIndeterminate = false; + downloadProgress.Value = value; + } + else + { + downloadProgress.IsIndeterminate = true; + } + }); + }; + + var cts = new CancellationTokenSource(); + await _selectedMenuItem.InstallService.DownloadVersion( + new DownloadService(), + new Shell(), + build.Version, + asset.Uri, + progress, + cts.Token); + await RefreshInstalledVersionAsync(); + } + private async void downloadButton_Click(object sender, RoutedEventArgs e) + { try { downloadProgress.IsVisible = true; @@ -126,42 +185,7 @@ private async void downloadButton_Click(object sender, RoutedEventArgs e) var selectedItem = versionDropdown.SelectedItem as ComboBoxItem; if (selectedItem?.Tag is Build build) { - var assets = build.Assets - .Where(x => x.IsPortable) - .Where(x => x.IsApplicableForCurrentPlatform()) - .OrderBy(x => x, BuildAssetComparer.Default) - .ToArray(); - var asset = assets.FirstOrDefault(); - if (asset != null) - { - var progress = new Progress(); - progress.ProgressChanged += (s, report) => - { - Dispatcher.UIThread.Post(() => - { - downloadButton.Content = report.Status; - if (report.Value is float value) - { - downloadProgress.IsIndeterminate = false; - downloadProgress.Value = value; - } - else - { - downloadProgress.IsIndeterminate = true; - } - }); - }; - - var cts = new CancellationTokenSource(); - await _selectedMenuItem.InstallService.DownloadVersion( - new DownloadService(), - new Shell(), - build.Version, - asset.Uri, - progress, - cts.Token); - await RefreshInstalledVersionAsync(); - } + DownloadBuild(build); } } catch (Exception ex) @@ -232,7 +256,7 @@ private async Task RefreshAvailableVersionsAsync() versionDropdown.IsHitTestVisible = false; // Refresh builds - var showDevelop = showPreReleaseCheckbox.IsChecked ?? false; + var showDevelop = showDevelopmentVersionsCheckbox.IsChecked ?? false; var builds = _selectedMenuItem.Builds; if (builds.IsDefault || (showDevelop && !_selectedMenuItem.BuildsIncludeDevelop)) { @@ -262,8 +286,12 @@ private async Task RefreshAvailableVersionsAsync() versionDropdown.SelectedIndex = 0; if (!_isBusy) { - downloadButton.IsEnabled = true; + downloadButton.IsEnabled = !_configService.AutoUpdateGame; versionDropdown.IsHitTestVisible = true; + if (_configService.AutoUpdateGame && builds.Length > 0) + { + DownloadBuild(builds[0]); + } } } } @@ -337,12 +365,14 @@ private void SetAllInteractionEnabled(bool value) gameListView.IsEnabled = value; updateButton.IsEnabled = value; versionDropdown.IsHitTestVisible = value && buildsAvailable; - showPreReleaseCheckbox.IsEnabled = value; + showDevelopmentVersionsCheckbox.IsEnabled = value; + autoUpdateGameCheckbox.IsEnabled = value; if (value) { playButton.IsEnabled = _selectedMenuItem?.InstallService.CanLaunch() ?? false; - downloadButton.IsEnabled = buildsAvailable; + downloadButton.IsEnabled = buildsAvailable && !_configService.AutoUpdateGame; + versionDropdown.IsEnabled = !_configService.AutoUpdateGame; } else { diff --git a/src/openlauncher/Properties/Resources.Designer.cs b/src/openlauncher/Properties/Resources.Designer.cs index abbdbfc..1a1b52b 100644 --- a/src/openlauncher/Properties/Resources.Designer.cs +++ b/src/openlauncher/Properties/Resources.Designer.cs @@ -87,6 +87,12 @@ public static string ShowDevelopmentVersions { } } + public static string AutoUpdateGame { + get { + return ResourceManager.GetString("AutoUpdateGame", resourceCulture); + } + } + public static string Update { get { return ResourceManager.GetString("Update", resourceCulture); diff --git a/src/openlauncher/Properties/Resources.nl.resx b/src/openlauncher/Properties/Resources.nl.resx index d9b9d56..9e88ef2 100644 --- a/src/openlauncher/Properties/Resources.nl.resx +++ b/src/openlauncher/Properties/Resources.nl.resx @@ -21,6 +21,9 @@ Ontwikkelversies tonen + + Updates automatisch installeren + Update diff --git a/src/openlauncher/Properties/Resources.resx b/src/openlauncher/Properties/Resources.resx index c788989..982c2df 100644 --- a/src/openlauncher/Properties/Resources.resx +++ b/src/openlauncher/Properties/Resources.resx @@ -21,6 +21,9 @@ Show development versions + + Automatically install updates + Update From 9d3a6584ac5d31b09f372b3a6e077d5eab419323 Mon Sep 17 00:00:00 2001 From: Michael Steenbeek Date: Sun, 18 May 2025 16:21:43 +0200 Subject: [PATCH 2/6] Fix "Extracting" text lingering --- src/openlauncher/MainWindow.axaml.cs | 92 ++++++++++++++-------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/openlauncher/MainWindow.axaml.cs b/src/openlauncher/MainWindow.axaml.cs index 2ddc459..b3fbe60 100644 --- a/src/openlauncher/MainWindow.axaml.cs +++ b/src/openlauncher/MainWindow.axaml.cs @@ -134,59 +134,50 @@ private async void DownloadBuild(Build build) if (build.Version == currentVersion) return; - var assets = build.Assets - .Where(x => x.IsPortable) - .Where(x => x.IsApplicableForCurrentPlatform()) - .OrderBy(x => x, BuildAssetComparer.Default) - .ToArray(); - - var asset = assets.FirstOrDefault(); - if (asset == null) - { - return; - } - - var progress = new Progress(); - progress.ProgressChanged += (s, report) => - { - Dispatcher.UIThread.Post(() => - { - downloadButton.Content = report.Status; - if (report.Value is float value) - { - downloadProgress.IsIndeterminate = false; - downloadProgress.Value = value; - } - else - { - downloadProgress.IsIndeterminate = true; - } - }); - }; - - var cts = new CancellationTokenSource(); - await _selectedMenuItem.InstallService.DownloadVersion( - new DownloadService(), - new Shell(), - build.Version, - asset.Uri, - progress, - cts.Token); - await RefreshInstalledVersionAsync(); - } - - private async void downloadButton_Click(object sender, RoutedEventArgs e) - { try { downloadProgress.IsVisible = true; SetAllInteractionEnabled(false); - var selectedItem = versionDropdown.SelectedItem as ComboBoxItem; - if (selectedItem?.Tag is Build build) + var assets = build.Assets + .Where(x => x.IsPortable) + .Where(x => x.IsApplicableForCurrentPlatform()) + .OrderBy(x => x, BuildAssetComparer.Default) + .ToArray(); + + var asset = assets.FirstOrDefault(); + if (asset == null) { - DownloadBuild(build); + return; } + + var progress = new Progress(); + progress.ProgressChanged += (s, report) => + { + Dispatcher.UIThread.Post(() => + { + downloadButton.Content = report.Status; + if (report.Value is float value) + { + downloadProgress.IsIndeterminate = false; + downloadProgress.Value = value; + } + else + { + downloadProgress.IsIndeterminate = true; + } + }); + }; + + var cts = new CancellationTokenSource(); + await _selectedMenuItem.InstallService.DownloadVersion( + new DownloadService(), + new Shell(), + build.Version, + asset.Uri, + progress, + cts.Token); + await RefreshInstalledVersionAsync(); } catch (Exception ex) { @@ -200,6 +191,15 @@ private async void downloadButton_Click(object sender, RoutedEventArgs e) } } + private async void downloadButton_Click(object sender, RoutedEventArgs e) + { + var selectedItem = versionDropdown.SelectedItem as ComboBoxItem; + if (selectedItem?.Tag is Build build) + { + DownloadBuild(build); + } + } + private async void playButton_Click(object sender, RoutedEventArgs e) { if (_selectedMenuItem == null) From eacb7951748f1a6c7982809ca588ce6c5a0548ae Mon Sep 17 00:00:00 2001 From: Michael Steenbeek Date: Tue, 20 May 2025 00:00:30 +0200 Subject: [PATCH 3/6] Fix error messages about async --- src/openlauncher/MainWindow.axaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openlauncher/MainWindow.axaml.cs b/src/openlauncher/MainWindow.axaml.cs index b3fbe60..24b4bd9 100644 --- a/src/openlauncher/MainWindow.axaml.cs +++ b/src/openlauncher/MainWindow.axaml.cs @@ -117,7 +117,7 @@ private async void showDevelopmentVersionsCheckbox_Changed(object sender, Routed await RefreshAvailableVersionsAsync(); } - private async void autoUpdateGameCheckbox_Changed(object sender, RoutedEventArgs e) + private void autoUpdateGameCheckbox_Changed(object sender, RoutedEventArgs e) { if (!_ready) return; @@ -191,7 +191,7 @@ await _selectedMenuItem.InstallService.DownloadVersion( } } - private async void downloadButton_Click(object sender, RoutedEventArgs e) + private void downloadButton_Click(object sender, RoutedEventArgs e) { var selectedItem = versionDropdown.SelectedItem as ComboBoxItem; if (selectedItem?.Tag is Build build) From 8d6135233c1bd5eccd100cdb87bec1bda16a0f77 Mon Sep 17 00:00:00 2001 From: Michael Steenbeek Date: Tue, 20 May 2025 00:15:26 +0200 Subject: [PATCH 4/6] Resolve switching issue --- src/openlauncher/MainWindow.axaml.cs | 135 ++++++++++++++------------- 1 file changed, 70 insertions(+), 65 deletions(-) diff --git a/src/openlauncher/MainWindow.axaml.cs b/src/openlauncher/MainWindow.axaml.cs index 24b4bd9..a93d72c 100644 --- a/src/openlauncher/MainWindow.axaml.cs +++ b/src/openlauncher/MainWindow.axaml.cs @@ -123,80 +123,85 @@ private void autoUpdateGameCheckbox_Changed(object sender, RoutedEventArgs e) return; _configService.AutoUpdateGame = autoUpdateGameCheckbox.IsChecked ?? false; SetAllInteractionEnabled(true); + + if (_configService.AutoUpdateGame) + { + InstallLatestBuildFromVersionDropdown(); + } } - private async void DownloadBuild(Build build) + private void downloadButton_Click(object sender, RoutedEventArgs e) + { + InstallLatestBuildFromVersionDropdown(); + } + + private async void InstallLatestBuildFromVersionDropdown() { if (_selectedMenuItem == null) return; - - var currentVersion = await _selectedMenuItem.InstallService.GetCurrentVersionAsync(); - if (build.Version == currentVersion) - return; - try + var selectedItem = versionDropdown.SelectedItem as ComboBoxItem; + if (selectedItem?.Tag is Build build) { - downloadProgress.IsVisible = true; - SetAllInteractionEnabled(false); - - var assets = build.Assets - .Where(x => x.IsPortable) - .Where(x => x.IsApplicableForCurrentPlatform()) - .OrderBy(x => x, BuildAssetComparer.Default) - .ToArray(); - - var asset = assets.FirstOrDefault(); - if (asset == null) - { + var currentVersion = await _selectedMenuItem.InstallService.GetCurrentVersionAsync(); + if (build.Version == currentVersion) return; - } - - var progress = new Progress(); - progress.ProgressChanged += (s, report) => + + try { - Dispatcher.UIThread.Post(() => + downloadProgress.IsVisible = true; + SetAllInteractionEnabled(false); + + var assets = build.Assets + .Where(x => x.IsPortable) + .Where(x => x.IsApplicableForCurrentPlatform()) + .OrderBy(x => x, BuildAssetComparer.Default) + .ToArray(); + + var asset = assets.FirstOrDefault(); + if (asset == null) { - downloadButton.Content = report.Status; - if (report.Value is float value) - { - downloadProgress.IsIndeterminate = false; - downloadProgress.Value = value; - } - else + return; + } + + var progress = new Progress(); + progress.ProgressChanged += (s, report) => + { + Dispatcher.UIThread.Post(() => { - downloadProgress.IsIndeterminate = true; - } - }); - }; - - var cts = new CancellationTokenSource(); - await _selectedMenuItem.InstallService.DownloadVersion( - new DownloadService(), - new Shell(), - build.Version, - asset.Uri, - progress, - cts.Token); - await RefreshInstalledVersionAsync(); - } - catch (Exception ex) - { - ShowError(StringResources.DownloadBuildFailedTitle, ex); - } - finally - { - downloadButton.Content = StringResources.Download; - downloadProgress.IsVisible = false; - SetAllInteractionEnabled(true); - } - } - - private void downloadButton_Click(object sender, RoutedEventArgs e) - { - var selectedItem = versionDropdown.SelectedItem as ComboBoxItem; - if (selectedItem?.Tag is Build build) - { - DownloadBuild(build); + downloadButton.Content = report.Status; + if (report.Value is float value) + { + downloadProgress.IsIndeterminate = false; + downloadProgress.Value = value; + } + else + { + downloadProgress.IsIndeterminate = true; + } + }); + }; + + var cts = new CancellationTokenSource(); + await _selectedMenuItem.InstallService.DownloadVersion( + new DownloadService(), + new Shell(), + build.Version, + asset.Uri, + progress, + cts.Token); + await RefreshInstalledVersionAsync(); + } + catch (Exception ex) + { + ShowError(StringResources.DownloadBuildFailedTitle, ex); + } + finally + { + downloadButton.Content = StringResources.Download; + downloadProgress.IsVisible = false; + SetAllInteractionEnabled(true); + } } } @@ -288,9 +293,9 @@ private async Task RefreshAvailableVersionsAsync() { downloadButton.IsEnabled = !_configService.AutoUpdateGame; versionDropdown.IsHitTestVisible = true; - if (_configService.AutoUpdateGame && builds.Length > 0) + if (_configService.AutoUpdateGame) { - DownloadBuild(builds[0]); + InstallLatestBuildFromVersionDropdown(); } } } From 22cdd575c411631ced4d22035e167f6ef0b5750a Mon Sep 17 00:00:00 2001 From: Michael Steenbeek Date: Thu, 22 May 2025 11:40:03 +0200 Subject: [PATCH 5/6] Move await statement into try-catch --- src/openlauncher/MainWindow.axaml.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/openlauncher/MainWindow.axaml.cs b/src/openlauncher/MainWindow.axaml.cs index a93d72c..2e72575 100644 --- a/src/openlauncher/MainWindow.axaml.cs +++ b/src/openlauncher/MainWindow.axaml.cs @@ -143,12 +143,12 @@ private async void InstallLatestBuildFromVersionDropdown() var selectedItem = versionDropdown.SelectedItem as ComboBoxItem; if (selectedItem?.Tag is Build build) { - var currentVersion = await _selectedMenuItem.InstallService.GetCurrentVersionAsync(); - if (build.Version == currentVersion) - return; - try { + var currentVersion = await _selectedMenuItem.InstallService.GetCurrentVersionAsync(); + if (build.Version == currentVersion) + return; + downloadProgress.IsVisible = true; SetAllInteractionEnabled(false); From 6deb0f02fcf338762ec3041ce8776edb3435918b Mon Sep 17 00:00:00 2001 From: Michael Steenbeek Date: Sat, 24 May 2025 13:19:44 +0200 Subject: [PATCH 6/6] Make auto-update a per-game setting --- .../ConfigService.cs | 19 ++++++-- src/openlauncher/MainWindow.axaml.cs | 44 +++++++++++++++---- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/IntelOrca.OpenLauncher.Core/ConfigService.cs b/src/IntelOrca.OpenLauncher.Core/ConfigService.cs index d922241..2a3e2d8 100644 --- a/src/IntelOrca.OpenLauncher.Core/ConfigService.cs +++ b/src/IntelOrca.OpenLauncher.Core/ConfigService.cs @@ -59,12 +59,22 @@ public bool PreReleaseChecked } } - public bool AutoUpdateGame + public bool AutoUpdateOpenRCT2 { - get => _config.AutoUpdateGame; + get => _config.AutoUpdateOpenRCT2; set { - _config.AutoUpdateGame = value; + _config.AutoUpdateOpenRCT2 = value; + SaveConfig(); + } + } + + public bool AutoUpdateOpenLoco + { + get => _config.AutoUpdateOpenLoco; + set + { + _config.AutoUpdateOpenLoco = value; SaveConfig(); } } @@ -83,7 +93,8 @@ public int SelectingGame internal struct Config { public bool PreReleaseChecked { get; set; } - public bool AutoUpdateGame { get; set; } + public bool AutoUpdateOpenRCT2 { get; set; } + public bool AutoUpdateOpenLoco { get; set; } public int SelectingGame { get; set; } } } diff --git a/src/openlauncher/MainWindow.axaml.cs b/src/openlauncher/MainWindow.axaml.cs index 2e72575..9434b15 100644 --- a/src/openlauncher/MainWindow.axaml.cs +++ b/src/openlauncher/MainWindow.axaml.cs @@ -48,10 +48,10 @@ public MainWindow() private async void Window_Opened(object sender, EventArgs e) { showDevelopmentVersionsCheckbox.IsChecked = _configService.PreReleaseChecked; - autoUpdateGameCheckbox.IsChecked = _configService.AutoUpdateGame; + autoUpdateGameCheckbox.IsChecked = AutoUpdateEnabled(); gameListView.SelectedIndex = _configService.SelectingGame; - versionDropdown.IsEnabled = !_configService.AutoUpdateGame; - downloadButton.IsEnabled = !_configService.AutoUpdateGame; + versionDropdown.IsEnabled = !AutoUpdateEnabled(); + downloadButton.IsEnabled = !AutoUpdateEnabled(); _ready = true; var selectedItem = gameListView.SelectedItem as GameMenuItem; @@ -121,10 +121,10 @@ private void autoUpdateGameCheckbox_Changed(object sender, RoutedEventArgs e) { if (!_ready) return; - _configService.AutoUpdateGame = autoUpdateGameCheckbox.IsChecked ?? false; + SetAutoUpdateEnabled(autoUpdateGameCheckbox.IsChecked ?? false); SetAllInteractionEnabled(true); - if (_configService.AutoUpdateGame) + if (AutoUpdateEnabled()) { InstallLatestBuildFromVersionDropdown(); } @@ -291,9 +291,9 @@ private async Task RefreshAvailableVersionsAsync() versionDropdown.SelectedIndex = 0; if (!_isBusy) { - downloadButton.IsEnabled = !_configService.AutoUpdateGame; + downloadButton.IsEnabled = !AutoUpdateEnabled(); versionDropdown.IsHitTestVisible = true; - if (_configService.AutoUpdateGame) + if (AutoUpdateEnabled()) { InstallLatestBuildFromVersionDropdown(); } @@ -376,8 +376,8 @@ private void SetAllInteractionEnabled(bool value) if (value) { playButton.IsEnabled = _selectedMenuItem?.InstallService.CanLaunch() ?? false; - downloadButton.IsEnabled = buildsAvailable && !_configService.AutoUpdateGame; - versionDropdown.IsEnabled = !_configService.AutoUpdateGame; + downloadButton.IsEnabled = buildsAvailable && !AutoUpdateEnabled(); + versionDropdown.IsEnabled = !AutoUpdateEnabled(); } else { @@ -385,6 +385,32 @@ private void SetAllInteractionEnabled(bool value) downloadButton.IsEnabled = value; } } + + private bool AutoUpdateEnabled() + { + if (_selectedMenuItem == null) + return false; + + var propertyName = "AutoUpdate" + _selectedMenuItem.Game.Name; + var property = _configService.GetType().GetProperty(propertyName); + if (property == null) + return false; + + return (bool)(property.GetValue(_configService) ?? false); + } + + private void SetAutoUpdateEnabled(bool on) + { + if (_selectedMenuItem == null) + return; + + var propertyName = "AutoUpdate" + _selectedMenuItem.Game.Name; + var property = _configService.GetType().GetProperty(propertyName); + if (property == null) + return; + + property.SetValue(_configService, on); + } private static string GetAge(DateTime dt) {