Skip to content

Commit 5276e21

Browse files
Feature: Profile file creation enhancements (#3227)
* Feature: Profile file creation enhancements * Docs: #3227 * Docs: #3227 * Apply suggestions from code review Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent 970fbb8 commit 5276e21

17 files changed

+219
-87
lines changed

Source/GlobalAssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
[assembly: AssemblyTrademark("")]
77
[assembly: AssemblyCulture("")]
88

9-
[assembly: AssemblyVersion("2025.10.18.0")]
10-
[assembly: AssemblyFileVersion("2025.10.18.0")]
9+
[assembly: AssemblyVersion("2025.11.1.0")]
10+
[assembly: AssemblyFileVersion("2025.11.1.0")]

Source/NETworkManager.Localization/Resources/Strings.Designer.cs

Lines changed: 24 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Source/NETworkManager.Localization/Resources/Strings.resx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2367,9 +2367,6 @@ $$hostname$$ --&gt; Hostname</value>
23672367
<data name="DeleteProfileFile" xml:space="preserve">
23682368
<value>Delete profile file</value>
23692369
</data>
2370-
<data name="DeleteProfileFileMessage" xml:space="preserve">
2371-
<value>Selected profile file will be deleted permanently.</value>
2372-
</data>
23732370
<data name="ResetProfilesMessage" xml:space="preserve">
23742371
<value>All profiles in this profile file will be permanently deleted!</value>
23752372
</data>
@@ -3999,4 +3996,16 @@ Right-click for more options.</value>
39993996
<data name="AdminConsoleSession" xml:space="preserve">
40003997
<value>Admin (console) session</value>
40013998
</data>
3999+
<data name="DeleteProfileFileXMessage" xml:space="preserve">
4000+
<value>Profile file "{0}" will be deleted permanently.</value>
4001+
</data>
4002+
<data name="EnableEncryptionQuestion" xml:space="preserve">
4003+
<value>Enable encryption?</value>
4004+
</data>
4005+
<data name="EnableEncryptionForProfileFileMessage" xml:space="preserve">
4006+
<value>Do you want to enable profile file encryption to protect sensitive data such as hosts, IP addresses, URLs, and stored credentials?
4007+
4008+
You can enable or disable encryption later at any time by right-clicking the profile file to manage encryption settings.
4009+
If you click Cancel, the profile file will remain unencrypted.</value>
4010+
</data>
40024011
</root>
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="WixToolset.Sdk/5.0.0">
1+
<Project Sdk="WixToolset.Sdk/6.0.2">
22
<ItemGroup>
33
<Content Include="Resources\WixUIBanner.png"/>
44
<Content Include="Resources\WixUIDialog.png"/>
@@ -7,7 +7,7 @@
77
<None Include="Resources\LICENSE.rtf"/>
88
</ItemGroup>
99
<ItemGroup>
10-
<PackageReference Include="WixToolset.UI.wixext" Version="5.0.0"/>
10+
<PackageReference Include="WixToolset.UI.wixext" Version="6.0.2"/>
1111
<ProjectReference Include="..\NETworkManager\NETworkManager.csproj"/>
1212
</ItemGroup>
1313
</Project>

Source/NETworkManager/ViewModels/SettingsProfilesViewModel.cs

Lines changed: 123 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ public SettingsProfilesViewModel(IDialogCoordinator instance)
8282
ProfileFiles.SortDescriptions.Add(
8383
new SortDescription(nameof(ProfileFileInfo.Name), ListSortDirection.Ascending));
8484

85+
SelectedProfileFile = ProfileFiles.Cast<ProfileFileInfo>().FirstOrDefault();
86+
8587
LoadSettings();
8688
}
8789

@@ -101,52 +103,113 @@ private static void OpenLocationAction()
101103
Process.Start("explorer.exe", ProfileManager.GetProfilesFolderLocation());
102104
}
103105

104-
public ICommand AddProfileFileCommand => new RelayCommand(_ => AddProfileFileAction());
106+
public ICommand AddProfileFileCommand => new RelayCommand(async _ => await AddProfileFileAction().ConfigureAwait(false));
105107

106-
private async void AddProfileFileAction()
108+
private async Task AddProfileFileAction()
107109
{
108-
var customDialog = new CustomDialog
109-
{
110-
Title = Strings.AddProfileFile
111-
};
110+
var profileName = string.Empty;
111+
112+
var childWindow = new ProfileFileChildWindow();
112113

113-
var profileFileViewModel = new ProfileFileViewModel(async instance =>
114+
var childWindowViewModel = new ProfileFileViewModel(instance =>
114115
{
115-
await _dialogCoordinator.HideMetroDialogAsync(this, customDialog);
116+
childWindow.IsOpen = false;
117+
ConfigurationManager.Current.IsChildWindowOpen = false;
118+
119+
profileName = instance.Name;
116120

117121
ProfileManager.CreateEmptyProfileFile(instance.Name);
118-
}, async _ => { await _dialogCoordinator.HideMetroDialogAsync(this, customDialog); });
122+
}, _ =>
123+
{
124+
childWindow.IsOpen = false;
125+
ConfigurationManager.Current.IsChildWindowOpen = false;
126+
});
127+
128+
childWindow.Title = Strings.AddProfileFile;
129+
130+
childWindow.DataContext = childWindowViewModel;
131+
132+
ConfigurationManager.Current.IsChildWindowOpen = true;
133+
134+
await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow);
135+
136+
if (string.IsNullOrEmpty(profileName))
137+
return;
138+
139+
SelectedProfileFile = ProfileFiles.Cast<ProfileFileInfo>()
140+
.FirstOrDefault(p => p.Name.Equals(profileName, StringComparison.OrdinalIgnoreCase));
119141

120-
customDialog.Content = new ProfileFileDialog
142+
// Ask to enable encryption for the new profile file
143+
if (await ShowEnableEncryptionMessage())
144+
EnableEncryptionAction();
145+
}
146+
147+
private async Task<bool> ShowEnableEncryptionMessage()
148+
{
149+
var result = false;
150+
151+
var childWindow = new OKCancelInfoMessageChildWindow();
152+
153+
var childWindowViewModel = new OKCancelInfoMessageViewModel(_ =>
121154
{
122-
DataContext = profileFileViewModel
123-
};
155+
childWindow.IsOpen = false;
156+
ConfigurationManager.Current.IsChildWindowOpen = false;
124157

125-
await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog);
158+
result = true;
159+
}, _ =>
160+
{
161+
childWindow.IsOpen = false;
162+
ConfigurationManager.Current.IsChildWindowOpen = false;
163+
},
164+
Strings.EnableEncryptionForProfileFileMessage
165+
);
166+
167+
childWindow.Title = Strings.EnableEncryptionQuestion;
168+
169+
childWindow.DataContext = childWindowViewModel;
170+
171+
ConfigurationManager.Current.IsChildWindowOpen = true;
172+
173+
await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow);
174+
175+
return result;
126176
}
127177

128-
public ICommand EditProfileFileCommand => new RelayCommand(_ => EditProfileFileAction());
178+
public ICommand EditProfileFileCommand => new RelayCommand(async _ => await EditProfileFileAction().ConfigureAwait(false));
129179

130-
private async void EditProfileFileAction()
180+
private async Task EditProfileFileAction()
131181
{
132-
var customDialog = new CustomDialog
133-
{
134-
Title = Strings.EditProfileFile
135-
};
182+
var profileName = string.Empty;
183+
184+
var childWindow = new ProfileFileChildWindow();
136185

137-
var profileFileViewModel = new ProfileFileViewModel(async instance =>
186+
var childWindowViewModel = new ProfileFileViewModel(instance =>
138187
{
139-
await _dialogCoordinator.HideMetroDialogAsync(this, customDialog);
188+
childWindow.IsOpen = false;
189+
ConfigurationManager.Current.IsChildWindowOpen = false;
140190

141-
ProfileManager.RenameProfileFile(SelectedProfileFile, instance.Name);
142-
}, async _ => { await _dialogCoordinator.HideMetroDialogAsync(this, customDialog); }, SelectedProfileFile);
191+
profileName = instance.Name;
143192

144-
customDialog.Content = new ProfileFileDialog
193+
ProfileManager.RenameProfileFile(SelectedProfileFile, instance.Name);
194+
}, _ =>
145195
{
146-
DataContext = profileFileViewModel
147-
};
196+
childWindow.IsOpen = false;
197+
ConfigurationManager.Current.IsChildWindowOpen = false;
198+
}, SelectedProfileFile);
148199

149-
await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog);
200+
childWindow.Title = Strings.EditProfileFile;
201+
202+
childWindow.DataContext = childWindowViewModel;
203+
204+
ConfigurationManager.Current.IsChildWindowOpen = true;
205+
206+
await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow);
207+
208+
if (string.IsNullOrEmpty(profileName))
209+
return;
210+
211+
SelectedProfileFile = ProfileFiles.Cast<ProfileFileInfo>()
212+
.FirstOrDefault(p => p.Name.Equals(profileName, StringComparison.OrdinalIgnoreCase));
150213
}
151214

152215
public ICommand DeleteProfileFileCommand =>
@@ -172,7 +235,7 @@ private Task DeleteProfileFileAction()
172235
childWindow.IsOpen = false;
173236
ConfigurationManager.Current.IsChildWindowOpen = false;
174237
},
175-
Strings.DeleteProfileFileMessage, Strings.Delete);
238+
string.Format(Strings.DeleteProfileFileXMessage, SelectedProfileFile.Name), Strings.Delete);
176239

177240
childWindow.Title = Strings.DeleteProfileFile;
178241

@@ -187,13 +250,7 @@ private Task DeleteProfileFileAction()
187250

188251
private async void EnableEncryptionAction()
189252
{
190-
var settings = AppearanceManager.MetroDialog;
191-
192-
settings.AffirmativeButtonText = Strings.OK;
193-
settings.NegativeButtonText = Strings.Cancel;
194-
settings.DefaultButtonFocus = MessageDialogResult.Affirmative;
195-
196-
if (await _dialogCoordinator.ShowMessageAsync(this, Strings.Disclaimer, Strings.ProfileEncryptionDisclaimer, MessageDialogStyle.AffirmativeAndNegative, settings) != MessageDialogResult.Affirmative)
253+
if (!await ShowEncryptionDisclaimerAsync())
197254
return;
198255

199256
var customDialog = new CustomDialog
@@ -228,6 +285,37 @@ await _dialogCoordinator.ShowMessageAsync(this, Strings.EncryptionError,
228285
await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog);
229286
}
230287

288+
private async Task<bool> ShowEncryptionDisclaimerAsync()
289+
{
290+
var result = false;
291+
292+
var childWindow = new OKCancelInfoMessageChildWindow();
293+
294+
var childWindowViewModel = new OKCancelInfoMessageViewModel(_ =>
295+
{
296+
childWindow.IsOpen = false;
297+
ConfigurationManager.Current.IsChildWindowOpen = false;
298+
299+
result = true;
300+
}, _ =>
301+
{
302+
childWindow.IsOpen = false;
303+
ConfigurationManager.Current.IsChildWindowOpen = false;
304+
},
305+
Strings.ProfileEncryptionDisclaimer
306+
);
307+
308+
childWindow.Title = Strings.Disclaimer;
309+
310+
childWindow.DataContext = childWindowViewModel;
311+
312+
ConfigurationManager.Current.IsChildWindowOpen = true;
313+
314+
await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow);
315+
316+
return result;
317+
}
318+
231319
public ICommand ChangeMasterPasswordCommand => new RelayCommand(_ => ChangeMasterPasswordAction());
232320

233321
private async void ChangeMasterPasswordAction()

Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
xmlns:simpleChildWindow="clr-namespace:MahApps.Metro.SimpleChildWindow;assembly=MahApps.Metro.SimpleChildWindow"
99
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
1010
xmlns:converters="clr-namespace:NETworkManager.Converters;assembly=NETworkManager.Converters"
11-
Loaded="ChildWindow_Loaded"
11+
Loaded="ChildWindow_OnLoaded"
1212
Style="{StaticResource DefaultChildWindow}"
1313
mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:OKCancelInfoMessageViewModel}">
1414
<simpleChildWindow:ChildWindow.Resources>

Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public OKCancelInfoMessageChildWindow()
1010
InitializeComponent();
1111
}
1212

13-
private void ChildWindow_Loaded(object sender, System.Windows.RoutedEventArgs e)
13+
private void ChildWindow_OnLoaded(object sender, System.Windows.RoutedEventArgs e)
1414
{
1515
Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(delegate
1616
{

Source/NETworkManager/Views/OKMessageChildWindow.xaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,13 @@
88
xmlns:simpleChildWindow="clr-namespace:MahApps.Metro.SimpleChildWindow;assembly=MahApps.Metro.SimpleChildWindow"
99
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
1010
xmlns:converters="clr-namespace:NETworkManager.Converters;assembly=NETworkManager.Converters"
11-
Loaded="ChildWindow_Loaded"
11+
Loaded="ChildWindow_OnLoaded"
1212
Style="{StaticResource DefaultChildWindow}"
1313
mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:OKMessageViewModel}">
1414
<simpleChildWindow:ChildWindow.Resources>
1515
<converters:ChildWindowIconToRectangleStyleConverter x:Key="ChildWindowIconToRectangleStyleConverter" />
1616
</simpleChildWindow:ChildWindow.Resources>
17-
<Grid Margin="10">
18-
17+
<Grid Margin="10">
1918
<Grid.RowDefinitions>
2019
<RowDefinition Height="*" />
2120
<RowDefinition Height="20" />

Source/NETworkManager/Views/OKMessageChildWindow.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public OKMessageChildWindow()
1010
InitializeComponent();
1111
}
1212

13-
private void ChildWindow_Loaded(object sender, System.Windows.RoutedEventArgs e)
13+
private void ChildWindow_OnLoaded(object sender, System.Windows.RoutedEventArgs e)
1414
{
1515
Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(delegate
1616
{

0 commit comments

Comments
 (0)