Кнопка обновления и столбец актуального значения

This commit is contained in:
2026-06-04 19:46:35 +03:00
parent e07fc408eb
commit caac16f88b
7 changed files with 155 additions and 55 deletions

View File

@@ -36,6 +36,16 @@ public sealed class LinuxEnvironmentVariableStore : IEnvironmentVariableStore
return System.Environment.GetEnvironmentVariable(name); return System.Environment.GetEnvironmentVariable(name);
} }
public string? GetUserPersistedValue(string name)
{
if (LoadMergedFromDirectory().TryGetValue(name, out var value))
{
return value;
}
return null;
}
public void Set(string name, string value) public void Set(string name, string value)
{ {
try try

View File

@@ -1,4 +1,5 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Microsoft.Win32;
using Sms.Environment; using Sms.Environment;
namespace Sms.Environment.Windows; namespace Sms.Environment.Windows;
@@ -7,14 +8,23 @@ public sealed class WindowsEnvironmentVariableStore : IEnvironmentVariableStore
{ {
private const int HWND_BROADCAST = 0xffff; private const int HWND_BROADCAST = 0xffff;
private const int WM_SETTINGCHANGE = 0x001A; private const int WM_SETTINGCHANGE = 0x001A;
private const string EnvironmentKeyPath = "Environment";
public string? Get(string name) => public string? Get(string name) =>
System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.User) GetUserPersistedValue(name)
?? System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process); ?? System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);
public string? GetUserPersistedValue(string name)
{
using var key = Registry.CurrentUser.OpenSubKey(EnvironmentKeyPath);
return key?.GetValue(name) as string;
}
public void Set(string name, string value) public void Set(string name, string value)
{ {
System.Environment.SetEnvironmentVariable(name, value, EnvironmentVariableTarget.User); using var key = Registry.CurrentUser.OpenSubKey(EnvironmentKeyPath, writable: true)
?? Registry.CurrentUser.CreateSubKey(EnvironmentKeyPath, writable: true);
key.SetValue(name, value, RegistryValueKind.ExpandString);
System.Environment.SetEnvironmentVariable(name, value, EnvironmentVariableTarget.Process); System.Environment.SetEnvironmentVariable(name, value, EnvironmentVariableTarget.Process);
BroadcastEnvironmentChange(); BroadcastEnvironmentChange();
} }
@@ -39,15 +49,36 @@ public sealed class WindowsEnvironmentVariableStore : IEnvironmentVariableStore
public IReadOnlyDictionary<string, string> GetProcessEnvironment() => public IReadOnlyDictionary<string, string> GetProcessEnvironment() =>
ToDictionary(System.Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process)); ToDictionary(System.Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process));
public IReadOnlyDictionary<string, string> GetUserPersistedEnvironment() => public IReadOnlyDictionary<string, string> GetUserPersistedEnvironment()
ToDictionary(System.Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User)); {
var result = new Dictionary<string, string>(StringComparer.Ordinal);
using var key = Registry.CurrentUser.OpenSubKey(EnvironmentKeyPath);
if (key is null)
{
return result;
}
public bool IsPersistedInUserStore(string name) => foreach (var name in key.GetValueNames())
System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.User) is not null; {
if (key.GetValue(name) is string value)
{
result[name] = value;
}
}
return result;
}
public bool IsPersistedInUserStore(string name)
{
using var key = Registry.CurrentUser.OpenSubKey(EnvironmentKeyPath);
return key?.GetValue(name) is not null;
}
public void RemoveFromUserStore(string name) public void RemoveFromUserStore(string name)
{ {
System.Environment.SetEnvironmentVariable(name, null, EnvironmentVariableTarget.User); using var key = Registry.CurrentUser.OpenSubKey(EnvironmentKeyPath, writable: true);
key?.DeleteValue(name, throwOnMissingValue: false);
System.Environment.SetEnvironmentVariable(name, null, EnvironmentVariableTarget.Process); System.Environment.SetEnvironmentVariable(name, null, EnvironmentVariableTarget.Process);
BroadcastEnvironmentChange(); BroadcastEnvironmentChange();
} }

View File

@@ -4,6 +4,8 @@ public interface IEnvironmentVariableStore
{ {
string? Get(string name); string? Get(string name);
string? GetUserPersistedValue(string name);
void Set(string name, string value); void Set(string name, string value);
bool Exists(string name); bool Exists(string name);

View File

@@ -41,10 +41,18 @@
Click="OnCloseClick" /> Click="OnCloseClick" />
</Grid> </Grid>
<CheckBox Grid.Row="1" <Grid Grid.Row="1"
Margin="12,8,12,0" Margin="12,8,12,0"
Content="Отображать все переменные" ColumnDefinitions="*,Auto"
IsChecked="{Binding ShowAllVariables}" /> ColumnSpacing="8">
<CheckBox Content="Отображать все переменные"
IsChecked="{Binding ShowAllVariables}"
VerticalAlignment="Center" />
<Button Grid.Column="1"
Content="Обновить"
Command="{Binding RefreshCommand}"
MinWidth="100" />
</Grid>
<Grid Grid.Row="2" <Grid Grid.Row="2"
Margin="12,8,12,0" Margin="12,8,12,0"
@@ -92,13 +100,21 @@
<DataGridTextColumn Header="Поле" <DataGridTextColumn Header="Поле"
Binding="{Binding Field}" Binding="{Binding Field}"
IsReadOnly="True" IsReadOnly="True"
Width="2*" /> Width="210"
<DataGridTextColumn Header="Значение" MinWidth="150" />
Binding="{Binding Value, Mode=TwoWay}" <DataGridTextColumn Header="Требуемое значение"
Width="3*" /> Binding="{Binding RequiredValue, Mode=TwoWay}"
Width="2*"
MinWidth="140" />
<DataGridTextColumn Header="Актуальное значение"
Binding="{Binding ActualValueDisplay}"
IsReadOnly="True"
Width="2*"
MinWidth="140" />
<DataGridTextColumn Header="Комментарий" <DataGridTextColumn Header="Комментарий"
Binding="{Binding Comment, Mode=TwoWay}" Binding="{Binding Comment, Mode=TwoWay}"
Width="3*" /> Width="240"
MinWidth="180" />
<DataGridTemplateColumn Header="" <DataGridTemplateColumn Header=""
Width="80"> Width="80">
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>

View File

@@ -49,7 +49,7 @@ public sealed class EnvironmentVariablesService
continue; continue;
} }
var value = ResolveValue(name); var value = ResolveRequiredValue(name);
_store.Set(name, value); _store.Set(name, value);
_log.WriteLine( _log.WriteLine(
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Created configured variable: {name}={value}"); $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Created configured variable: {name}={value}");
@@ -64,7 +64,7 @@ public sealed class EnvironmentVariablesService
foreach (var name in _options.Names) foreach (var name in _options.Names)
{ {
rows.Add(CreateRow(name, ResolveValue(name))); rows.Add(CreateRow(name, ResolveRequiredValue(name)));
knownNames.Add(name); knownNames.Add(name);
} }
@@ -72,7 +72,7 @@ public sealed class EnvironmentVariablesService
{ {
if (knownNames.Add(name)) if (knownNames.Add(name))
{ {
rows.Add(CreateRow(name, ResolveValue(name))); rows.Add(CreateRow(name, ResolveRequiredValue(name)));
} }
} }
@@ -89,7 +89,7 @@ public sealed class EnvironmentVariablesService
continue; continue;
} }
rows.Add(CreateRow(pair.Key, pair.Value)); rows.Add(CreateRow(pair.Key, ResolveRequiredValue(pair.Key)));
} }
return rows; return rows;
@@ -137,14 +137,17 @@ public sealed class EnvironmentVariablesService
public EnvironmentVariableRow GetRowSnapshot(string name) public EnvironmentVariableRow GetRowSnapshot(string name)
{ {
ReloadMetadata(); ReloadMetadata();
return CreateRow(name, ResolveValue(name)); return CreateRow(name, ResolveRequiredValue(name));
} }
public string GetDisplayValue(string name) => ResolveValue(name); public string GetDisplayValue(string name) => ResolveRequiredValue(name);
public string? GetProcessValue(string name) =>
_store.GetProcessEnvironment().TryGetValue(name, out var value) ? value : null;
public void SaveValue(string name, string value) public void SaveValue(string name, string value)
{ {
var previous = _store.Get(name); var previous = _store.GetUserPersistedValue(name);
_store.Set(name, value); _store.Set(name, value);
_log.WriteLine( _log.WriteLine(
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Changed value: {name}={value} (previous: {previous ?? "<none>"})"); $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Changed value: {name}={value} (previous: {previous ?? "<none>"})");
@@ -183,7 +186,7 @@ public sealed class EnvironmentVariablesService
return; return;
} }
var previous = _store.Get(name); var previous = _store.GetUserPersistedValue(name);
_store.RemoveFromUserStore(name); _store.RemoveFromUserStore(name);
_log.WriteLine( _log.WriteLine(
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Removed from user store: {name} (previous: {previous ?? "<none>"})"); $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Removed from user store: {name} (previous: {previous ?? "<none>"})");
@@ -215,14 +218,15 @@ public sealed class EnvironmentVariablesService
string.Equals(name, _options.CommentsVariableName, StringComparison.Ordinal) string.Equals(name, _options.CommentsVariableName, StringComparison.Ordinal)
|| string.Equals(name, _options.CustomVariablesVariableName, StringComparison.Ordinal); || string.Equals(name, _options.CustomVariablesVariableName, StringComparison.Ordinal);
private string ResolveValue(string name) private string ResolveRequiredValue(string name)
{ {
var existing = _store.Get(name); if (_store.GetUserPersistedValue(name) is { } persisted)
if (existing is not null)
{ {
return existing; return persisted;
} }
if (_configuredNames.Contains(name) || _customNames.Contains(name))
{
if (_options.Defaults.TryGetValue(name, out var defaultValue)) if (_options.Defaults.TryGetValue(name, out var defaultValue))
{ {
return defaultValue; return defaultValue;
@@ -231,6 +235,9 @@ public sealed class EnvironmentVariablesService
return string.Empty; return string.Empty;
} }
return string.Empty;
}
private List<string> LoadCustomNames() private List<string> LoadCustomNames()
{ {
var raw = _store.Get(_options.CustomVariablesVariableName); var raw = _store.Get(_options.CustomVariablesVariableName);

View File

@@ -17,6 +17,7 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
IsCustom = row.IsCustom; IsCustom = row.IsCustom;
_service = service; _service = service;
ApplySnapshot(row, suppressSave: true); ApplySnapshot(row, suppressSave: true);
RefreshActualValue(_service.GetProcessValue(Field));
} }
public string Field { get; } public string Field { get; }
@@ -28,10 +29,16 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
[ObservableProperty] [ObservableProperty]
private bool _isPersistedInUserStore; private bool _isPersistedInUserStore;
[ObservableProperty]
private string _actualValue = string.Empty;
public string ActualValueDisplay =>
string.IsNullOrEmpty(ActualValue) ? "<нет>" : ActualValue;
public string UserStoreBadge => IsPersistedInUserStore ? "USER" : string.Empty; public string UserStoreBadge => IsPersistedInUserStore ? "USER" : string.Empty;
[ObservableProperty] [ObservableProperty]
private string _value = string.Empty; private string _requiredValue = string.Empty;
[ObservableProperty] [ObservableProperty]
private string _comment = string.Empty; private string _comment = string.Empty;
@@ -43,30 +50,9 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
RowAppearanceChanged?.Invoke(this, EventArgs.Empty); RowAppearanceChanged?.Invoke(this, EventArgs.Empty);
} }
public event EventHandler? RowAppearanceChanged; partial void OnActualValueChanged(string value) => OnPropertyChanged(nameof(ActualValueDisplay));
public void ApplySnapshot(EnvironmentVariableRow row, bool suppressSave = false) partial void OnRequiredValueChanged(string value)
{
if (suppressSave)
{
BeginLoad();
}
Value = row.Value;
Comment = row.Comment;
IsPersistedInUserStore = row.IsPersistedInUserStore;
if (suppressSave)
{
EndLoad();
}
}
public void BeginLoad() => _isLoading = true;
public void EndLoad() => _isLoading = false;
partial void OnValueChanged(string value)
{ {
if (_isLoading) if (_isLoading)
{ {
@@ -78,8 +64,38 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
{ {
IsPersistedInUserStore = true; IsPersistedInUserStore = true;
} }
RefreshActualValue(_service.GetProcessValue(Field));
} }
public event EventHandler? RowAppearanceChanged;
public void ApplySnapshot(EnvironmentVariableRow row, bool suppressSave = false)
{
if (suppressSave)
{
BeginLoad();
}
RequiredValue = row.Value;
Comment = row.Comment;
IsPersistedInUserStore = row.IsPersistedInUserStore;
if (suppressSave)
{
EndLoad();
}
}
public void RefreshActualValue(string? processValue)
{
ActualValue = processValue ?? string.Empty;
}
public void BeginLoad() => _isLoading = true;
public void EndLoad() => _isLoading = false;
partial void OnCommentChanged(string value) partial void OnCommentChanged(string value)
{ {
if (_isLoading) if (_isLoading)
@@ -103,8 +119,9 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
_service.DeleteFromUserStore(Field); _service.DeleteFromUserStore(Field);
BeginLoad(); BeginLoad();
IsPersistedInUserStore = false; IsPersistedInUserStore = false;
Value = _service.GetDisplayValue(Field); RequiredValue = _service.GetDisplayValue(Field);
EndLoad(); EndLoad();
RefreshActualValue(_service.GetProcessValue(Field));
} }
public event EventHandler? Removed; public event EventHandler? Removed;

View File

@@ -35,6 +35,13 @@ public sealed partial class MainWindowViewModel : ObservableObject
partial void OnNewVariableNameChanged(string value) => AddVariableCommand.NotifyCanExecuteChanged(); partial void OnNewVariableNameChanged(string value) => AddVariableCommand.NotifyCanExecuteChanged();
[RelayCommand]
private void Refresh()
{
SyncRowsFromService();
RefreshProcessStates();
}
[RelayCommand(CanExecute = nameof(CanAddVariable))] [RelayCommand(CanExecute = nameof(CanAddVariable))]
private void AddVariable() private void AddVariable()
{ {
@@ -121,6 +128,16 @@ public sealed partial class MainWindowViewModel : ObservableObject
MoveItem(Rows, currentIndex, targetIndex); MoveItem(Rows, currentIndex, targetIndex);
} }
} }
RefreshProcessStates();
}
private void RefreshProcessStates()
{
foreach (var row in Rows)
{
row.RefreshActualValue(_service.GetProcessValue(row.Field));
}
} }
private static void MoveItem( private static void MoveItem(