diff --git a/Source/GlobalAssemblyInfo.cs b/Source/GlobalAssemblyInfo.cs
index 8d752ce11e..2c3b62ecf7 100644
--- a/Source/GlobalAssemblyInfo.cs
+++ b/Source/GlobalAssemblyInfo.cs
@@ -6,5 +6,5 @@
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("2025.10.18.0")]
-[assembly: AssemblyFileVersion("2025.10.18.0")]
+[assembly: AssemblyVersion("2025.11.1.0")]
+[assembly: AssemblyFileVersion("2025.11.1.0")]
diff --git a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs
index 9cfdb253aa..7d7b7c174b 100644
--- a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs
+++ b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs
@@ -2756,11 +2756,11 @@ public static string DeleteProfileFile {
}
///
- /// Looks up a localized string similar to Selected profile file will be deleted permanently..
+ /// Looks up a localized string similar to Profile file "{0}" will be deleted permanently..
///
- public static string DeleteProfileFileMessage {
+ public static string DeleteProfileFileXMessage {
get {
- return ResourceManager.GetString("DeleteProfileFileMessage", resourceCulture);
+ return ResourceManager.GetString("DeleteProfileFileXMessage", resourceCulture);
}
}
@@ -3422,6 +3422,27 @@ public static string EnableEncryptionDots {
}
}
+ ///
+ /// Looks up a localized string similar to Do you want to enable profile file encryption to protect sensitive data such as hosts, IP addresses, URLs, and stored credentials?
+ ///
+ ///You can enable or disable encryption later at any time by right-clicking the profile file to manage encryption settings.
+ ///If you click Cancel, the profile file will remain unencrypted..
+ ///
+ public static string EnableEncryptionForProfileFileMessage {
+ get {
+ return ResourceManager.GetString("EnableEncryptionForProfileFileMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Enable encryption?.
+ ///
+ public static string EnableEncryptionQuestion {
+ get {
+ return ResourceManager.GetString("EnableEncryptionQuestion", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Enable entry.
///
diff --git a/Source/NETworkManager.Localization/Resources/Strings.resx b/Source/NETworkManager.Localization/Resources/Strings.resx
index ec8798c515..df3fb92241 100644
--- a/Source/NETworkManager.Localization/Resources/Strings.resx
+++ b/Source/NETworkManager.Localization/Resources/Strings.resx
@@ -2367,9 +2367,6 @@ $$hostname$$ --> Hostname
Delete profile file
-
- Selected profile file will be deleted permanently.
-
All profiles in this profile file will be permanently deleted!
@@ -3999,4 +3996,16 @@ Right-click for more options.
Admin (console) session
+
+ Profile file "{0}" will be deleted permanently.
+
+
+ Enable encryption?
+
+
+ Do you want to enable profile file encryption to protect sensitive data such as hosts, IP addresses, URLs, and stored credentials?
+
+You can enable or disable encryption later at any time by right-clicking the profile file to manage encryption settings.
+If you click Cancel, the profile file will remain unencrypted.
+
\ No newline at end of file
diff --git a/Source/NETworkManager.Setup/NETworkManager.Setup.wixproj b/Source/NETworkManager.Setup/NETworkManager.Setup.wixproj
index eb0f19acfd..a64ab50855 100644
--- a/Source/NETworkManager.Setup/NETworkManager.Setup.wixproj
+++ b/Source/NETworkManager.Setup/NETworkManager.Setup.wixproj
@@ -1,4 +1,4 @@
-
+
@@ -7,7 +7,7 @@
-
+
\ No newline at end of file
diff --git a/Source/NETworkManager/ViewModels/SettingsProfilesViewModel.cs b/Source/NETworkManager/ViewModels/SettingsProfilesViewModel.cs
index e9fc53d933..1290260821 100644
--- a/Source/NETworkManager/ViewModels/SettingsProfilesViewModel.cs
+++ b/Source/NETworkManager/ViewModels/SettingsProfilesViewModel.cs
@@ -82,6 +82,8 @@ public SettingsProfilesViewModel(IDialogCoordinator instance)
ProfileFiles.SortDescriptions.Add(
new SortDescription(nameof(ProfileFileInfo.Name), ListSortDirection.Ascending));
+ SelectedProfileFile = ProfileFiles.Cast().FirstOrDefault();
+
LoadSettings();
}
@@ -101,52 +103,113 @@ private static void OpenLocationAction()
Process.Start("explorer.exe", ProfileManager.GetProfilesFolderLocation());
}
- public ICommand AddProfileFileCommand => new RelayCommand(_ => AddProfileFileAction());
+ public ICommand AddProfileFileCommand => new RelayCommand(async _ => await AddProfileFileAction().ConfigureAwait(false));
- private async void AddProfileFileAction()
+ private async Task AddProfileFileAction()
{
- var customDialog = new CustomDialog
- {
- Title = Strings.AddProfileFile
- };
+ var profileName = string.Empty;
+
+ var childWindow = new ProfileFileChildWindow();
- var profileFileViewModel = new ProfileFileViewModel(async instance =>
+ var childWindowViewModel = new ProfileFileViewModel(instance =>
{
- await _dialogCoordinator.HideMetroDialogAsync(this, customDialog);
+ childWindow.IsOpen = false;
+ ConfigurationManager.Current.IsChildWindowOpen = false;
+
+ profileName = instance.Name;
ProfileManager.CreateEmptyProfileFile(instance.Name);
- }, async _ => { await _dialogCoordinator.HideMetroDialogAsync(this, customDialog); });
+ }, _ =>
+ {
+ childWindow.IsOpen = false;
+ ConfigurationManager.Current.IsChildWindowOpen = false;
+ });
+
+ childWindow.Title = Strings.AddProfileFile;
+
+ childWindow.DataContext = childWindowViewModel;
+
+ ConfigurationManager.Current.IsChildWindowOpen = true;
+
+ await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow);
+
+ if (string.IsNullOrEmpty(profileName))
+ return;
+
+ SelectedProfileFile = ProfileFiles.Cast()
+ .FirstOrDefault(p => p.Name.Equals(profileName, StringComparison.OrdinalIgnoreCase));
- customDialog.Content = new ProfileFileDialog
+ // Ask to enable encryption for the new profile file
+ if (await ShowEnableEncryptionMessage())
+ EnableEncryptionAction();
+ }
+
+ private async Task ShowEnableEncryptionMessage()
+ {
+ var result = false;
+
+ var childWindow = new OKCancelInfoMessageChildWindow();
+
+ var childWindowViewModel = new OKCancelInfoMessageViewModel(_ =>
{
- DataContext = profileFileViewModel
- };
+ childWindow.IsOpen = false;
+ ConfigurationManager.Current.IsChildWindowOpen = false;
- await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog);
+ result = true;
+ }, _ =>
+ {
+ childWindow.IsOpen = false;
+ ConfigurationManager.Current.IsChildWindowOpen = false;
+ },
+ Strings.EnableEncryptionForProfileFileMessage
+ );
+
+ childWindow.Title = Strings.EnableEncryptionQuestion;
+
+ childWindow.DataContext = childWindowViewModel;
+
+ ConfigurationManager.Current.IsChildWindowOpen = true;
+
+ await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow);
+
+ return result;
}
- public ICommand EditProfileFileCommand => new RelayCommand(_ => EditProfileFileAction());
+ public ICommand EditProfileFileCommand => new RelayCommand(async _ => await EditProfileFileAction().ConfigureAwait(false));
- private async void EditProfileFileAction()
+ private async Task EditProfileFileAction()
{
- var customDialog = new CustomDialog
- {
- Title = Strings.EditProfileFile
- };
+ var profileName = string.Empty;
+
+ var childWindow = new ProfileFileChildWindow();
- var profileFileViewModel = new ProfileFileViewModel(async instance =>
+ var childWindowViewModel = new ProfileFileViewModel(instance =>
{
- await _dialogCoordinator.HideMetroDialogAsync(this, customDialog);
+ childWindow.IsOpen = false;
+ ConfigurationManager.Current.IsChildWindowOpen = false;
- ProfileManager.RenameProfileFile(SelectedProfileFile, instance.Name);
- }, async _ => { await _dialogCoordinator.HideMetroDialogAsync(this, customDialog); }, SelectedProfileFile);
+ profileName = instance.Name;
- customDialog.Content = new ProfileFileDialog
+ ProfileManager.RenameProfileFile(SelectedProfileFile, instance.Name);
+ }, _ =>
{
- DataContext = profileFileViewModel
- };
+ childWindow.IsOpen = false;
+ ConfigurationManager.Current.IsChildWindowOpen = false;
+ }, SelectedProfileFile);
- await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog);
+ childWindow.Title = Strings.EditProfileFile;
+
+ childWindow.DataContext = childWindowViewModel;
+
+ ConfigurationManager.Current.IsChildWindowOpen = true;
+
+ await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow);
+
+ if (string.IsNullOrEmpty(profileName))
+ return;
+
+ SelectedProfileFile = ProfileFiles.Cast()
+ .FirstOrDefault(p => p.Name.Equals(profileName, StringComparison.OrdinalIgnoreCase));
}
public ICommand DeleteProfileFileCommand =>
@@ -172,7 +235,7 @@ private Task DeleteProfileFileAction()
childWindow.IsOpen = false;
ConfigurationManager.Current.IsChildWindowOpen = false;
},
- Strings.DeleteProfileFileMessage, Strings.Delete);
+ string.Format(Strings.DeleteProfileFileXMessage, SelectedProfileFile.Name), Strings.Delete);
childWindow.Title = Strings.DeleteProfileFile;
@@ -187,13 +250,7 @@ private Task DeleteProfileFileAction()
private async void EnableEncryptionAction()
{
- var settings = AppearanceManager.MetroDialog;
-
- settings.AffirmativeButtonText = Strings.OK;
- settings.NegativeButtonText = Strings.Cancel;
- settings.DefaultButtonFocus = MessageDialogResult.Affirmative;
-
- if (await _dialogCoordinator.ShowMessageAsync(this, Strings.Disclaimer, Strings.ProfileEncryptionDisclaimer, MessageDialogStyle.AffirmativeAndNegative, settings) != MessageDialogResult.Affirmative)
+ if (!await ShowEncryptionDisclaimerAsync())
return;
var customDialog = new CustomDialog
@@ -228,6 +285,37 @@ await _dialogCoordinator.ShowMessageAsync(this, Strings.EncryptionError,
await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog);
}
+ private async Task ShowEncryptionDisclaimerAsync()
+ {
+ var result = false;
+
+ var childWindow = new OKCancelInfoMessageChildWindow();
+
+ var childWindowViewModel = new OKCancelInfoMessageViewModel(_ =>
+ {
+ childWindow.IsOpen = false;
+ ConfigurationManager.Current.IsChildWindowOpen = false;
+
+ result = true;
+ }, _ =>
+ {
+ childWindow.IsOpen = false;
+ ConfigurationManager.Current.IsChildWindowOpen = false;
+ },
+ Strings.ProfileEncryptionDisclaimer
+ );
+
+ childWindow.Title = Strings.Disclaimer;
+
+ childWindow.DataContext = childWindowViewModel;
+
+ ConfigurationManager.Current.IsChildWindowOpen = true;
+
+ await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow);
+
+ return result;
+ }
+
public ICommand ChangeMasterPasswordCommand => new RelayCommand(_ => ChangeMasterPasswordAction());
private async void ChangeMasterPasswordAction()
diff --git a/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml b/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml
index 9a0514cd2b..480e739d72 100644
--- a/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml
+++ b/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml
@@ -8,7 +8,7 @@
xmlns:simpleChildWindow="clr-namespace:MahApps.Metro.SimpleChildWindow;assembly=MahApps.Metro.SimpleChildWindow"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:converters="clr-namespace:NETworkManager.Converters;assembly=NETworkManager.Converters"
- Loaded="ChildWindow_Loaded"
+ Loaded="ChildWindow_OnLoaded"
Style="{StaticResource DefaultChildWindow}"
mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:OKCancelInfoMessageViewModel}">
diff --git a/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml.cs b/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml.cs
index d0fba4585e..5853058f48 100644
--- a/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml.cs
+++ b/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml.cs
@@ -10,7 +10,7 @@ public OKCancelInfoMessageChildWindow()
InitializeComponent();
}
- private void ChildWindow_Loaded(object sender, System.Windows.RoutedEventArgs e)
+ private void ChildWindow_OnLoaded(object sender, System.Windows.RoutedEventArgs e)
{
Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(delegate
{
diff --git a/Source/NETworkManager/Views/OKMessageChildWindow.xaml b/Source/NETworkManager/Views/OKMessageChildWindow.xaml
index 2193eddd6b..d743b51e67 100644
--- a/Source/NETworkManager/Views/OKMessageChildWindow.xaml
+++ b/Source/NETworkManager/Views/OKMessageChildWindow.xaml
@@ -8,14 +8,13 @@
xmlns:simpleChildWindow="clr-namespace:MahApps.Metro.SimpleChildWindow;assembly=MahApps.Metro.SimpleChildWindow"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:converters="clr-namespace:NETworkManager.Converters;assembly=NETworkManager.Converters"
- Loaded="ChildWindow_Loaded"
+ Loaded="ChildWindow_OnLoaded"
Style="{StaticResource DefaultChildWindow}"
mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:OKMessageViewModel}">
-
-
+
diff --git a/Source/NETworkManager/Views/OKMessageChildWindow.xaml.cs b/Source/NETworkManager/Views/OKMessageChildWindow.xaml.cs
index a6a48d5e49..b4a07654d1 100644
--- a/Source/NETworkManager/Views/OKMessageChildWindow.xaml.cs
+++ b/Source/NETworkManager/Views/OKMessageChildWindow.xaml.cs
@@ -10,7 +10,7 @@ public OKMessageChildWindow()
InitializeComponent();
}
- private void ChildWindow_Loaded(object sender, System.Windows.RoutedEventArgs e)
+ private void ChildWindow_OnLoaded(object sender, System.Windows.RoutedEventArgs e)
{
Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(delegate
{
diff --git a/Source/NETworkManager/Views/ProfileFileDialog.xaml b/Source/NETworkManager/Views/ProfileFileChildWindow.xaml
similarity index 66%
rename from Source/NETworkManager/Views/ProfileFileDialog.xaml
rename to Source/NETworkManager/Views/ProfileFileChildWindow.xaml
index 3bcb7756b1..d990e6dc9c 100644
--- a/Source/NETworkManager/Views/ProfileFileDialog.xaml
+++ b/Source/NETworkManager/Views/ProfileFileChildWindow.xaml
@@ -1,15 +1,20 @@
-
-
+
+
@@ -30,7 +35,7 @@
+ mah:TextBoxHelper.Watermark="{x:Static localization:Strings.ExampleProfileFileName}">
@@ -51,7 +56,7 @@
+ Value="False" />
@@ -65,7 +70,7 @@
+ Style="{StaticResource DefaultButton}" />
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/Source/NETworkManager/Views/ProfileFileChildWindow.xaml.cs b/Source/NETworkManager/Views/ProfileFileChildWindow.xaml.cs
new file mode 100644
index 0000000000..e883e3e8ea
--- /dev/null
+++ b/Source/NETworkManager/Views/ProfileFileChildWindow.xaml.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Windows;
+using System.Windows.Threading;
+
+namespace NETworkManager.Views;
+
+public partial class ProfileFileChildWindow
+{
+ public ProfileFileChildWindow()
+ {
+ InitializeComponent();
+ }
+
+ private void ChildWindow_OnLoaded(object sender, RoutedEventArgs e)
+ {
+ Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(delegate
+ {
+ TextBoxName.Focus();
+ }));
+ }
+}
\ No newline at end of file
diff --git a/Source/NETworkManager/Views/ProfileFileDialog.xaml.cs b/Source/NETworkManager/Views/ProfileFileDialog.xaml.cs
deleted file mode 100644
index aa6981cd74..0000000000
--- a/Source/NETworkManager/Views/ProfileFileDialog.xaml.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System.Windows;
-
-namespace NETworkManager.Views;
-
-public partial class ProfileFileDialog
-{
- public ProfileFileDialog()
- {
- InitializeComponent();
- }
-
- private void UserControl_Loaded(object sender, RoutedEventArgs e)
- {
- TextBoxName.Focus();
- }
-}
\ No newline at end of file
diff --git a/Source/NETworkManager/Views/UpgradeChildWindow.xaml b/Source/NETworkManager/Views/UpgradeChildWindow.xaml
index 08eea116ab..d413a48f42 100644
--- a/Source/NETworkManager/Views/UpgradeChildWindow.xaml
+++ b/Source/NETworkManager/Views/UpgradeChildWindow.xaml
@@ -14,7 +14,7 @@
CloseByEscape="False"
Padding="20"
OverlayBrush="{DynamicResource ResourceKey=MahApps.Brushes.Gray8}"
- Loaded="ChildWindow_Loaded"
+ Loaded="ChildWindow_OnLoaded"
mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:UpgradeViewModel}">
diff --git a/Source/NETworkManager/Views/UpgradeChildWindow.xaml.cs b/Source/NETworkManager/Views/UpgradeChildWindow.xaml.cs
index d7a4499319..c67d28efdd 100644
--- a/Source/NETworkManager/Views/UpgradeChildWindow.xaml.cs
+++ b/Source/NETworkManager/Views/UpgradeChildWindow.xaml.cs
@@ -10,7 +10,7 @@ public UpgradeChildWindow()
InitializeComponent();
}
- private void ChildWindow_Loaded(object sender, System.Windows.RoutedEventArgs e)
+ private void ChildWindow_OnLoaded(object sender, System.Windows.RoutedEventArgs e)
{
Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(delegate
{
diff --git a/Source/NETworkManager/Views/WelcomeChildWindow.xaml b/Source/NETworkManager/Views/WelcomeChildWindow.xaml
index c1edab2131..937838428f 100644
--- a/Source/NETworkManager/Views/WelcomeChildWindow.xaml
+++ b/Source/NETworkManager/Views/WelcomeChildWindow.xaml
@@ -15,7 +15,7 @@
CloseByEscape="False"
Padding="20"
OverlayBrush="{DynamicResource MahApps.Brushes.Gray8}"
- Loaded="ChildWindow_Loaded"
+ Loaded="ChildWindow_OnLoaded"
mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:WelcomeViewModel}">
diff --git a/Source/NETworkManager/Views/WelcomeChildWindow.xaml.cs b/Source/NETworkManager/Views/WelcomeChildWindow.xaml.cs
index 4ac9b4075a..b79451893f 100644
--- a/Source/NETworkManager/Views/WelcomeChildWindow.xaml.cs
+++ b/Source/NETworkManager/Views/WelcomeChildWindow.xaml.cs
@@ -10,7 +10,7 @@ public WelcomeChildWindow()
InitializeComponent();
}
- private void ChildWindow_Loaded(object sender, System.Windows.RoutedEventArgs e)
+ private void ChildWindow_OnLoaded(object sender, System.Windows.RoutedEventArgs e)
{
Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(delegate
{
diff --git a/Website/docs/changelog/next-release.md b/Website/docs/changelog/next-release.md
index ad3a9ca3c4..61e52ecca2 100644
--- a/Website/docs/changelog/next-release.md
+++ b/Website/docs/changelog/next-release.md
@@ -25,6 +25,11 @@ Release date: **xx.xx.2025**
## Improvements
+**Profiles**
+
+- Profile file creation flow improved — when adding a new profile you are now prompted to enable profile-file encryption to protect stored credentials and settings. [#3227](https://github.com/BornToBeRoot/NETworkManager/pull/3227)
+- Profile file dialog migrated to a child window to improve usability. [#3227](https://github.com/BornToBeRoot/NETworkManager/pull/3227)
+
**Remote Desktop**
- Redesign RDP connect dialog (migrated from dialog to child window). [#3216](https://github.com/BornToBeRoot/NETworkManager/pull/3216)