diff --git a/Source/GlobalAssemblyInfo.cs b/Source/GlobalAssemblyInfo.cs
index 3c1969fc43..09a20cd912 100644
--- a/Source/GlobalAssemblyInfo.cs
+++ b/Source/GlobalAssemblyInfo.cs
@@ -6,5 +6,5 @@
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("2025.5.22.0")]
-[assembly: AssemblyFileVersion("2025.5.22.0")]
+[assembly: AssemblyVersion("2025.6.13.0")]
+[assembly: AssemblyFileVersion("2025.6.13.0")]
diff --git a/Source/NETworkManager.Documentation/DocumentationIdentifier.cs b/Source/NETworkManager.Documentation/DocumentationIdentifier.cs
index c046ec6f41..ecb8ad5946 100644
--- a/Source/NETworkManager.Documentation/DocumentationIdentifier.cs
+++ b/Source/NETworkManager.Documentation/DocumentationIdentifier.cs
@@ -203,5 +203,10 @@ public enum DocumentationIdentifier
///
/// Command line arguments.
///
- CommandLineArguments
+ CommandLineArguments,
+
+ ///
+ /// Changelog base documentation page.
+ ///
+ ChangelogBase
}
\ No newline at end of file
diff --git a/Source/NETworkManager.Documentation/DocumentationManager.cs b/Source/NETworkManager.Documentation/DocumentationManager.cs
index 87898fc4ff..aab0aef654 100644
--- a/Source/NETworkManager.Documentation/DocumentationManager.cs
+++ b/Source/NETworkManager.Documentation/DocumentationManager.cs
@@ -137,14 +137,12 @@ public static class DocumentationManager
@"Documentation/profiles"),
new DocumentationInfo(DocumentationIdentifier.CommandLineArguments,
- @"docs/commandline-arguments")
+ @"docs/commandline-arguments"),
+
+ new DocumentationInfo(DocumentationIdentifier.ChangelogBase,
+ @"docs/changelog")
];
- ///
- /// Command to open a documentation page based on .
- ///
- public static ICommand OpenDocumentationCommand => new RelayCommand(OpenDocumentationAction);
-
///
/// Method to create the documentation url from .
///
@@ -179,13 +177,15 @@ public static void OpenDocumentation(DocumentationIdentifier documentationIdenti
}
///
- /// Method to open a documentation page based on .
+ /// Method to open the current changelog in the default web browser.
///
- ///
- private static void OpenDocumentationAction(object documentationIdentifier)
+ public static void OpenChangelog()
{
- if (documentationIdentifier != null)
- OpenDocumentation((DocumentationIdentifier)documentationIdentifier);
+ var url = CreateUrl(DocumentationIdentifier.ChangelogBase);
+
+ url += $"/{AssemblyManager.Current.Version.ToString().Replace('.', '-')}";
+
+ ExternalProcessStarter.OpenUrl(url);
}
///
diff --git a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs
index 0f1ca5069a..f9eba9d838 100644
--- a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs
+++ b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs
@@ -1454,6 +1454,15 @@ public static string Change {
}
}
+ ///
+ /// Looks up a localized string similar to Changelog.
+ ///
+ public static string Changelog {
+ get {
+ return ResourceManager.GetString("Changelog", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Change master password.
///
@@ -10639,6 +10648,15 @@ public static string UpdateAvailable {
}
}
+ ///
+ /// Looks up a localized string similar to Upgraded to {0}.
+ ///
+ public static string UpgradedToXXX {
+ get {
+ return ResourceManager.GetString("UpgradedToXXX", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Upload.
///
@@ -11057,6 +11075,24 @@ public static string WelcomePrivacyMessage {
}
}
+ ///
+ /// Looks up a localized string similar to What's new?.
+ ///
+ public static string WhatsNew {
+ get {
+ return ResourceManager.GetString("WhatsNew", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to This release includes new features, improvements, and bug fixes. Check out the changelog for all the details!.
+ ///
+ public static string WhatsNewMessage {
+ get {
+ return ResourceManager.GetString("WhatsNewMessage", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to White.
///
diff --git a/Source/NETworkManager.Localization/Resources/Strings.resx b/Source/NETworkManager.Localization/Resources/Strings.resx
index 6d506c3def..d9dc0f7bf9 100644
--- a/Source/NETworkManager.Localization/Resources/Strings.resx
+++ b/Source/NETworkManager.Localization/Resources/Strings.resx
@@ -3918,4 +3918,16 @@ Right-click for more options.
Retrying in {0} seconds...
+
+ What's new?
+
+
+ Changelog
+
+
+ Upgraded to {0}
+
+
+ This release includes new features, improvements, and bug fixes. Check out the changelog for all the details!
+
\ No newline at end of file
diff --git a/Source/NETworkManager.Settings/ConfigurationInfo.cs b/Source/NETworkManager.Settings/ConfigurationInfo.cs
index 491f23227f..b092949113 100644
--- a/Source/NETworkManager.Settings/ConfigurationInfo.cs
+++ b/Source/NETworkManager.Settings/ConfigurationInfo.cs
@@ -31,7 +31,7 @@ public ConfigurationInfo(bool isAdmin, string executionPath, string applicationF
///
/// Indicates that the application is running as administrator.
///
- public bool IsAdmin { get; set; }
+ public bool IsAdmin { get; }
///
/// Execution path of the application like "C:\Program Files\NETworkManager".
diff --git a/Source/NETworkManager.Settings/ConfigurationManager.cs b/Source/NETworkManager.Settings/ConfigurationManager.cs
index f95b48a9fe..15a3192bf8 100644
--- a/Source/NETworkManager.Settings/ConfigurationManager.cs
+++ b/Source/NETworkManager.Settings/ConfigurationManager.cs
@@ -1,6 +1,6 @@
-using System.IO;
+using NETworkManager.Models;
+using System.IO;
using System.Security.Principal;
-using NETworkManager.Models;
namespace NETworkManager.Settings;
@@ -26,11 +26,12 @@ public static class ConfigurationManager
static ConfigurationManager()
{
Current = new ConfigurationInfo(
- new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator),
- AssemblyManager.Current.Location,
- Path.Combine(AssemblyManager.Current.Location, AssemblyManager.Current.Name + ".exe"),
- AssemblyManager.Current.Name,
- File.Exists(Path.Combine(AssemblyManager.Current.Location, $"{IsPortableFileName}.{IsPortableExtension}")));
+ isAdmin: new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator),
+ executionPath: AssemblyManager.Current.Location,
+ applicationFullName: Path.Combine(AssemblyManager.Current.Location, AssemblyManager.Current.Name + ".exe"),
+ applicationName: AssemblyManager.Current.Name,
+ isPortable: File.Exists(Path.Combine(AssemblyManager.Current.Location, $"{IsPortableFileName}.{IsPortableExtension}"))
+ );
}
///
diff --git a/Source/NETworkManager.Settings/SettingsInfo.cs b/Source/NETworkManager.Settings/SettingsInfo.cs
index 4cc74fa90f..24374e9d53 100644
--- a/Source/NETworkManager.Settings/SettingsInfo.cs
+++ b/Source/NETworkManager.Settings/SettingsInfo.cs
@@ -22,8 +22,18 @@ namespace NETworkManager.Settings;
public class SettingsInfo : INotifyPropertyChanged
{
+ ///
+ /// Occurs when a property value changes.
+ ///
+ /// This event is typically used to notify subscribers that a property value has been updated. It
+ /// is commonly implemented in classes that support data binding or need to signal changes to property
+ /// values.
public event PropertyChangedEventHandler PropertyChanged;
+ ///
+ /// Helper method to raise the event.
+ ///
+ /// Name of the property that changed.
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
SettingsChanged = true;
@@ -35,8 +45,14 @@ private void OnPropertyChanged([CallerMemberName] string propertyName = null)
[XmlIgnore] public bool SettingsChanged { get; set; }
+ ///
+ /// Private field for the property.
+ ///
private bool _welcomeDialog_Show = true;
+ ///
+ /// Determines if the welcome dialog should be shown on application start.
+ ///
public bool WelcomeDialog_Show
{
get => _welcomeDialog_Show;
@@ -50,8 +66,37 @@ public bool WelcomeDialog_Show
}
}
+ ///
+ /// Private field for the property.
+ ///
+ private bool _upgradeDialog_Show;
+
+ ///
+ /// Indicates if the update dialog should be shown on application start.
+ /// Usually this is set to true if the application has been updated to a new version.
+ ///
+ public bool UpgradeDialog_Show
+ {
+ get => _upgradeDialog_Show;
+ set
+ {
+ if (value == _upgradeDialog_Show)
+ return;
+
+ _upgradeDialog_Show = value;
+ OnPropertyChanged();
+ }
+ }
+
+ ///
+ /// Private field for the property.
+ ///
private string _version;
+ ///
+ /// Version of the settings file. Should be identical to the version of the application.
+ /// It is used to determine if the settings file needs to be updated.
+ ///
public string Version
{
get => _version;
diff --git a/Source/NETworkManager.Settings/SettingsManager.cs b/Source/NETworkManager.Settings/SettingsManager.cs
index 9fee3564fc..1386298412 100644
--- a/Source/NETworkManager.Settings/SettingsManager.cs
+++ b/Source/NETworkManager.Settings/SettingsManager.cs
@@ -186,7 +186,6 @@ public static void Upgrade(Version fromVersion, Version toVersion)
if (fromVersion < new Version(2023, 11, 28, 0))
UpgradeTo_2023_11_28_0();
-
// 2024.11.11.0
if (fromVersion < new Version(2024, 11, 11, 0))
UpgradeTo_2024_11_11_0();
@@ -196,7 +195,9 @@ public static void Upgrade(Version fromVersion, Version toVersion)
UpgradeToLatest(toVersion);
// Update to the latest version and save
+ Current.UpgradeDialog_Show = true;
Current.Version = toVersion.ToString();
+
Save();
Log.Info("Settings upgrade finished!");
diff --git a/Source/NETworkManager/MainWindow.xaml.cs b/Source/NETworkManager/MainWindow.xaml.cs
index 96e54a37ca..955b2bc5c7 100644
--- a/Source/NETworkManager/MainWindow.xaml.cs
+++ b/Source/NETworkManager/MainWindow.xaml.cs
@@ -393,7 +393,7 @@ public ProfileFileInfo SelectedProfileFile
{
if (!_isProfileFileUpdating)
LoadProfile(value);
-
+
ConfigurationManager.Current.ProfileManagerShowUnlock = value.IsEncrypted && !value.IsPasswordValid;
SettingsManager.Current.Profiles_LastSelected = value.Name;
}
@@ -513,6 +513,27 @@ await this.ShowMessageAsync(Strings.SettingsHaveBeenReset,
await this.ShowChildWindowAsync(childWindow);
}
+ else if (SettingsManager.Current.UpgradeDialog_Show)
+ {
+ var childWindow = new UpgradeChildWindow();
+
+ var viewModel = new UpgradeViewModel(instance =>
+ {
+ childWindow.IsOpen = false;
+
+ SettingsManager.Current.UpgradeDialog_Show = false;
+
+ SettingsManager.Save();
+
+ Load();
+ });
+
+ childWindow.DataContext = viewModel;
+
+ ConfigurationManager.Current.IsChildWindowOpen = true;
+
+ await this.ShowChildWindowAsync(childWindow);
+ }
else
{
Load();
@@ -826,11 +847,11 @@ private void OnApplicationViewVisible(ApplicationName name, bool fromSettings =
ContentControlApplication.Content = _sntpLookupHostView;
break;
case ApplicationName.HostsFileEditor:
- if(_hostsFileEditorView == null)
+ if (_hostsFileEditorView == null)
_hostsFileEditorView = new HostsFileEditorView();
else
_hostsFileEditorView.OnViewVisible();
-
+
ContentControlApplication.Content = _hostsFileEditorView;
break;
case ApplicationName.DiscoveryProtocol:
@@ -913,7 +934,7 @@ private void OnApplicationViewVisible(ApplicationName name, bool fromSettings =
ContentControlApplication.Content = _arpTableView;
break;
-
+
default:
Log.Error("Cannot show unknown application view: " + name);
break;
@@ -1387,7 +1408,7 @@ private void LoadProfiles()
.FirstOrDefault(x => x.Name == SettingsManager.Current.Profiles_LastSelected);
SelectedProfileFile ??= ProfileFiles.SourceCollection.Cast().FirstOrDefault();
}
-
+
private async void LoadProfile(ProfileFileInfo info, bool showWrongPassword = false)
{
// Disable profile management while switching profiles
diff --git a/Source/NETworkManager/ViewModels/UpgradeViewModel.cs b/Source/NETworkManager/ViewModels/UpgradeViewModel.cs
new file mode 100644
index 0000000000..8eaa9342e0
--- /dev/null
+++ b/Source/NETworkManager/ViewModels/UpgradeViewModel.cs
@@ -0,0 +1,33 @@
+using NETworkManager.Documentation;
+using NETworkManager.Settings;
+using NETworkManager.Utilities;
+using System;
+using System.Windows.Input;
+
+namespace NETworkManager.ViewModels;
+
+public class UpgradeViewModel : ViewModelBase
+{
+ public static string Title => string.Format(Localization.Resources.Strings.UpgradedToXXX, AssemblyManager.Current.Version);
+
+ public UpgradeViewModel(Action continueCommand)
+ {
+ ContinueCommand = new RelayCommand(_ => continueCommand(this));
+ }
+
+ public ICommand OpenWebsiteCommand => new RelayCommand(OpenWebsiteAction);
+
+ private static void OpenWebsiteAction(object url)
+ {
+ ExternalProcessStarter.OpenUrl((string)url);
+ }
+
+ public ICommand OpenChangelogCommand => new RelayCommand(OpenChangelogAction);
+
+ private void OpenChangelogAction(object obj)
+ {
+ DocumentationManager.OpenChangelog();
+ }
+
+ public ICommand ContinueCommand { get; }
+}
diff --git a/Source/NETworkManager/Views/UpgradeChildWindow.xaml b/Source/NETworkManager/Views/UpgradeChildWindow.xaml
new file mode 100644
index 0000000000..ae6a2a722c
--- /dev/null
+++ b/Source/NETworkManager/Views/UpgradeChildWindow.xaml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/NETworkManager/Views/UpgradeChildWindow.xaml.cs b/Source/NETworkManager/Views/UpgradeChildWindow.xaml.cs
new file mode 100644
index 0000000000..3049e19f46
--- /dev/null
+++ b/Source/NETworkManager/Views/UpgradeChildWindow.xaml.cs
@@ -0,0 +1,9 @@
+namespace NETworkManager.Views;
+
+public partial class UpgradeChildWindow
+{
+ public UpgradeChildWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/Website/docs/changelog/next-release.md b/Website/docs/changelog/next-release.md
index e1bc1e086f..ac3b8ecf77 100644
--- a/Website/docs/changelog/next-release.md
+++ b/Website/docs/changelog/next-release.md
@@ -39,6 +39,9 @@ Release date: **xx.xx.2025**
## Improvements
+- Redesign welcome dialog. [#3077](https://github.com/BornToBeRoot/NETworkManager/pull/3077)
+- Add upgrade dialog when updating from a previous version. [#3077](https://github.com/BornToBeRoot/NETworkManager/pull/3077)
+
**WiFi**
- Redesign refresh button/view. [#3012](https://github.com/BornToBeRoot/NETworkManager/pull/3012)