diff --git a/src/Sms.TaskTwo.Avalonia/Views/MainWindow.axaml b/src/Sms.TaskTwo.Avalonia/Views/MainWindow.axaml index 15cef0b..878eea8 100644 --- a/src/Sms.TaskTwo.Avalonia/Views/MainWindow.axaml +++ b/src/Sms.TaskTwo.Avalonia/Views/MainWindow.axaml @@ -57,7 +57,7 @@ Spacing="2"> + IsVisible="{Binding HasAddVariableError}" /> _gridRows = new(); - public MainWindow() { InitializeComponent(); @@ -24,71 +23,25 @@ public partial class MainWindow : Window return; } - if (_gridRows.TryGetValue(row, out var previousRow) && previousRow != e.Row) - { - previousRow.DataContextChanged -= OnRowDataContextChanged; - } - - _gridRows[row] = e.Row; ApplyRowClasses(e.Row, row); - row.RowAppearanceChanged -= OnRowAppearanceChanged; - row.RowAppearanceChanged += OnRowAppearanceChanged; - - e.Row.DataContextChanged -= OnRowDataContextChanged; - e.Row.DataContextChanged += OnRowDataContextChanged; - } - - private void OnRowDataContextChanged(object? sender, EventArgs e) - { - if (sender is not DataGridRow gridRow) + PropertyChangedEventHandler? handler = null; + handler = (_, args) => { - return; - } + if (args.PropertyName is nameof(EnvironmentVariableRowViewModel.UseUserStore)) + { + ApplyRowClasses(e.Row, row); + } + }; - var row = _gridRows.FirstOrDefault(pair => pair.Value == gridRow).Key; - if (row is null) - { - return; - } - - row.RowAppearanceChanged -= OnRowAppearanceChanged; - _gridRows.Remove(row); - gridRow.DataContextChanged -= OnRowDataContextChanged; - } - - private void OnRowAppearanceChanged(object? sender, EventArgs e) - { - if (sender is not EnvironmentVariableRowViewModel row) - { - return; - } - - if (_gridRows.TryGetValue(row, out var gridRow)) - { - ApplyRowClasses(gridRow, row); - } + row.PropertyChanged += handler; + e.Row.DetachedFromVisualTree += (_, _) => row.PropertyChanged -= handler; } private static void ApplyRowClasses(DataGridRow gridRow, EnvironmentVariableRowViewModel row) { - gridRow.Classes.Remove("appSettings"); - gridRow.Classes.Remove("custom"); - gridRow.Classes.Remove("userStore"); - - if (row.IsFromAppSettings) - { - gridRow.Classes.Add("appSettings"); - } - - if (row.IsCustom) - { - gridRow.Classes.Add("custom"); - } - - if (row.UseUserStore) - { - gridRow.Classes.Add("userStore"); - } + gridRow.Classes.Set("appSettings", row.IsFromAppSettings); + gridRow.Classes.Set("custom", row.IsCustom); + gridRow.Classes.Set("userStore", row.UseUserStore); } } diff --git a/src/Sms.TaskTwo.Core/Services/EnvironmentVariablesService.cs b/src/Sms.TaskTwo.Core/Services/EnvironmentVariablesService.cs index 688a9e3..57040d7 100644 --- a/src/Sms.TaskTwo.Core/Services/EnvironmentVariablesService.cs +++ b/src/Sms.TaskTwo.Core/Services/EnvironmentVariablesService.cs @@ -36,12 +36,11 @@ public sealed class EnvironmentVariablesService _options = options.Value; _log = log; _configuredNames = new HashSet(_options.Names, StringComparer.Ordinal); + ReloadMetadata(); } public void EnsureConfiguredVariablesExist() { - ReloadMetadata(); - foreach (var name in _options.Names) { if (_store.IsPersistedInUserStore(name)) @@ -51,8 +50,7 @@ public sealed class EnvironmentVariablesService var value = ResolveRequiredValue(name); _store.Set(name, value); - _log.WriteLine( - $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Created configured variable: {name}={value}"); + LogInfo($"Created configured variable: {name}={value}"); } } @@ -97,7 +95,6 @@ public sealed class EnvironmentVariablesService public bool TryAddCustomVariable(string name, string value, out string? errorMessage) { - ReloadMetadata(); name = name.Trim(); if (string.IsNullOrWhiteSpace(name)) @@ -127,20 +124,16 @@ public sealed class EnvironmentVariablesService _customNames.Add(name); PersistCustomNames(); _store.Set(name, value); - _log.WriteLine( - $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Created custom variable: {name}={value}"); + LogInfo($"Created custom variable: {name}={value}"); errorMessage = null; return true; } - public EnvironmentVariableRow GetRowSnapshot(string name) - { - ReloadMetadata(); - return CreateRow(name, ResolveRequiredValue(name)); - } + public EnvironmentVariableRow GetRowSnapshot(string name) => + CreateRow(name, ResolveRequiredValue(name)); - public string GetDisplayValue(string name) => ResolveRequiredValue(name); + public string GetRequiredValue(string name) => ResolveRequiredValue(name); public string? GetProcessValue(string name) => _store.GetProcessEnvironment().TryGetValue(name, out var value) ? value : null; @@ -149,8 +142,7 @@ public sealed class EnvironmentVariablesService { var previous = _store.GetUserPersistedValue(name); _store.Set(name, value); - _log.WriteLine( - $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Changed value: {name}={value} (previous: {previous ?? ""})"); + LogInfo($"Changed value: {name}={value} (previous: {previous ?? ""})"); } public void SaveComment(string name, string comment) @@ -158,22 +150,18 @@ public sealed class EnvironmentVariablesService var previous = _comments.GetValueOrDefault(name, string.Empty); _comments[name] = comment; PersistComments(); - _log.WriteLine( - $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Changed comment: {name}={comment} (previous: {(string.IsNullOrEmpty(previous) ? "" : previous)})"); + LogInfo($"Changed comment: {name}={comment} (previous: {(string.IsNullOrEmpty(previous) ? "" : previous)})"); } public void RemoveVariable(string name) { - ReloadMetadata(); - if (IsCustom(name)) { _customNames.Remove(name); PersistCustomNames(); _comments.Remove(name); PersistComments(); - _log.WriteLine( - $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Removed custom variable from list: {name}"); + LogInfo($"Removed custom variable from list: {name}"); } DeleteFromUserStore(name); @@ -188,8 +176,7 @@ public sealed class EnvironmentVariablesService var previous = _store.GetUserPersistedValue(name); _store.RemoveFromUserStore(name); - _log.WriteLine( - $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Removed from user store: {name} (previous: {previous ?? ""})"); + LogInfo($"Removed from user store: {name} (previous: {previous ?? ""})"); } public bool IsCustom(string name) => _customNames.Contains(name, StringComparer.Ordinal); @@ -227,12 +214,7 @@ public sealed class EnvironmentVariablesService if (_configuredNames.Contains(name) || _customNames.Contains(name)) { - if (_options.Defaults.TryGetValue(name, out var defaultValue)) - { - return defaultValue; - } - - return string.Empty; + return _options.Defaults.TryGetValue(name, out var defaultValue) ? defaultValue : string.Empty; } return string.Empty; @@ -252,8 +234,7 @@ public sealed class EnvironmentVariablesService } catch (JsonException ex) { - _log.WriteLine( - $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [WARN] Failed to parse custom variables {_options.CustomVariablesVariableName}: {ex.Message}"); + LogWarn($"Failed to parse custom variables {_options.CustomVariablesVariableName}: {ex.Message}"); return []; } } @@ -279,8 +260,7 @@ public sealed class EnvironmentVariablesService } catch (JsonException ex) { - _log.WriteLine( - $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [WARN] Failed to parse comments variable {_options.CommentsVariableName}: {ex.Message}"); + LogWarn($"Failed to parse comments variable {_options.CommentsVariableName}: {ex.Message}"); return new Dictionary(StringComparer.Ordinal); } } @@ -290,4 +270,10 @@ public sealed class EnvironmentVariablesService var json = JsonSerializer.Serialize(_comments, JsonOptions); _store.Set(_options.CommentsVariableName, json); } + + private void LogInfo(string message) => + _log.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] {message}"); + + private void LogWarn(string message) => + _log.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [WARN] {message}"); } diff --git a/src/Sms.TaskTwo.ViewModels/EnvironmentVariableRowViewModel.cs b/src/Sms.TaskTwo.ViewModels/EnvironmentVariableRowViewModel.cs index ed3f66b..0545d59 100644 --- a/src/Sms.TaskTwo.ViewModels/EnvironmentVariableRowViewModel.cs +++ b/src/Sms.TaskTwo.ViewModels/EnvironmentVariableRowViewModel.cs @@ -36,6 +36,7 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject private bool _useUserStore; [ObservableProperty] + [NotifyPropertyChangedFor(nameof(ActualValueDisplay))] private string _actualValue = string.Empty; public string ActualValueDisplay => @@ -49,8 +50,6 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject partial void OnUseUserStoreChanged(bool value) { - RowAppearanceChanged?.Invoke(this, EventArgs.Empty); - if (_isLoading) { return; @@ -58,9 +57,7 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject if (IsFromAppSettings && !value) { - BeginLoad(); - UseUserStore = true; - EndLoad(); + RunWithoutSave(() => UseUserStore = true); return; } @@ -79,14 +76,10 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject } _service.DeleteFromUserStore(Field); - BeginLoad(); - RequiredValue = _service.GetDisplayValue(Field); - EndLoad(); + RunWithoutSave(() => RequiredValue = _service.GetRequiredValue(Field)); RefreshActualValue(_service.GetProcessValue(Field)); } - partial void OnActualValueChanged(string value) => OnPropertyChanged(nameof(ActualValueDisplay)); - partial void OnRequiredValueChanged(string value) { if (_isLoading || !UseUserStore) @@ -98,22 +91,22 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject RefreshActualValue(_service.GetProcessValue(Field)); } - public event EventHandler? RowAppearanceChanged; - public void ApplySnapshot(EnvironmentVariableRow row, bool suppressSave = false) { if (suppressSave) { - BeginLoad(); + RunWithoutSave(ApplyValues); + } + else + { + ApplyValues(); } - RequiredValue = row.Value; - Comment = row.Comment; - UseUserStore = row.IsFromAppSettings || row.IsPersistedInUserStore; - - if (suppressSave) + void ApplyValues() { - EndLoad(); + RequiredValue = row.Value; + Comment = row.Comment; + UseUserStore = row.IsFromAppSettings || row.IsPersistedInUserStore; } } @@ -122,10 +115,6 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject ActualValue = processValue ?? string.Empty; } - public void BeginLoad() => _isLoading = true; - - public void EndLoad() => _isLoading = false; - partial void OnCommentChanged(string value) { if (_isLoading) @@ -135,4 +124,17 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject _service.SaveComment(Field, value); } + + private void RunWithoutSave(Action action) + { + _isLoading = true; + try + { + action(); + } + finally + { + _isLoading = false; + } + } } diff --git a/src/Sms.TaskTwo.ViewModels/MainWindowViewModel.cs b/src/Sms.TaskTwo.ViewModels/MainWindowViewModel.cs index a1ce390..67d59ab 100644 --- a/src/Sms.TaskTwo.ViewModels/MainWindowViewModel.cs +++ b/src/Sms.TaskTwo.ViewModels/MainWindowViewModel.cs @@ -30,8 +30,11 @@ public sealed partial class MainWindowViewModel : ObservableObject private string _newVariableValue = string.Empty; [ObservableProperty] + [NotifyPropertyChangedFor(nameof(HasAddVariableError))] private string? _addVariableError; + public bool HasAddVariableError => !string.IsNullOrEmpty(AddVariableError); + partial void OnShowAllVariablesChanged(bool value) => SyncRowsFromService(); partial void OnNewVariableNameChanged(string value) => AddVariableCommand.NotifyCanExecuteChanged(); @@ -59,18 +62,18 @@ public sealed partial class MainWindowViewModel : ObservableObject private EnvironmentVariableRowViewModel CreateRowViewModel(EnvironmentVariableRow row) { - EnvironmentVariableRowViewModel? viewModel = null; - viewModel = new EnvironmentVariableRowViewModel( + var field = row.Field; + return new EnvironmentVariableRowViewModel( row, _service, onCustomRemoved: () => { - if (viewModel is not null) + var existing = Rows.FirstOrDefault(r => r.Field == field); + if (existing is not null) { - Rows.Remove(viewModel); + Rows.Remove(existing); } }); - return viewModel; } private int FindInsertIndexForCustomVariable() @@ -128,11 +131,6 @@ public sealed partial class MainWindowViewModel : ObservableObject } } - RefreshProcessStates(); - } - - private void RefreshProcessStates() - { foreach (var row in Rows) { row.RefreshActualValue(_service.GetProcessValue(row.Field)); diff --git a/src/Sms.TaskTwo.Wpf/App.xaml b/src/Sms.TaskTwo.Wpf/App.xaml index d523f36..243195b 100644 --- a/src/Sms.TaskTwo.Wpf/App.xaml +++ b/src/Sms.TaskTwo.Wpf/App.xaml @@ -4,6 +4,7 @@ Startup="OnStartup" Exit="OnExit"> + + + + + - - + - - - - - +