Listen to WM_SETTINGSCHANGED

Add Infobar reload button
This commit is contained in:
Stefan Markovic
2023-09-22 13:14:13 +02:00
parent 548e79d76b
commit 00ab5ca2e0
13 changed files with 222 additions and 115 deletions

View File

@@ -48,7 +48,7 @@ namespace EnvironmentVariables
services.AddSingleton<IElevationHelper, ElevationHelper>(); services.AddSingleton<IElevationHelper, ElevationHelper>();
services.AddSingleton<IEnvironmentVariablesService, EnvironmentVariablesService>(); services.AddSingleton<IEnvironmentVariablesService, EnvironmentVariablesService>();
services.AddTransient<MainViewModel>(); services.AddSingleton<MainViewModel>();
}).Build(); }).Build();
UnhandledException += App_UnhandledException; UnhandledException += App_UnhandledException;

View File

@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using EnvironmentVariables.Models;
using Microsoft.UI.Xaml.Data;
namespace EnvironmentVariables.Converters;
public class EnvironmentStateToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var type = (EnvironmentState)value;
return type switch
{
EnvironmentState.Unchanged => false,
_ => true,
};
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}

View File

@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using EnvironmentVariables.Helpers;
using EnvironmentVariables.Models;
using Microsoft.UI.Xaml.Data;
namespace EnvironmentVariables.Converters;
public class EnvironmentStateToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var resourceLoader = ResourceLoaderInstance.ResourceLoader;
var type = (EnvironmentState)value;
return type switch
{
EnvironmentState.Unchanged => string.Empty,
EnvironmentState.ChangedOnStartup => resourceLoader.GetString("StateNotUpToDateOnStartupMsg"),
EnvironmentState.EnvironmentMessageRecieved => resourceLoader.GetString("StateNotUpToDateEnvironmentMessageRecievedMsg"),
_ => throw new NotImplementedException(),
};
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}

View File

@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using EnvironmentVariables.Models;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
namespace EnvironmentVariables.Converters;
public class EnvironmentStateToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var type = (EnvironmentState)value;
return type switch
{
EnvironmentState.Unchanged => Visibility.Collapsed,
_ => Visibility.Visible,
};
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}

View File

@@ -5,6 +5,9 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using EnvironmentVariables.Helpers; using EnvironmentVariables.Helpers;
using EnvironmentVariables.Helpers.Win32;
using EnvironmentVariables.ViewModels;
using Microsoft.UI.Dispatching;
using WinUIEx; using WinUIEx;
namespace EnvironmentVariables namespace EnvironmentVariables
@@ -30,40 +33,36 @@ namespace EnvironmentVariables
RegisterWindow(); RegisterWindow();
} }
private static WinProc newWndProc; private static readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
private static NativeMethods.WinProc newWndProc;
private static IntPtr oldWndProc = IntPtr.Zero; private static IntPtr oldWndProc = IntPtr.Zero;
private delegate IntPtr WinProc(IntPtr hWnd, WindowMessage msg, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll")]
internal static extern int GetDpiForWindow(IntPtr hwnd);
[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
private static extern int SetWindowLong32(IntPtr hWnd, WindowLongIndexFlags nIndex, WinProc newProc);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, WindowLongIndexFlags nIndex, WinProc newProc);
[DllImport("user32.dll")]
private static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, WindowMessage msg, IntPtr wParam, IntPtr lParam);
private void RegisterWindow() private void RegisterWindow()
{ {
newWndProc = new WinProc(WndProc); newWndProc = new NativeMethods.WinProc(WndProc);
var handle = this.GetWindowHandle(); var handle = this.GetWindowHandle();
oldWndProc = SetWindowLongPtr(handle, WindowLongIndexFlags.GWL_WNDPROC, newWndProc); oldWndProc = NativeMethods.SetWindowLongPtr(handle, NativeMethods.WindowLongIndexFlags.GWL_WNDPROC, newWndProc);
} }
private static IntPtr WndProc(IntPtr hWnd, WindowMessage msg, IntPtr wParam, IntPtr lParam) private static IntPtr WndProc(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam)
{ {
switch (msg) switch (msg)
{ {
case WindowMessage.WM_SETTINGSCHANGED: case NativeMethods.WindowMessage.WM_SETTINGSCHANGED:
{ {
var asd = Marshal.PtrToStringUTF8(lParam); var lParamStr = Marshal.PtrToStringUTF8(lParam);
_ = asd.Substring(0, asd.Length - 1); if (lParamStr == "Environment")
{
// Do not react on self - not nice, re-check this
if (wParam != (IntPtr)0x12345)
{
var viewModel = App.GetService<MainViewModel>();
viewModel.IsStateModified = Models.EnvironmentState.EnvironmentMessageRecieved;
}
}
break; break;
} }
@@ -71,30 +70,7 @@ namespace EnvironmentVariables
break; break;
} }
return CallWindowProc(oldWndProc, hWnd, msg, wParam, lParam); return NativeMethods.CallWindowProc(oldWndProc, hWnd, msg, wParam, lParam);
}
[Flags]
private enum WindowLongIndexFlags : int
{
GWL_WNDPROC = -4,
}
private static IntPtr SetWindowLongPtr(IntPtr hWnd, WindowLongIndexFlags nIndex, WinProc newProc)
{
if (IntPtr.Size == 8)
{
return SetWindowLongPtr64(hWnd, nIndex, newProc);
}
else
{
return new IntPtr(SetWindowLong32(hWnd, nIndex, newProc));
}
}
private enum WindowMessage : int
{
WM_SETTINGSCHANGED = 0x001A,
} }
} }
} }

View File

@@ -41,6 +41,9 @@
</DataTemplate> </DataTemplate>
<converters:VariableTypeToGlyphConverter x:Key="VariableTypeToGlyphConverter" /> <converters:VariableTypeToGlyphConverter x:Key="VariableTypeToGlyphConverter" />
<converters:EnvironmentStateToBoolConverter x:Key="EnvironmentStateToBoolConverter" />
<converters:EnvironmentStateToStringConverter x:Key="EnvironmentStateToStringConverter" />
<converters:EnvironmentStateToVisibilityConverter x:Key="EnvironmentStateToVisibilityConverter" />
<converters1:BoolToVisibilityConverter <converters1:BoolToVisibilityConverter
x:Key="BoolToInvertedVisibilityConverter" x:Key="BoolToInvertedVisibilityConverter"
@@ -68,13 +71,6 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<!-- content --> <!-- content -->
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Grid.Row="1">
<InfoBar
x:Uid="StateNotUpToDateInfoBar"
IsOpen="{x:Bind ViewModel.IsStateModified, Mode=TwoWay}"
Severity="Warning"
Visibility="{x:Bind ViewModel.IsStateModified, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}}" />
</Grid>
<!-- buttons --> <!-- buttons -->
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
@@ -91,8 +87,26 @@
<KeyboardAccelerator Key="N" Modifiers="Control" /> <KeyboardAccelerator Key="N" Modifiers="Control" />
</Button.KeyboardAccelerators> </Button.KeyboardAccelerators>
</Button> </Button>
<ProgressRing IsActive="{x:Bind ViewModel.ApplyingChanges, Mode=TwoWay}" />
</StackPanel> </StackPanel>
<Grid Grid.Row="1">
<InfoBar
x:Name="StateNotUpToDateInfoBar"
x:Uid="StateNotUpToDateInfoBar"
IsOpen="{x:Bind ViewModel.IsStateModified, Mode=OneWay, Converter={StaticResource EnvironmentStateToBoolConverter}}"
Message="{x:Bind ViewModel.IsStateModified, Mode=OneWay, Converter={StaticResource EnvironmentStateToStringConverter}}"
Severity="Warning"
Visibility="{x:Bind ViewModel.IsStateModified, Mode=OneWay, Converter={StaticResource EnvironmentStateToVisibilityConverter}}">
<InfoBar.ActionButton>
<Button
Click="ReloadButton_Click"
Content="{ui:FontIcon Glyph=&#xe72c;}"
Style="{StaticResource SubtleButtonStyle}"
Visibility="{x:Bind ViewModel.IsInfoBarButtonVisible, Mode=OneWay}" />
</InfoBar.ActionButton>
</InfoBar>
</Grid>
<Grid <Grid
Grid.Row="2" Grid.Row="2"
Margin="0,24,0,0" Margin="0,24,0,0"

View File

@@ -161,5 +161,11 @@ namespace EnvironmentVariables.Views
} }
} }
} }
private void ReloadButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
ViewModel.LoadEnvironmentVariables();
ViewModel.IsStateModified = EnvironmentState.Unchanged;
}
} }
} }

View File

@@ -4,10 +4,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
using EnvironmentVariables.Helpers.Win32; using EnvironmentVariables.Helpers.Win32;
using EnvironmentVariables.Models; using EnvironmentVariables.Models;
using Microsoft.Win32; using Microsoft.Win32;
@@ -77,14 +73,14 @@ namespace EnvironmentVariables.Helpers
} }
} }
private static void NotifyEnvironmentChange() internal static void NotifyEnvironmentChange()
{ {
unsafe unsafe
{ {
// send a WM_SETTINGCHANGE message to all windows // send a WM_SETTINGCHANGE message to all windows
fixed (char* lParam = "Environment") fixed (char* lParam = "Environment")
{ {
_ = NativeMethods.SendNotifyMessage(new IntPtr(NativeMethods.HWND_BROADCAST), NativeMethods.WM_SETTINGCHANGE, IntPtr.Zero, (IntPtr)lParam); _ = NativeMethods.SendNotifyMessage(new IntPtr(NativeMethods.HWND_BROADCAST), NativeMethods.WindowMessage.WM_SETTINGSCHANGED, (IntPtr)0x12345, (IntPtr)lParam);
} }
} }
} }
@@ -108,6 +104,21 @@ namespace EnvironmentVariables.Helpers
} }
} }
internal static bool SetVariableWithoutNotify(Variable variable)
{
bool fromMachine = variable.ParentType switch
{
VariablesSetType.Profile => false,
VariablesSetType.User => false,
VariablesSetType.System => true,
_ => throw new NotImplementedException(),
};
SetEnvironmentVariableFromRegistryWithoutNotify(variable.Name, variable.Values, fromMachine);
return true;
}
internal static bool SetVariable(Variable variable) internal static bool SetVariable(Variable variable)
{ {
bool fromMachine = variable.ParentType switch bool fromMachine = variable.ParentType switch
@@ -124,9 +135,9 @@ namespace EnvironmentVariables.Helpers
return true; return true;
} }
internal static bool SetVariables(List<Variable> variables, VariablesSetType type) internal static bool UnsetVariableWithoutNotify(Variable variable)
{ {
bool fromMachine = type switch bool fromMachine = variable.ParentType switch
{ {
VariablesSetType.Profile => false, VariablesSetType.Profile => false,
VariablesSetType.User => false, VariablesSetType.User => false,
@@ -134,12 +145,7 @@ namespace EnvironmentVariables.Helpers
_ => throw new NotImplementedException(), _ => throw new NotImplementedException(),
}; };
foreach (Variable variable in variables) SetEnvironmentVariableFromRegistryWithoutNotify(variable.Name, null, fromMachine);
{
SetEnvironmentVariableFromRegistryWithoutNotify(variable.Name, variable.Values, fromMachine);
}
NotifyEnvironmentChange();
return true; return true;
} }
@@ -159,25 +165,5 @@ namespace EnvironmentVariables.Helpers
return true; return true;
} }
internal static bool UnsetVariables(List<Variable> variables, VariablesSetType type)
{
bool fromMachine = type switch
{
VariablesSetType.Profile => false,
VariablesSetType.User => false,
VariablesSetType.System => true,
_ => throw new NotImplementedException(),
};
foreach (Variable variable in variables)
{
SetEnvironmentVariableFromRegistryWithoutNotify(variable.Name, null, fromMachine);
}
NotifyEnvironmentChange();
return true;
}
} }
} }

View File

@@ -11,9 +11,44 @@ namespace EnvironmentVariables.Helpers.Win32
{ {
internal const int HWND_BROADCAST = 0xffff; internal const int HWND_BROADCAST = 0xffff;
internal const int WM_SETTINGCHANGE = 0x001A; internal delegate IntPtr WinProc(IntPtr hWnd, WindowMessage msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern int SendNotifyMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); internal static extern int SendNotifyMessage(IntPtr hWnd, WindowMessage msg, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll")]
internal static extern int GetDpiForWindow(IntPtr hwnd);
[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
internal static extern int SetWindowLong32(IntPtr hWnd, WindowLongIndexFlags nIndex, WinProc newProc);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
internal static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, WindowLongIndexFlags nIndex, WinProc newProc);
[DllImport("user32.dll")]
internal static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, WindowMessage msg, IntPtr wParam, IntPtr lParam);
[Flags]
internal enum WindowLongIndexFlags : int
{
GWL_WNDPROC = -4,
}
internal enum WindowMessage : int
{
WM_SETTINGSCHANGED = 0x001A,
}
internal static IntPtr SetWindowLongPtr(IntPtr hWnd, WindowLongIndexFlags nIndex, WinProc newProc)
{
if (IntPtr.Size == 8)
{
return SetWindowLongPtr64(hWnd, nIndex, newProc);
}
else
{
return new IntPtr(SetWindowLong32(hWnd, nIndex, newProc));
}
}
} }
} }

View File

@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace EnvironmentVariables.Models
{
public enum EnvironmentState
{
Unchanged = 0,
ChangedOnStartup,
EnvironmentMessageRecieved,
}
}

View File

@@ -44,19 +44,19 @@ namespace EnvironmentVariables.Models
variableToOverride.Name = EnvironmentVariablesHelper.GetBackupVariableName(variableToOverride, this.Name); variableToOverride.Name = EnvironmentVariablesHelper.GetBackupVariableName(variableToOverride, this.Name);
// Backup the variable // Backup the variable
if (!EnvironmentVariablesHelper.SetVariable(variableToOverride)) if (!EnvironmentVariablesHelper.SetVariableWithoutNotify(variableToOverride))
{ {
Logger.LogError("Failed to set backup variable."); Logger.LogError("Failed to set backup variable.");
} }
} }
if (!EnvironmentVariablesHelper.SetVariable(variable)) if (!EnvironmentVariablesHelper.SetVariableWithoutNotify(variable))
{ {
Logger.LogError("Failed to set profile variable."); Logger.LogError("Failed to set profile variable.");
} }
} }
// viewModel.ApplyingChanges = false; EnvironmentVariablesHelper.NotifyEnvironmentChange();
}); });
} }
@@ -64,12 +64,10 @@ namespace EnvironmentVariables.Models
{ {
return Task.Run(() => return Task.Run(() =>
{ {
var viewModel = App.GetService<MainViewModel>();
viewModel.ApplyingChanges = true;
foreach (var variable in Variables) foreach (var variable in Variables)
{ {
// Unset the variable // Unset the variable
if (!EnvironmentVariablesHelper.UnsetVariable(variable)) if (!EnvironmentVariablesHelper.UnsetVariableWithoutNotify(variable))
{ {
Logger.LogError("Failed to unset variable."); Logger.LogError("Failed to unset variable.");
} }
@@ -84,19 +82,19 @@ namespace EnvironmentVariables.Models
{ {
var variableToRestore = new Variable(originalName, backupVariable.Values, backupVariable.ParentType); var variableToRestore = new Variable(originalName, backupVariable.Values, backupVariable.ParentType);
if (!EnvironmentVariablesHelper.UnsetVariable(backupVariable)) if (!EnvironmentVariablesHelper.UnsetVariableWithoutNotify(backupVariable))
{ {
Logger.LogError("Failed to unset backup variable."); Logger.LogError("Failed to unset backup variable.");
} }
if (!EnvironmentVariablesHelper.SetVariable(variableToRestore)) if (!EnvironmentVariablesHelper.SetVariableWithoutNotify(variableToRestore))
{ {
Logger.LogError("Failed to restore backup variable."); Logger.LogError("Failed to restore backup variable.");
} }
} }
} }
viewModel.ApplyingChanges = false; EnvironmentVariablesHelper.NotifyEnvironmentChange();
}); });
} }

View File

@@ -221,10 +221,13 @@
<data name="StateNotUpToDateInfoBar.Title" xml:space="preserve"> <data name="StateNotUpToDateInfoBar.Title" xml:space="preserve">
<value>Changes were made outside of this app.</value> <value>Changes were made outside of this app.</value>
</data> </data>
<data name="StateNotUpToDateInfoBar.Message" xml:space="preserve"> <data name="StateNotUpToDateOnStartupMsg" xml:space="preserve">
<value>Variables included in applied profile have been modified. Review the latest changes before applying the profile again.</value> <value>Variables included in applied profile have been modified. Review the latest changes before applying the profile again.</value>
</data> </data>
<data name="CancelBtn" xml:space="preserve"> <data name="CancelBtn" xml:space="preserve">
<value>Cancel</value> <value>Cancel</value>
</data> </data>
<data name="StateNotUpToDateEnvironmentMessageRecievedMsg" xml:space="preserve">
<value>Variables have been modified. Reload to get the latest changes.</value>
</data>
</root> </root>

View File

@@ -8,14 +8,12 @@ using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Linq;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using EnvironmentVariables.Helpers; using EnvironmentVariables.Helpers;
using EnvironmentVariables.Models; using EnvironmentVariables.Models;
using ManagedCommon; using ManagedCommon;
using Microsoft.UI.Dispatching; using Microsoft.UI.Dispatching;
using Windows.Foundation.Collections;
namespace EnvironmentVariables.ViewModels namespace EnvironmentVariables.ViewModels
{ {
@@ -41,10 +39,10 @@ namespace EnvironmentVariables.ViewModels
private bool _isElevated; private bool _isElevated;
[ObservableProperty] [ObservableProperty]
private bool _isStateModified; [NotifyPropertyChangedFor(nameof(IsInfoBarButtonVisible))]
private EnvironmentState _isStateModified;
[ObservableProperty] public bool IsInfoBarButtonVisible => IsStateModified == EnvironmentState.EnvironmentMessageRecieved;
private bool _applyingChanges;
public ProfileVariablesSet AppliedProfile { get; set; } public ProfileVariablesSet AppliedProfile { get; set; }
@@ -105,11 +103,11 @@ namespace EnvironmentVariables.ViewModels
if (appliedProfile.IsCorrectlyApplied()) if (appliedProfile.IsCorrectlyApplied())
{ {
AppliedProfile = appliedProfile; AppliedProfile = appliedProfile;
IsStateModified = false; IsStateModified = EnvironmentState.Unchanged;
} }
else else
{ {
IsStateModified = true; IsStateModified = EnvironmentState.ChangedOnStartup;
appliedProfile.IsEnabled = false; appliedProfile.IsEnabled = false;
} }
} }
@@ -144,13 +142,11 @@ namespace EnvironmentVariables.ViewModels
bool changed = original.Name != edited.Name || original.Values != edited.Values; bool changed = original.Name != edited.Name || original.Values != edited.Values;
if (changed) if (changed)
{ {
ApplyingChanges = true;
var task = original.Update(edited, propagateChange); var task = original.Update(edited, propagateChange);
task.ContinueWith(x => task.ContinueWith(x =>
{ {
_dispatcherQueue.TryEnqueue(() => _dispatcherQueue.TryEnqueue(() =>
{ {
ApplyingChanges = false;
PopulateAppliedVariables(); PopulateAppliedVariables();
}); });
}); });
@@ -211,13 +207,11 @@ namespace EnvironmentVariables.ViewModels
private void SetAppliedProfile(ProfileVariablesSet profile) private void SetAppliedProfile(ProfileVariablesSet profile)
{ {
ApplyingChanges = true;
var task = profile.Apply(); var task = profile.Apply();
task.ContinueWith((a) => task.ContinueWith((a) =>
{ {
_dispatcherQueue.TryEnqueue(() => _dispatcherQueue.TryEnqueue(() =>
{ {
ApplyingChanges = false;
PopulateAppliedVariables(); PopulateAppliedVariables();
}); });
}); });
@@ -230,13 +224,11 @@ namespace EnvironmentVariables.ViewModels
{ {
var appliedProfile = AppliedProfile; var appliedProfile = AppliedProfile;
appliedProfile.PropertyChanged -= Profile_PropertyChanged; appliedProfile.PropertyChanged -= Profile_PropertyChanged;
ApplyingChanges = true;
var task = AppliedProfile.UnApply(); var task = AppliedProfile.UnApply();
task.ContinueWith((a) => task.ContinueWith((a) =>
{ {
_dispatcherQueue.TryEnqueue(() => _dispatcherQueue.TryEnqueue(() =>
{ {
ApplyingChanges = false;
PopulateAppliedVariables(); PopulateAppliedVariables();
}); });
}); });
@@ -286,7 +278,6 @@ namespace EnvironmentVariables.ViewModels
if (propagateChange) if (propagateChange)
{ {
ApplyingChanges = true;
var task = Task.Run(() => var task = Task.Run(() =>
{ {
EnvironmentVariablesHelper.UnsetVariable(variable); EnvironmentVariablesHelper.UnsetVariable(variable);
@@ -295,7 +286,6 @@ namespace EnvironmentVariables.ViewModels
{ {
_dispatcherQueue.TryEnqueue(() => _dispatcherQueue.TryEnqueue(() =>
{ {
ApplyingChanges = false;
PopulateAppliedVariables(); PopulateAppliedVariables();
}); });
}); });