diff --git a/.editorconfig b/.editorconfig index 631b04b..342d7c2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -525,9 +525,11 @@ dotnet_diagnostic.IDE0079.severity = none # dotnet_remove_unnecessary_ dotnet_diagnostic.MA0134.severity = none # Observe result of async calls +dotnet_diagnostic.S2629.severity = none # Don't use string interpolation in logging message templates. dotnet_diagnostic.S3267.severity = none # Loops should be simplified with 'LINQ' expressions dotnet_diagnostic.S4487.severity = none # Remove this unread private field dotnet_diagnostic.S6602.severity = none # "Find" method should be used instead of the "FirstOrDefault" dotnet_diagnostic.S6605.severity = none # Collection-specific "Exists" method should be used instead of the "Any" +dotnet_diagnostic.S6667.severity = none # Logging in a catch clause should pass the caught exception as a parameter. dotnet_diagnostic.SCS0006.severity = none # Do Not Use Broken Cryptographic Algorithms - Its ok, only using to calculate a file-hash \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index 53f57da..3f80a19 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -44,7 +44,7 @@ - + \ No newline at end of file diff --git a/src/Atc.Installer.Integration/Enums/HostingFrameworkType.cs b/src/Atc.Installer.Integration/Enums/HostingFrameworkType.cs index 8858b58..fa438c3 100644 --- a/src/Atc.Installer.Integration/Enums/HostingFrameworkType.cs +++ b/src/Atc.Installer.Integration/Enums/HostingFrameworkType.cs @@ -12,7 +12,7 @@ public enum HostingFrameworkType NativeNoSettings, [Description(".NET Framework 4.8")] - DonNetFramework48, + DotNetFramework48, [Description(".NET 7")] DotNet7, diff --git a/src/Atc.Installer.Integration/Helpers/RegistryHelper.cs b/src/Atc.Installer.Integration/Helpers/RegistryHelper.cs new file mode 100644 index 0000000..6b20c0f --- /dev/null +++ b/src/Atc.Installer.Integration/Helpers/RegistryHelper.cs @@ -0,0 +1,82 @@ +namespace Atc.Installer.Integration.Helpers; + +[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "OK.")] +[SupportedOSPlatform("Windows")] +public static class RegistryHelper +{ + public static (bool IsSucceeded, string? ErrorMessage) CreateSubKeyInLocalMachine( + string key) + { + ArgumentException.ThrowIfNullOrEmpty(key); + + try + { + using var registryKey = Registry.LocalMachine.CreateSubKey(key); + if (registryKey is null) + { + return (false, $"Registry-LocalMachine-CreateSubKey: {key}"); + } + } + catch (Exception ex) + { + return (false, $"Registry-LocalMachine-CreateSubKey: {key} - {ex.Message}"); + } + + return (true, null); + } + + public static (bool IsSucceeded, string? ErrorMessage) DeleteSubKeyTreeInLocalMachine( + string key) + { + ArgumentException.ThrowIfNullOrEmpty(key); + + try + { + Registry.LocalMachine.DeleteSubKeyTree(key, throwOnMissingSubKey: false); + } + catch (Exception ex) + { + return (false, $"Registry-LocalMachine-DeleteSubKeyTree: {key} - {ex.Message}"); + } + + return (true, null); + } + + public static (bool IsSucceeded, string? ErrorMessage) CreateSubKeyInCurrentUser( + string key) + { + ArgumentException.ThrowIfNullOrEmpty(key); + + try + { + using var registryKey = Registry.CurrentUser.CreateSubKey(key); + if (registryKey is null) + { + return (false, $"Registry-CurrentUser-CreateSubKey: {key}"); + } + } + catch (Exception ex) + { + return (false, $"Registry-CurrentUser-CreateSubKey: {key} - {ex.Message}"); + } + + return (true, null); + } + + public static (bool IsSucceeded, string? ErrorMessage) DeleteSubKeyTreeInCurrentUser( + string key) + { + ArgumentException.ThrowIfNullOrEmpty(key); + + try + { + Registry.CurrentUser.DeleteSubKeyTree(key, throwOnMissingSubKey: false); + } + catch (Exception ex) + { + return (false, $"Registry-CurrentUser-DeleteSubKeyTree: {key} - {ex.Message}"); + } + + return (true, null); + } +} \ No newline at end of file diff --git a/src/Atc.Installer.Integration/InstallationConfigurations/ApplicationOption.cs b/src/Atc.Installer.Integration/InstallationConfigurations/ApplicationOption.cs index b30ad4c..0c768a7 100644 --- a/src/Atc.Installer.Integration/InstallationConfigurations/ApplicationOption.cs +++ b/src/Atc.Installer.Integration/InstallationConfigurations/ApplicationOption.cs @@ -26,6 +26,8 @@ public class ApplicationOption public IList FolderPermissions { get; set; } = new List(); + public IList RegistrySettings { get; set; } = new List(); + public IList FirewallRules { get; set; } = new List(); public IList ConfigurationSettingsFiles { get; set; } = new List(); diff --git a/src/Atc.Installer.Integration/InstallationConfigurations/RegistrySettingOption.cs b/src/Atc.Installer.Integration/InstallationConfigurations/RegistrySettingOption.cs new file mode 100644 index 0000000..46f3da5 --- /dev/null +++ b/src/Atc.Installer.Integration/InstallationConfigurations/RegistrySettingOption.cs @@ -0,0 +1,11 @@ +namespace Atc.Installer.Integration.InstallationConfigurations; + +public class RegistrySettingOption +{ + public string Key { get; set; } = string.Empty; + + public InsertRemoveType Action { get; set; } + + public override string ToString() + => $"{nameof(Key)}: {Key}, {nameof(Action)}: {Action}"; +} \ No newline at end of file diff --git a/src/Atc.Installer.Wpf.App/Atc.Installer.Wpf.App.csproj b/src/Atc.Installer.Wpf.App/Atc.Installer.Wpf.App.csproj index 975c35b..ac8d2f6 100644 --- a/src/Atc.Installer.Wpf.App/Atc.Installer.Wpf.App.csproj +++ b/src/Atc.Installer.Wpf.App/Atc.Installer.Wpf.App.csproj @@ -43,10 +43,10 @@ - - - - + + + + diff --git a/src/Atc.Installer.Wpf.App/Helpers/ConfigurationFileHelper.cs b/src/Atc.Installer.Wpf.App/Helpers/ConfigurationFileHelper.cs index 932863a..9059e81 100644 --- a/src/Atc.Installer.Wpf.App/Helpers/ConfigurationFileHelper.cs +++ b/src/Atc.Installer.Wpf.App/Helpers/ConfigurationFileHelper.cs @@ -130,6 +130,15 @@ private static void MapCustomSettingsToTemplateSettings( } } + foreach (var customRegistrySetting in customApplication.RegistrySettings) + { + if (templateApplication.RegistrySettings.FirstOrDefault(x => x.Key == customRegistrySetting.Key) is + null) + { + templateApplication.RegistrySettings.Add(customRegistrySetting); + } + } + foreach (var customFirewallRule in customApplication.FirewallRules) { if (templateApplication.FirewallRules.FirstOrDefault(x => x.Name == customFirewallRule.Name) is null) diff --git a/src/Atc.Installer.Wpf.App/MainWindowViewModel_Commands.cs b/src/Atc.Installer.Wpf.App/MainWindowViewModel_Commands.cs index f7564c5..20122a8 100644 --- a/src/Atc.Installer.Wpf.App/MainWindowViewModel_Commands.cs +++ b/src/Atc.Installer.Wpf.App/MainWindowViewModel_Commands.cs @@ -118,6 +118,7 @@ InstallationFile is not null && x.DefaultApplicationSettings.IsDirty || x.ApplicationSettings.IsDirty || x.FolderPermissions.IsDirty || + x.RegistrySettings.IsDirty || x.FirewallRules.IsDirty || x.ConfigurationSettingsFiles.IsDirty)); @@ -258,7 +259,7 @@ private async Task ReportingToExcelCommandHandler() InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal), Title = "Select a file", Filter = "Excel Files|*.xlsx", - FileName = ProjectName, + FileName = ProjectName ?? "NoName", }; if (saveFileDialog.ShowDialog() != true || diff --git a/src/Atc.Installer.Wpf.ComponentProvider.ElasticSearch/Atc.Installer.Wpf.ComponentProvider.ElasticSearch.csproj b/src/Atc.Installer.Wpf.ComponentProvider.ElasticSearch/Atc.Installer.Wpf.ComponentProvider.ElasticSearch.csproj index dc78b85..cb8c1dd 100644 --- a/src/Atc.Installer.Wpf.ComponentProvider.ElasticSearch/Atc.Installer.Wpf.ComponentProvider.ElasticSearch.csproj +++ b/src/Atc.Installer.Wpf.ComponentProvider.ElasticSearch/Atc.Installer.Wpf.ComponentProvider.ElasticSearch.csproj @@ -8,10 +8,10 @@ - - - - + + + + diff --git a/src/Atc.Installer.Wpf.ComponentProvider.InternetInformationServer/Atc.Installer.Wpf.ComponentProvider.InternetInformationServer.csproj b/src/Atc.Installer.Wpf.ComponentProvider.InternetInformationServer/Atc.Installer.Wpf.ComponentProvider.InternetInformationServer.csproj index a4fa938..eabfd41 100644 --- a/src/Atc.Installer.Wpf.ComponentProvider.InternetInformationServer/Atc.Installer.Wpf.ComponentProvider.InternetInformationServer.csproj +++ b/src/Atc.Installer.Wpf.ComponentProvider.InternetInformationServer/Atc.Installer.Wpf.ComponentProvider.InternetInformationServer.csproj @@ -8,10 +8,10 @@ - - - - + + + + diff --git a/src/Atc.Installer.Wpf.ComponentProvider.InternetInformationServer/InternetInformationServerComponentProviderViewModel.cs b/src/Atc.Installer.Wpf.ComponentProvider.InternetInformationServer/InternetInformationServerComponentProviderViewModel.cs index cb3d659..6a06765 100644 --- a/src/Atc.Installer.Wpf.ComponentProvider.InternetInformationServer/InternetInformationServerComponentProviderViewModel.cs +++ b/src/Atc.Installer.Wpf.ComponentProvider.InternetInformationServer/InternetInformationServerComponentProviderViewModel.cs @@ -774,7 +774,7 @@ private void CheckPrerequisitesForHostingFramework() { switch (HostingFramework) { - case HostingFrameworkType.DonNetFramework48: + case HostingFrameworkType.DotNetFramework48: if (iisInstallerService.IsMicrosoftDotNetFramework48()) { AddToInstallationPrerequisites("IsMicrosoftDotNetFramework48", LogCategoryType.Information, "Microsoft .NET Framework 4.8 is installed"); @@ -976,6 +976,8 @@ private async Task ServiceDeployWebsitePostProcessing( EnsureFolderPermissions(); + EnsureRegistrySettings(); + EnsureFirewallRules(); if (HostingFramework == HostingFrameworkType.NodeJs) diff --git a/src/Atc.Installer.Wpf.ComponentProvider.PostgreSql/Atc.Installer.Wpf.ComponentProvider.PostgreSql.csproj b/src/Atc.Installer.Wpf.ComponentProvider.PostgreSql/Atc.Installer.Wpf.ComponentProvider.PostgreSql.csproj index 6141700..8ecfc93 100644 --- a/src/Atc.Installer.Wpf.ComponentProvider.PostgreSql/Atc.Installer.Wpf.ComponentProvider.PostgreSql.csproj +++ b/src/Atc.Installer.Wpf.ComponentProvider.PostgreSql/Atc.Installer.Wpf.ComponentProvider.PostgreSql.csproj @@ -8,10 +8,10 @@ - - - - + + + + diff --git a/src/Atc.Installer.Wpf.ComponentProvider.WindowsApplication/Atc.Installer.Wpf.ComponentProvider.WindowsApplication.csproj b/src/Atc.Installer.Wpf.ComponentProvider.WindowsApplication/Atc.Installer.Wpf.ComponentProvider.WindowsApplication.csproj index 35ab153..a4028ec 100644 --- a/src/Atc.Installer.Wpf.ComponentProvider.WindowsApplication/Atc.Installer.Wpf.ComponentProvider.WindowsApplication.csproj +++ b/src/Atc.Installer.Wpf.ComponentProvider.WindowsApplication/Atc.Installer.Wpf.ComponentProvider.WindowsApplication.csproj @@ -8,10 +8,10 @@ - - - - + + + + diff --git a/src/Atc.Installer.Wpf.ComponentProvider.WindowsApplication/WindowsApplicationComponentProviderViewModel.cs b/src/Atc.Installer.Wpf.ComponentProvider.WindowsApplication/WindowsApplicationComponentProviderViewModel.cs index fcf0ca7..2932f61 100644 --- a/src/Atc.Installer.Wpf.ComponentProvider.WindowsApplication/WindowsApplicationComponentProviderViewModel.cs +++ b/src/Atc.Installer.Wpf.ComponentProvider.WindowsApplication/WindowsApplicationComponentProviderViewModel.cs @@ -529,10 +529,10 @@ private void CheckPrerequisitesForHostingFramework() case HostingFrameworkType.DotNet8: AddToInstallationPrerequisites("IsMicrosoftDotNet8", LogCategoryType.Warning, "Microsoft .NET 8 is not installed"); break; - case HostingFrameworkType.DonNetFramework48 when waInstallerService.IsMicrosoftDotNetFramework48(): + case HostingFrameworkType.DotNetFramework48 when waInstallerService.IsMicrosoftDotNetFramework48(): AddToInstallationPrerequisites("IsMicrosoftDotNetFramework48", LogCategoryType.Information, "Microsoft .NET Framework 4.8 is installed"); break; - case HostingFrameworkType.DonNetFramework48: + case HostingFrameworkType.DotNetFramework48: AddToInstallationPrerequisites("IsMicrosoftDotNetFramework48", LogCategoryType.Warning, "Microsoft .NET Framework 4.8 is not installed"); break; case HostingFrameworkType.Native: @@ -796,6 +796,8 @@ private async Task ServiceDeployWindowServicePostProcessing( EnsureFolderPermissions(); + EnsureRegistrySettings(); + EnsureFirewallRules(); if (TryGetStringFromApplicationSettings("WebProtocol", out _)) @@ -886,6 +888,8 @@ private Task ServiceDeployWindowApplicationCreate( EnsureFolderPermissions(); + EnsureRegistrySettings(); + EnsureFirewallRules(); InstallationState = ComponentInstallationState.Installed; @@ -921,6 +925,8 @@ private bool ServiceDeployWindowApplicationUpdate( EnsureFolderPermissions(); + EnsureRegistrySettings(); + EnsureFirewallRules(); InstallationState = ComponentInstallationState.Installed; diff --git a/src/Atc.Installer.Wpf.ComponentProvider/Atc.Installer.Wpf.ComponentProvider.csproj b/src/Atc.Installer.Wpf.ComponentProvider/Atc.Installer.Wpf.ComponentProvider.csproj index 0ccfb80..3424b0b 100644 --- a/src/Atc.Installer.Wpf.ComponentProvider/Atc.Installer.Wpf.ComponentProvider.csproj +++ b/src/Atc.Installer.Wpf.ComponentProvider/Atc.Installer.Wpf.ComponentProvider.csproj @@ -16,10 +16,10 @@ - - - - + + + + diff --git a/src/Atc.Installer.Wpf.ComponentProvider/ComponentProviderViewModel.cs b/src/Atc.Installer.Wpf.ComponentProvider/ComponentProviderViewModel.cs index 041220d..ef732b4 100644 --- a/src/Atc.Installer.Wpf.ComponentProvider/ComponentProviderViewModel.cs +++ b/src/Atc.Installer.Wpf.ComponentProvider/ComponentProviderViewModel.cs @@ -36,6 +36,7 @@ public ComponentProviderViewModel() DefaultApplicationSettings = new ApplicationSettingsViewModel(isDefaultApplicationSettings: true, RefComponentProviders); ApplicationSettings = new ApplicationSettingsViewModel(isDefaultApplicationSettings: false, RefComponentProviders); FolderPermissions = new FolderPermissionsViewModel(); + RegistrySettings = new RegistrySettingsViewModel(); FirewallRules = new FirewallRulesViewModel(); ConfigurationSettingsFiles = new ConfigurationSettingsFilesViewModel(); } @@ -80,6 +81,9 @@ public ComponentProviderViewModel( FolderPermissions = new FolderPermissionsViewModel(); FolderPermissions.Populate(this, applicationOption.FolderPermissions); + RegistrySettings = new RegistrySettingsViewModel(); + RegistrySettings.Populate(this, applicationOption.RegistrySettings); + FirewallRules = new FirewallRulesViewModel(); FirewallRules.Populate(this, applicationOption.FirewallRules); @@ -155,6 +159,8 @@ public bool ShowOnlyBaseSettings public FolderPermissionsViewModel FolderPermissions { get; } + public RegistrySettingsViewModel RegistrySettings { get; } + public FirewallRulesViewModel FirewallRules { get; } public ConfigurationSettingsFilesViewModel ConfigurationSettingsFiles { get; } diff --git a/src/Atc.Installer.Wpf.ComponentProvider/ComponentProviderViewModel_LogicBase.cs b/src/Atc.Installer.Wpf.ComponentProvider/ComponentProviderViewModel_LogicBase.cs index 8e2cd8f..ec57cd5 100644 --- a/src/Atc.Installer.Wpf.ComponentProvider/ComponentProviderViewModel_LogicBase.cs +++ b/src/Atc.Installer.Wpf.ComponentProvider/ComponentProviderViewModel_LogicBase.cs @@ -3,6 +3,7 @@ // ReSharper disable ReturnTypeCanBeEnumerable.Local // ReSharper disable StringLiteralTypo // ReSharper disable SwitchStatementHandlesSomeKnownEnumValuesWithDefault +// ReSharper disable SwitchStatementMissingSomeEnumCasesNoDefault namespace Atc.Installer.Wpf.ComponentProvider; [SuppressMessage("Design", "MA0048:File name must match type name", Justification = "OK - partial class")] @@ -110,7 +111,7 @@ public void LoadConfigurationFiles() switch (HostingFramework) { - case HostingFrameworkType.DonNetFramework48: + case HostingFrameworkType.DotNetFramework48: { if (InstalledMainFilePath is not null) { @@ -201,10 +202,21 @@ public void PrepareInstallationFiles( UnpackedZipFolderPath = Path.Combine(InstallerTempDirectory.FullName, @$"{ProjectName}\Unpacked\{Name}"); - if (Directory.Exists(UnpackedZipFolderPath) && - Directory.GetFiles(UnpackedZipFolderPath).Length == 0) + if (Directory.Exists(UnpackedZipFolderPath)) { - Directory.Delete(UnpackedZipFolderPath, recursive: true); + var minimumExpectedNumberOfFiles = 1; + if (HostingFramework is + HostingFrameworkType.DotNetFramework48 or + HostingFrameworkType.DotNet7 or + HostingFrameworkType.DotNet8) + { + minimumExpectedNumberOfFiles = 10; + } + + if (Directory.GetFiles(UnpackedZipFolderPath).Length < minimumExpectedNumberOfFiles) + { + Directory.Delete(UnpackedZipFolderPath, recursive: true); + } } if (unpackIfExist || @@ -524,6 +536,69 @@ protected void EnsureFolderPermissions() AddLogItem(LogLevel.Information, "Folder permissions is ensured"); } + protected void EnsureRegistrySettings() + { + AddLogItem(LogLevel.Trace, "Ensure registry settings"); + + foreach (var vm in RegistrySettings.Items) + { + var key = vm.Key; + if (key.ContainsTemplatePattern()) + { + key = ResolveTemplateIfNeededByApplicationSettingsLookup(key); + } + + if (key.StartsWith("HKEY_LOCAL_MACHINE\\", StringComparison.Ordinal)) + { + key = key.Replace("HKEY_LOCAL_MACHINE\\", string.Empty, StringComparison.Ordinal); + switch (vm.Action) + { + case InsertRemoveType.Insert: + var insertResult = RegistryHelper.CreateSubKeyInLocalMachine(key); + if (!insertResult.IsSucceeded) + { + AddLogItem(LogLevel.Error, insertResult.ErrorMessage!); + } + + break; + case InsertRemoveType.Remove: + var removeResult = RegistryHelper.DeleteSubKeyTreeInLocalMachine(key); + if (!removeResult.IsSucceeded) + { + AddLogItem(LogLevel.Error, removeResult.ErrorMessage!); + } + + break; + } + } + else if (key.StartsWith("HKEY_CURRENT_USER\\", StringComparison.Ordinal)) + { + key = key.Replace("HKEY_CURRENT_USER\\", string.Empty, StringComparison.Ordinal); + switch (vm.Action) + { + case InsertRemoveType.Insert: + var insertResult = RegistryHelper.CreateSubKeyInCurrentUser(key); + if (!insertResult.IsSucceeded) + { + AddLogItem(LogLevel.Error, insertResult.ErrorMessage!); + } + + break; + case InsertRemoveType.Remove: + var removeResult = RegistryHelper.DeleteSubKeyTreeInCurrentUser(key); + if (!removeResult.IsSucceeded) + { + AddLogItem(LogLevel.Error, removeResult.ErrorMessage!); + } + + break; + } + } + } + + AddLogItem(LogLevel.Trace, "Registry settings is ensured"); + } + protected void EnsureFirewallRules() { AddLogItem(LogLevel.Trace, "Ensure firewall rules"); @@ -760,7 +835,7 @@ public async Task GetReportingData() switch (HostingFramework) { - case HostingFrameworkType.DonNetFramework48 or + case HostingFrameworkType.DotNetFramework48 or HostingFrameworkType.DotNet7 or HostingFrameworkType.DotNet8: { @@ -883,7 +958,7 @@ private void ResolveInstalledMainFile( { case { ComponentType: ComponentType.Application, HostingFramework: HostingFrameworkType.DotNet7 }: case { ComponentType: ComponentType.Application, HostingFramework: HostingFrameworkType.DotNet8 }: - case { ComponentType: ComponentType.Application, HostingFramework: HostingFrameworkType.DonNetFramework48 }: + case { ComponentType: ComponentType.Application, HostingFramework: HostingFrameworkType.DotNetFramework48 }: InstalledMainFilePath = new ValueTemplateItemViewModel( Path.Combine(basePath, $"{Name}.exe"), template: instFolderPath, @@ -897,7 +972,7 @@ private void ResolveInstalledMainFile( break; case { ComponentType: ComponentType.InternetInformationService, HostingFramework: HostingFrameworkType.DotNet7 }: case { ComponentType: ComponentType.InternetInformationService, HostingFramework: HostingFrameworkType.DotNet8 }: - case { ComponentType: ComponentType.InternetInformationService, HostingFramework: HostingFrameworkType.DonNetFramework48 }: + case { ComponentType: ComponentType.InternetInformationService, HostingFramework: HostingFrameworkType.DotNetFramework48 }: InstalledMainFilePath = new ValueTemplateItemViewModel( Path.Combine(basePath, $"{Name}.dll"), template: instFolderPath, @@ -911,7 +986,7 @@ private void ResolveInstalledMainFile( break; case { ComponentType: ComponentType.WindowsService, HostingFramework: HostingFrameworkType.DotNet7 }: case { ComponentType: ComponentType.WindowsService, HostingFramework: HostingFrameworkType.DotNet8 }: - case { ComponentType: ComponentType.WindowsService, HostingFramework: HostingFrameworkType.DonNetFramework48 }: + case { ComponentType: ComponentType.WindowsService, HostingFramework: HostingFrameworkType.DotNetFramework48 }: InstalledMainFilePath = new ValueTemplateItemViewModel( Path.Combine(basePath, $"{Name}.exe"), template: instFolderPath, diff --git a/src/Atc.Installer.Wpf.ComponentProvider/Controls/RegistrySettingsViewModel.cs b/src/Atc.Installer.Wpf.ComponentProvider/Controls/RegistrySettingsViewModel.cs new file mode 100644 index 0000000..f6d7cde --- /dev/null +++ b/src/Atc.Installer.Wpf.ComponentProvider/Controls/RegistrySettingsViewModel.cs @@ -0,0 +1,61 @@ +namespace Atc.Installer.Wpf.ComponentProvider.Controls; + +public class RegistrySettingsViewModel : ViewModelBase +{ + private ComponentProviderViewModel? refComponentProvider; + private bool enableEditingMode; + + public RegistrySettingsViewModel() + => Messenger.Default.Register(this, HandleUpdateApplicationOptionsMessage); + + public bool EnableEditingMode + { + get => enableEditingMode; + set + { + if (enableEditingMode == value) + { + return; + } + + enableEditingMode = value; + RaisePropertyChanged(); + } + } + + public ObservableCollectionEx Items { get; init; } = new(); + + public void Populate( + ComponentProviderViewModel refComponentProviderViewModel, + IList registrySettings) + { + ArgumentNullException.ThrowIfNull(refComponentProviderViewModel); + ArgumentNullException.ThrowIfNull(registrySettings); + + refComponentProvider = refComponentProviderViewModel; + + Items.Clear(); + + Items.SuppressOnChangedNotification = true; + + foreach (var registrySetting in registrySettings) + { + Items.Add(new RegistrySettingViewModel(registrySetting)); + } + + Items.SuppressOnChangedNotification = false; + } + + public void ClearAllIsDirty() + { + IsDirty = false; + foreach (var item in Items) + { + item.IsDirty = false; + } + } + + private void HandleUpdateApplicationOptionsMessage( + UpdateApplicationOptionsMessage obj) + => EnableEditingMode = obj.EnableEditingMode; +} \ No newline at end of file diff --git a/src/Atc.Installer.Wpf.ComponentProvider/ValueConverters/ComponentProviderVersionCompareVisibilityVisibleValueConverter.cs b/src/Atc.Installer.Wpf.ComponentProvider/ValueConverters/ComponentProviderVersionCompareVisibilityVisibleValueConverter.cs index f08a1cc..c86dd38 100644 --- a/src/Atc.Installer.Wpf.ComponentProvider/ValueConverters/ComponentProviderVersionCompareVisibilityVisibleValueConverter.cs +++ b/src/Atc.Installer.Wpf.ComponentProvider/ValueConverters/ComponentProviderVersionCompareVisibilityVisibleValueConverter.cs @@ -21,7 +21,7 @@ public object Convert( } if (vm.HostingFramework is - HostingFrameworkType.DonNetFramework48 or + HostingFrameworkType.DotNetFramework48 or HostingFrameworkType.DotNet7 or HostingFrameworkType.DotNet8) { diff --git a/src/Atc.Installer.Wpf.ComponentProvider/ViewModels/RegistrySettingViewModel.cs b/src/Atc.Installer.Wpf.ComponentProvider/ViewModels/RegistrySettingViewModel.cs new file mode 100644 index 0000000..61ebb7d --- /dev/null +++ b/src/Atc.Installer.Wpf.ComponentProvider/ViewModels/RegistrySettingViewModel.cs @@ -0,0 +1,43 @@ +namespace Atc.Installer.Wpf.ComponentProvider.ViewModels; + +public class RegistrySettingViewModel : ViewModelBase +{ + private string key = string.Empty; + private InsertRemoveType action; + + public RegistrySettingViewModel() + { + } + + public RegistrySettingViewModel( + RegistrySettingOption registrySetting) + { + ArgumentNullException.ThrowIfNull(registrySetting); + + Key = registrySetting.Key; + Action = registrySetting.Action; + } + + public string Key + { + get => key; + set + { + key = value; + RaisePropertyChanged(); + } + } + + public InsertRemoveType Action + { + get => action; + set + { + action = value; + RaisePropertyChanged(); + } + } + + public override string ToString() + => $"{nameof(Key)}: {Key}, {nameof(Action)}: {Action}"; +} \ No newline at end of file