diff --git a/Natsurainko.FluentLauncher/App.xaml.cs b/Natsurainko.FluentLauncher/App.xaml.cs index 622de40a..1c2255fd 100644 --- a/Natsurainko.FluentLauncher/App.xaml.cs +++ b/Natsurainko.FluentLauncher/App.xaml.cs @@ -27,9 +27,10 @@ namespace Natsurainko.FluentLauncher; public partial class App : Application { public static IServiceProvider Services { get; } = ConfigureServices(); - public static T GetService() => Services.GetService(); - public static MainWindow MainWindow { get; set; } - public static DispatcherQueue DispatcherQueue { get; private set; } + public static T GetService() => Services.GetService() + ?? throw new InvalidOperationException($"Service of type {typeof(T).Name} not found."); + public static MainWindow MainWindow { get; set; } = null!; + public static DispatcherQueue DispatcherQueue { get; private set; } = null!; public App() { diff --git a/Natsurainko.FluentLauncher/Classes/Data/UI/ChooseModLoaderData.cs b/Natsurainko.FluentLauncher/Classes/Data/UI/ChooseModLoaderData.cs index 848cd83b..37ba3b61 100644 --- a/Natsurainko.FluentLauncher/Classes/Data/UI/ChooseModLoaderData.cs +++ b/Natsurainko.FluentLauncher/Classes/Data/UI/ChooseModLoaderData.cs @@ -54,10 +54,10 @@ public ChooseModLoaderData(ModLoaderType type, VersionManifestItem manifestItem, private string displayText = ResourceUtils.GetValue("CoreInstallWizard", "ChooseModLoaderPage", "_Loading"); [ObservableProperty] - private IEnumerable items; + private IEnumerable? items; [ObservableProperty] - private LoaderBuildData selectedItem; + private LoaderBuildData? selectedItem; private void Init() => Task.Run(() => { @@ -74,20 +74,31 @@ private void Init() => Task.Run(() => using var responseMessage = HttpUtils.HttpGet(url); responseMessage.EnsureSuccessStatusCode(); - var array = JsonNode.Parse(responseMessage.Content.ReadAsString()).AsArray(); - IEnumerable loaders = default; + var array = responseMessage.Content.ReadAsString() + .ToJsonNode()? + .AsArray() + .WhereNotNull(); + IEnumerable? loaders = null; - if (array.Any()) + if (array is not null && array.Any()) { switch (Type) { case ModLoaderType.Forge: - var forge = array.Select(x => new LoaderBuildData + var forge = array.Select(x => { - DisplayText = x["version"].GetValue(), - Metadata = x["build"] - }).ToList(); + var displayText = x["version"]?.GetValue(); + var metadata = x["build"]; + if (displayText is null || metadata is null) + throw new Exception("Invalid forge data"); + + return new LoaderBuildData + { + DisplayText = displayText, + Metadata = metadata + }; + }).WhereNotNull().ToList(); forge.Sort((a, b) => a.Metadata.GetValue().CompareTo(b.Metadata.GetValue())); forge.Reverse(); @@ -97,10 +108,17 @@ private void Init() => Task.Run(() => break; case ModLoaderType.Fabric: - var fabric = array.Select(x => new LoaderBuildData + var fabric = array.Select(x => { - DisplayText = x["loader"]["version"].GetValue(), - Metadata = x + var displayText = x["loader"]?["version"]?.GetValue(); + if (displayText is null) + throw new Exception("Invalid fabric data"); + + return new LoaderBuildData + { + DisplayText = displayText, + Metadata = x + }; }).ToList(); loaders = fabric; @@ -108,10 +126,18 @@ private void Init() => Task.Run(() => break; case ModLoaderType.OptiFine: - var optifine = array.Select(x => new LoaderBuildData + var optifine = array.Select(x => { - DisplayText = $"{x["type"].GetValue()}_{x["patch"].GetValue()}", - Metadata = x + var type = x["type"]?.GetValue(); + var patch = x["patch"]?.GetValue(); + if (type is null || patch is null) + throw new Exception("Invalid optifine data"); + + return new LoaderBuildData + { + DisplayText = $"{type}_{patch}", + Metadata = x + }; }).ToList(); loaders = optifine; @@ -213,8 +239,8 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) public class LoaderBuildData { - public string DisplayText { get; set; } + public required string DisplayText { get; set; } - public JsonNode Metadata { get; set; } + public required JsonNode Metadata { get; set; } } } diff --git a/Natsurainko.FluentLauncher/Classes/Data/UI/NewsData.cs b/Natsurainko.FluentLauncher/Classes/Data/UI/NewsData.cs index 91a3ef1b..a6ac7ebb 100644 --- a/Natsurainko.FluentLauncher/Classes/Data/UI/NewsData.cs +++ b/Natsurainko.FluentLauncher/Classes/Data/UI/NewsData.cs @@ -1,23 +1,39 @@ -using System.Text.Json.Serialization; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; namespace Natsurainko.FluentLauncher.Classes.Data.UI; internal class NewsData { - public string ImageUrl { get; set; } + public string? ImageUrl { get; set; } = null!; [JsonPropertyName("title")] - public string Title { get; set; } + public string? Title { get; set; } [JsonPropertyName("tag")] - public string Tag { get; set; } + public string? Tag { get; set; } [JsonPropertyName("date")] - public string Date { get; set; } + public string? Date { get; set; } [JsonPropertyName("text")] - public string Text { get; set; } + public string? Text { get; set; } [JsonPropertyName("readMoreLink")] - public string ReadMoreUrl { get; set; } + public string? ReadMoreUrl { get; set; } + + + public static NewsData Deserialize(JsonNode node) + { + NewsData? data = node.Deserialize(); + if (data is null) + throw new JsonException("Failed to deserialize news data"); + + string urlPath = node["newsPageImage"]?["url"]?.GetValue() + ?? throw new JsonException("Failed to get news image URL"); + data.ImageUrl = $"https://launchercontent.mojang.com{urlPath}"; + + return data; + } } diff --git a/Natsurainko.FluentLauncher/Services/Storage/InterfaceCacheService.cs b/Natsurainko.FluentLauncher/Services/Storage/InterfaceCacheService.cs index b97af87d..20bcd645 100644 --- a/Natsurainko.FluentLauncher/Services/Storage/InterfaceCacheService.cs +++ b/Natsurainko.FluentLauncher/Services/Storage/InterfaceCacheService.cs @@ -1,5 +1,6 @@ using Natsurainko.FluentLauncher.Classes.Data.UI; using Natsurainko.FluentLauncher.Services.Settings; +using Natsurainko.FluentLauncher.Utils; using Nrk.FluentCore.Resources; using Nrk.FluentCore.Utils; using System; @@ -108,15 +109,16 @@ public Task FetchNews() _fetchNewsTask = Task.Run(() => { var localFile = GetLocalFileOfInterface("https://launchercontent.mojang.com/news.json", autoRefresh: true); - if (string.IsNullOrEmpty(localFile)) throw new ArgumentNullException(nameof(localFile)); + if (string.IsNullOrEmpty(localFile)) + throw new ArgumentNullException(nameof(localFile)); - return JsonNode.Parse(File.ReadAllText(localFile))["entries"].AsArray() - .Select(x => - { - var contentData = x.Deserialize(); - contentData.ImageUrl = $"https://launchercontent.mojang.com{x["newsPageImage"]["url"].GetValue()}"; - return contentData; - }).ToArray(); + var entries = JsonNodeUtils.ParseFile(localFile)?["entries"] + ?? throw new JsonException("Failed to get news entries"); + + return entries + .AsArray() + .WhereNotNull() + .Select(x => NewsData.Deserialize(x)).ToArray(); }); return _fetchNewsTask; diff --git a/Natsurainko.FluentLauncher/Services/UI/AppearanceService.cs b/Natsurainko.FluentLauncher/Services/UI/AppearanceService.cs index 64fc270f..1eb4cb4c 100644 --- a/Natsurainko.FluentLauncher/Services/UI/AppearanceService.cs +++ b/Natsurainko.FluentLauncher/Services/UI/AppearanceService.cs @@ -19,8 +19,8 @@ namespace Natsurainko.FluentLauncher.Services.UI; internal class AppearanceService { private readonly SettingsService _settingsService; - private NavigationView _navigationView; - private BitmapImage backgroundImage; + private NavigationView? _navigationView; + private BitmapImage? backgroundImage; public Type HomePageType => _settingsService.UseNewHomePage ? typeof(NewHomePage) : typeof(HomePage); @@ -29,13 +29,6 @@ public AppearanceService(SettingsService settingsService) _settingsService = settingsService; } - private void NavigationViewDisplayModeChanged(AppSettingsManagement.SettingsContainer sender, AppSettingsManagement.SettingChangedEventArgs e) - { - _navigationView.PaneDisplayMode = _settingsService.NavigationViewDisplayMode == 0 - ? NavigationViewPaneDisplayMode.Auto - : NavigationViewPaneDisplayMode.LeftMinimal; - } - public void RegisterNavigationView(NavigationView navigationView) { _navigationView = navigationView; @@ -43,7 +36,12 @@ public void RegisterNavigationView(NavigationView navigationView) ? NavigationViewPaneDisplayMode.Auto : NavigationViewPaneDisplayMode.LeftMinimal; - _settingsService.NavigationViewDisplayModeChanged += NavigationViewDisplayModeChanged; + _settingsService.NavigationViewDisplayModeChanged += (sender, e)=> + { + _navigationView.PaneDisplayMode = _settingsService.NavigationViewDisplayMode == 0 + ? NavigationViewPaneDisplayMode.Auto + : NavigationViewPaneDisplayMode.LeftMinimal; + }; } public void ApplyDisplayTheme() @@ -136,9 +134,9 @@ public void ApplyBackgroundAfterPageInit(ShellPage page) public void ApplyBackgroundAtWindowCreated(MainWindow window) { - WindowsSystemDispatcherQueueHelper m_wsdqHelper = null; - DesktopAcrylicController m_backdropController = null; - SystemBackdropConfiguration m_configurationSource = null; + WindowsSystemDispatcherQueueHelper? m_wsdqHelper = null; + DesktopAcrylicController? m_backdropController = null; + SystemBackdropConfiguration? m_configurationSource = null; bool TrySetAcrylicBackdrop(MainWindow window) { diff --git a/Natsurainko.FluentLauncher/Services/UI/Navigation/INavigationAware.cs b/Natsurainko.FluentLauncher/Services/UI/Navigation/INavigationAware.cs index 70b91e3d..3123bcec 100644 --- a/Natsurainko.FluentLauncher/Services/UI/Navigation/INavigationAware.cs +++ b/Natsurainko.FluentLauncher/Services/UI/Navigation/INavigationAware.cs @@ -5,7 +5,7 @@ /// public interface INavigationAware { - void OnNavigatedTo(object parameter) { } + void OnNavigatedTo(object? parameter) { } void OnNavigatedFrom() { } } diff --git a/Natsurainko.FluentLauncher/Services/UI/Windows/ActivationService.cs b/Natsurainko.FluentLauncher/Services/UI/Windows/ActivationService.cs index 7123e92f..0ece01e6 100644 --- a/Natsurainko.FluentLauncher/Services/UI/Windows/ActivationService.cs +++ b/Natsurainko.FluentLauncher/Services/UI/Windows/ActivationService.cs @@ -34,7 +34,8 @@ public IWindowService ActivateWindow(string key) // Constructs the window Type windowType = RegisteredWindows[key].WindowType; // windowType is guaranteed to be a subclass of TWindowBase when the activation service is built - TWindowBase window = (TWindowBase)scope.ServiceProvider.GetService(windowType); + TWindowBase window = (TWindowBase?)scope.ServiceProvider.GetService(windowType) + ?? throw new InvalidOperationException($"The window type {windowType} is not registered with the window provider."); // If the window supports navigation, initialize the navigation service for the window scope // The navigation service may have been instantiated and injected into 'window' already. diff --git a/Natsurainko.FluentLauncher/Utils/JsonNodeUtils.cs b/Natsurainko.FluentLauncher/Utils/JsonNodeUtils.cs new file mode 100644 index 00000000..bf90c3c7 --- /dev/null +++ b/Natsurainko.FluentLauncher/Utils/JsonNodeUtils.cs @@ -0,0 +1,26 @@ +using System.IO; +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace Natsurainko.FluentLauncher.Utils; + +internal static class JsonNodeUtils +{ + public static JsonNode ParseFile(string filePath) + { + JsonNode? jsonNode = null; + try + { + jsonNode = JsonNode.Parse(File.ReadAllText(filePath)); + } + catch (JsonException) { } + + // jsonNode is null if either + // - the file is not a valid json file, or + // - the file contains the string "null" and JsonNode.Parse(string) returns null. + if (jsonNode is null) + throw new InvalidDataException($"Error in parsing the json file {filePath}."); + + return jsonNode; + } +} diff --git a/Natsurainko.FluentLauncher/Utils/LinqExtensions.cs b/Natsurainko.FluentLauncher/Utils/LinqExtensions.cs new file mode 100644 index 00000000..4b76d9a1 --- /dev/null +++ b/Natsurainko.FluentLauncher/Utils/LinqExtensions.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Natsurainko.FluentLauncher.Utils; + +internal static class LinqExtensions +{ + public static IEnumerable WhereNotNull(this IEnumerable source) where T : class + { + return source.Where(x => x is not null)!; + } +} diff --git a/Natsurainko.FluentLauncher/Utils/NavigationViewItemExtensions.cs b/Natsurainko.FluentLauncher/Utils/NavigationViewItemExtensions.cs new file mode 100644 index 00000000..bee8aafd --- /dev/null +++ b/Natsurainko.FluentLauncher/Utils/NavigationViewItemExtensions.cs @@ -0,0 +1,17 @@ +using Microsoft.UI.Xaml.Controls; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Natsurainko.FluentLauncher.Utils; + +internal static class NavigationViewItemExtensions +{ + public static string GetTag(this NavigationViewItem item) + { + return item.Tag?.ToString() + ?? throw new ArgumentNullException("The item's tag is null."); + } +} diff --git a/Natsurainko.FluentLauncher/ViewModels/Activities/ActivitiesNavigationViewModel.cs b/Natsurainko.FluentLauncher/ViewModels/Activities/ActivitiesNavigationViewModel.cs index 20d37402..256ce0eb 100644 --- a/Natsurainko.FluentLauncher/ViewModels/Activities/ActivitiesNavigationViewModel.cs +++ b/Natsurainko.FluentLauncher/ViewModels/Activities/ActivitiesNavigationViewModel.cs @@ -16,7 +16,7 @@ public ActivitiesNavigationViewModel(INavigationService navigationService) #region Navigation - void INavigationAware.OnNavigatedTo(object parameter) + void INavigationAware.OnNavigatedTo(object? parameter) { if (parameter is string pageKey) _navigationService.NavigateTo(pageKey); diff --git a/Natsurainko.FluentLauncher/ViewModels/Cores/Manage/CoreModsViewModel.cs b/Natsurainko.FluentLauncher/ViewModels/Cores/Manage/CoreModsViewModel.cs index a8bb34d3..86ab7645 100644 --- a/Natsurainko.FluentLauncher/ViewModels/Cores/Manage/CoreModsViewModel.cs +++ b/Natsurainko.FluentLauncher/ViewModels/Cores/Manage/CoreModsViewModel.cs @@ -5,6 +5,7 @@ using Natsurainko.FluentLauncher.Utils; using Nrk.FluentCore.Launch; using Nrk.FluentCore.Management.Mods; +using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; @@ -36,9 +37,10 @@ public CoreModsViewModel(INavigationService navigationService) [ObservableProperty] private string searchBoxInput; - void INavigationAware.OnNavigatedTo(object parameter) + void INavigationAware.OnNavigatedTo(object? parameter) { - var gameInfo = parameter as GameInfo; + if (parameter is not GameInfo gameInfo) + throw new ArgumentException("Invalid parameter type"); SupportMod = gameInfo.IsSupportMod(); diff --git a/Natsurainko.FluentLauncher/ViewModels/Cores/Manage/CoreSettingsViewModel.cs b/Natsurainko.FluentLauncher/ViewModels/Cores/Manage/CoreSettingsViewModel.cs index eca68ce7..a79e5ad1 100644 --- a/Natsurainko.FluentLauncher/ViewModels/Cores/Manage/CoreSettingsViewModel.cs +++ b/Natsurainko.FluentLauncher/ViewModels/Cores/Manage/CoreSettingsViewModel.cs @@ -10,6 +10,7 @@ using Natsurainko.FluentLauncher.Views.Common; using Nrk.FluentCore.Authentication; using Nrk.FluentCore.Launch; +using System; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; @@ -40,9 +41,11 @@ public CoreSettingsViewModel(GameService gameService, AccountService accountServ _accountService = accountService; } - void INavigationAware.OnNavigatedTo(object parameter) + void INavigationAware.OnNavigatedTo(object? parameter) { - _gameInfo = parameter as GameInfo; + if (parameter is not GameInfo _gameInfo) + throw new ArgumentException("Invalid parameter type"); + GameSpecialConfig = _gameInfo.GetSpecialConfig(); Accounts = _accountService.Accounts; diff --git a/Natsurainko.FluentLauncher/ViewModels/Cores/Manage/CoreStatisticViewModel.cs b/Natsurainko.FluentLauncher/ViewModels/Cores/Manage/CoreStatisticViewModel.cs index 4ae10de6..3bb71d34 100644 --- a/Natsurainko.FluentLauncher/ViewModels/Cores/Manage/CoreStatisticViewModel.cs +++ b/Natsurainko.FluentLauncher/ViewModels/Cores/Manage/CoreStatisticViewModel.cs @@ -38,9 +38,12 @@ public string FormatSize public bool IsVanilla => _gameInfo.IsVanilla; - void INavigationAware.OnNavigatedTo(object parameter) + void INavigationAware.OnNavigatedTo(object? parameter) { - _gameInfo = parameter as GameInfo; + if (parameter is not GameInfo gameInfo) + throw new ArgumentException("Invalid parameter type"); + + _gameInfo = gameInfo; Task.Run(() => { diff --git a/Natsurainko.FluentLauncher/ViewModels/Cores/ManageNavigationViewModel.cs b/Natsurainko.FluentLauncher/ViewModels/Cores/ManageNavigationViewModel.cs index 4f016434..6a3f4728 100644 --- a/Natsurainko.FluentLauncher/ViewModels/Cores/ManageNavigationViewModel.cs +++ b/Natsurainko.FluentLauncher/ViewModels/Cores/ManageNavigationViewModel.cs @@ -7,6 +7,7 @@ using Natsurainko.FluentLauncher.Views; using Natsurainko.FluentLauncher.Views.Common; using Nrk.FluentCore.Launch; +using System; namespace Natsurainko.FluentLauncher.ViewModels.Cores; @@ -37,9 +38,12 @@ void BreadcrumbBarClicked(object args) #region Navigation - void INavigationAware.OnNavigatedTo(object parameter) + void INavigationAware.OnNavigatedTo(object? parameter) { - _gameInfo = parameter as GameInfo; + if (parameter is not GameInfo gameInfo) + throw new ArgumentException("Invalid parameter type"); + + _gameInfo = gameInfo; BreadcrumbBarItemsSource = new string[] { diff --git a/Natsurainko.FluentLauncher/ViewModels/Downloads/CoreInstallWizardViewModel.cs b/Natsurainko.FluentLauncher/ViewModels/Downloads/CoreInstallWizardViewModel.cs index 7ab7b2f6..34cb958b 100644 --- a/Natsurainko.FluentLauncher/ViewModels/Downloads/CoreInstallWizardViewModel.cs +++ b/Natsurainko.FluentLauncher/ViewModels/Downloads/CoreInstallWizardViewModel.cs @@ -57,9 +57,12 @@ public CoreInstallWizardViewModel( _interfaceCacheService = interfaceCacheService; } - void INavigationAware.OnNavigatedTo(object parameter) + void INavigationAware.OnNavigatedTo(object? parameter) { - _manifestItem = (VersionManifestItem)parameter; + if (parameter is VersionManifestItem item) + { + _manifestItem = item; + } } [RelayCommand] diff --git a/Natsurainko.FluentLauncher/ViewModels/Downloads/ResourceItemViewModel.cs b/Natsurainko.FluentLauncher/ViewModels/Downloads/ResourceItemViewModel.cs index 88de2431..b98df3cb 100644 --- a/Natsurainko.FluentLauncher/ViewModels/Downloads/ResourceItemViewModel.cs +++ b/Natsurainko.FluentLauncher/ViewModels/Downloads/ResourceItemViewModel.cs @@ -35,7 +35,7 @@ public ResourceItemViewModel(INavigationService navigationService, InterfaceCach [ObservableProperty] private object resource; - void INavigationAware.OnNavigatedTo(object parameter) + void INavigationAware.OnNavigatedTo(object? parameter) { Resource = parameter; diff --git a/Natsurainko.FluentLauncher/ViewModels/Downloads/ResourcesSearchViewModel.cs b/Natsurainko.FluentLauncher/ViewModels/Downloads/ResourcesSearchViewModel.cs index 9d2d4335..78b14d7d 100644 --- a/Natsurainko.FluentLauncher/ViewModels/Downloads/ResourcesSearchViewModel.cs +++ b/Natsurainko.FluentLauncher/ViewModels/Downloads/ResourcesSearchViewModel.cs @@ -8,6 +8,7 @@ using Natsurainko.FluentLauncher.Services.UI.Navigation; using Natsurainko.FluentLauncher.Utils; using Nrk.FluentCore.Resources; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -58,9 +59,10 @@ public ResourcesSearchViewModel( public bool ComboBoxEnable => ResourceType != 4; - void INavigationAware.OnNavigatedTo(object parameter) + void INavigationAware.OnNavigatedTo(object? parameter) { - var searchData = parameter as ResourceSearchData; + if (parameter is not ResourceSearchData searchData) + throw new ArgumentException("Invalid parameter type"); SearchBoxInput = searchData.SearchInput; ResourceType = searchData.ResourceType; diff --git a/Natsurainko.FluentLauncher/ViewModels/OOBE/OOBEViewModel.cs b/Natsurainko.FluentLauncher/ViewModels/OOBE/OOBEViewModel.cs index 9d06183a..4bd58c08 100644 --- a/Natsurainko.FluentLauncher/ViewModels/OOBE/OOBEViewModel.cs +++ b/Natsurainko.FluentLauncher/ViewModels/OOBE/OOBEViewModel.cs @@ -75,7 +75,7 @@ public OOBEViewModel( "OOBEGetStartedPage" }; - void INavigationAware.OnNavigatedTo(object parameter) + void INavigationAware.OnNavigatedTo(object? parameter) { _navigationService.NavigateTo("OOBELanguagePage"); // Default page } diff --git a/Natsurainko.FluentLauncher/ViewModels/Settings/SettingsNavigationViewModel.cs b/Natsurainko.FluentLauncher/ViewModels/Settings/SettingsNavigationViewModel.cs index 35ec3ed4..52b44172 100644 --- a/Natsurainko.FluentLauncher/ViewModels/Settings/SettingsNavigationViewModel.cs +++ b/Natsurainko.FluentLauncher/ViewModels/Settings/SettingsNavigationViewModel.cs @@ -14,7 +14,7 @@ public SettingsNavigationViewModel(INavigationService navigationService) _navigationService = navigationService; } - void INavigationAware.OnNavigatedTo(object parameter) + void INavigationAware.OnNavigatedTo(object? parameter) { if (parameter is string pageKey) _navigationService.NavigateTo(pageKey); diff --git a/Natsurainko.FluentLauncher/ViewModels/ShellViewModel.cs b/Natsurainko.FluentLauncher/ViewModels/ShellViewModel.cs index b366f126..ed8e3fac 100644 --- a/Natsurainko.FluentLauncher/ViewModels/ShellViewModel.cs +++ b/Natsurainko.FluentLauncher/ViewModels/ShellViewModel.cs @@ -18,7 +18,7 @@ public ShellViewModel(INavigationService shellNavigationService, SettingsService _settings = settings; } - void INavigationAware.OnNavigatedTo(object parameter) + void INavigationAware.OnNavigatedTo(object? parameter) { if (parameter is string pageKey) { diff --git a/Natsurainko.FluentLauncher/Views/Activities/ActivitiesNavigationPage.xaml.cs b/Natsurainko.FluentLauncher/Views/Activities/ActivitiesNavigationPage.xaml.cs index 6172b052..59482503 100644 --- a/Natsurainko.FluentLauncher/Views/Activities/ActivitiesNavigationPage.xaml.cs +++ b/Natsurainko.FluentLauncher/Views/Activities/ActivitiesNavigationPage.xaml.cs @@ -2,6 +2,7 @@ using Microsoft.UI.Xaml.Navigation; using Natsurainko.FluentLauncher.Services.UI.Navigation; using Natsurainko.FluentLauncher.Services.UI.Pages; +using Natsurainko.FluentLauncher.Utils; using Natsurainko.FluentLauncher.ViewModels.Activities; using System.Linq; @@ -18,13 +19,13 @@ public ActivitiesNavigationPage() } private void NavigationView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) - => VM.NavigateTo(((NavigationViewItem)args.InvokedItemContainer).Tag.ToString()); + => VM.NavigateTo(((NavigationViewItem)args.InvokedItemContainer).GetTag()); private void ContentFrame_Navigated(object sender, NavigationEventArgs e) { foreach (NavigationViewItem item in NavigationView.MenuItems.Union(NavigationView.FooterMenuItems).Cast()) { - if (App.GetService().RegisteredPages[item.Tag.ToString()].PageType == e.SourcePageType) + if (App.GetService().RegisteredPages[item.GetTag()].PageType == e.SourcePageType) { NavigationView.SelectedItem = item; item.IsSelected = true; diff --git a/Natsurainko.FluentLauncher/Views/Cores/Manage/CoreModsPage.xaml.cs b/Natsurainko.FluentLauncher/Views/Cores/Manage/CoreModsPage.xaml.cs index 5c44a1b3..9e3c1eaa 100644 --- a/Natsurainko.FluentLauncher/Views/Cores/Manage/CoreModsPage.xaml.cs +++ b/Natsurainko.FluentLauncher/Views/Cores/Manage/CoreModsPage.xaml.cs @@ -15,15 +15,15 @@ private void toggleSwitch_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArg { void ToggleSwitch_Toggled(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) { - var toggleSwitch = sender as ToggleSwitch; - var modInfo = toggleSwitch.DataContext as ModInfo; - var modsManager = (this.DataContext as CoreModsViewModel).modsManager; + var toggleSwitch = (ToggleSwitch)sender; + var modInfo = (ModInfo)toggleSwitch.DataContext; + var modsManager = ((CoreModsViewModel)DataContext).modsManager; if (modInfo != null) modsManager.Switch(modInfo, toggleSwitch.IsOn); } - var toggleSwitch = sender as ToggleSwitch; + var toggleSwitch = (ToggleSwitch)sender; toggleSwitch.Toggled += ToggleSwitch_Toggled; toggleSwitch.Unloaded += (_, _) => toggleSwitch.Toggled -= ToggleSwitch_Toggled; diff --git a/Natsurainko.FluentLauncher/Views/Cores/ManageNavigationPage.xaml.cs b/Natsurainko.FluentLauncher/Views/Cores/ManageNavigationPage.xaml.cs index ccd7ce9a..224204a8 100644 --- a/Natsurainko.FluentLauncher/Views/Cores/ManageNavigationPage.xaml.cs +++ b/Natsurainko.FluentLauncher/Views/Cores/ManageNavigationPage.xaml.cs @@ -1,5 +1,6 @@ using Microsoft.UI.Xaml.Controls; using Natsurainko.FluentLauncher.Services.UI.Navigation; +using Natsurainko.FluentLauncher.Utils; using Natsurainko.FluentLauncher.ViewModels.Cores; namespace Natsurainko.FluentLauncher.Views.Cores; @@ -15,5 +16,5 @@ public ManageNavigationPage() } private void NavigationView_ItemInvoked(NavigationView _, NavigationViewItemInvokedEventArgs args) - => VM.NavigateTo(((NavigationViewItem)args.InvokedItemContainer).Tag.ToString(), VM._gameInfo); + => VM.NavigateTo(((NavigationViewItem)args.InvokedItemContainer).GetTag(), VM._gameInfo); } diff --git a/Natsurainko.FluentLauncher/Views/Home/NewHomePage.xaml.cs b/Natsurainko.FluentLauncher/Views/Home/NewHomePage.xaml.cs index 185faa01..08ae4c01 100644 --- a/Natsurainko.FluentLauncher/Views/Home/NewHomePage.xaml.cs +++ b/Natsurainko.FluentLauncher/Views/Home/NewHomePage.xaml.cs @@ -12,7 +12,7 @@ public NewHomePage() private void Button_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) { - var vm = DataContext as HomeViewModel; + var vm = (HomeViewModel)DataContext; splitView.IsPaneOpen = !splitView.IsPaneOpen; if (splitView.IsPaneOpen && vm.ActiveGameInfo != null) diff --git a/Natsurainko.FluentLauncher/Views/OOBE/AccountPage.xaml.cs b/Natsurainko.FluentLauncher/Views/OOBE/AccountPage.xaml.cs index a131678c..e8869c95 100644 --- a/Natsurainko.FluentLauncher/Views/OOBE/AccountPage.xaml.cs +++ b/Natsurainko.FluentLauncher/Views/OOBE/AccountPage.xaml.cs @@ -11,15 +11,15 @@ public AccountPage() private void Grid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) { - var grid = sender as Grid; - var button = grid.FindName("DeleteButton") as Button; + var grid = (Grid)sender; + var button = (Button)grid.FindName("DeleteButton"); button.Visibility = Microsoft.UI.Xaml.Visibility.Visible; } private void Grid_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) { - var grid = sender as Grid; - var button = grid.FindName("DeleteButton") as Button; + var grid = (Grid)sender; + var button = (Button)grid.FindName("DeleteButton"); button.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed; } } diff --git a/Natsurainko.FluentLauncher/Views/OOBE/JavaPage.xaml.cs b/Natsurainko.FluentLauncher/Views/OOBE/JavaPage.xaml.cs index 75194a18..0d83e89f 100644 --- a/Natsurainko.FluentLauncher/Views/OOBE/JavaPage.xaml.cs +++ b/Natsurainko.FluentLauncher/Views/OOBE/JavaPage.xaml.cs @@ -11,15 +11,15 @@ public JavaPage() private void Grid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) { - var grid = sender as Grid; - var button = grid.FindName("DeleteButton") as Button; + var grid = (Grid)sender; + var button = (Button)grid.FindName("DeleteButton"); button.Visibility = Microsoft.UI.Xaml.Visibility.Visible; } private void Grid_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) { - var grid = sender as Grid; - var button = grid.FindName("DeleteButton") as Button; + var grid = (Grid)sender; + var button = (Button)grid.FindName("DeleteButton"); button.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed; } } diff --git a/Natsurainko.FluentLauncher/Views/OOBE/MinecraftFolderPage.xaml.cs b/Natsurainko.FluentLauncher/Views/OOBE/MinecraftFolderPage.xaml.cs index c4a98d4f..9fdc6e87 100644 --- a/Natsurainko.FluentLauncher/Views/OOBE/MinecraftFolderPage.xaml.cs +++ b/Natsurainko.FluentLauncher/Views/OOBE/MinecraftFolderPage.xaml.cs @@ -11,15 +11,15 @@ public MinecraftFolderPage() private void Grid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) { - var grid = sender as Grid; - var button = grid.FindName("DeleteButton") as Button; + var grid = (Grid)sender; + var button = (Button)grid.FindName("DeleteButton"); button.Visibility = Microsoft.UI.Xaml.Visibility.Visible; } private void Grid_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) { - var grid = sender as Grid; - var button = grid.FindName("DeleteButton") as Button; + var grid = (Grid)sender; + var button = (Button)grid.FindName("DeleteButton"); button.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed; } } diff --git a/Natsurainko.FluentLauncher/Views/OOBE/OOBENavigationPage.xaml.cs b/Natsurainko.FluentLauncher/Views/OOBE/OOBENavigationPage.xaml.cs index 610f544b..2d810354 100644 --- a/Natsurainko.FluentLauncher/Views/OOBE/OOBENavigationPage.xaml.cs +++ b/Natsurainko.FluentLauncher/Views/OOBE/OOBENavigationPage.xaml.cs @@ -4,6 +4,7 @@ using Microsoft.UI.Xaml.Navigation; using Natsurainko.FluentLauncher.Services.UI.Navigation; using Natsurainko.FluentLauncher.Services.UI.Pages; +using Natsurainko.FluentLauncher.Utils; using Natsurainko.FluentLauncher.ViewModels.OOBE; using System.Linq; @@ -35,7 +36,7 @@ private void NavigationViewControl_ItemInvoked(NavigationView sender, Navigation int sourcePageIndex = VM.CurrentPageIndex; var navigationViewItems = sender.MenuItems.Union(sender.FooterMenuItems).Cast().Select(item => item.Tag).Cast().ToList(); - string pageTag = ((NavigationViewItem)args.InvokedItemContainer).Tag.ToString(); + string pageTag = ((NavigationViewItem)args.InvokedItemContainer).GetTag(); int targetPageIndex = navigationViewItems.IndexOf(pageTag); // Set transition direction @@ -64,7 +65,7 @@ private void ContentFrame_Navigated(object sender, NavigationEventArgs e) { foreach (NavigationViewItem item in NavigationView.MenuItems.Union(NavigationView.FooterMenuItems).Cast()) { - if (App.GetService().RegisteredPages[item.Tag.ToString()].PageType == e.SourcePageType) + if (App.GetService().RegisteredPages[item.GetTag()].PageType == e.SourcePageType) { bypassTransitionUpdate = true; NavigationView.SelectedItem = item; diff --git a/Natsurainko.FluentLauncher/Views/Settings/NavigationPage.xaml.cs b/Natsurainko.FluentLauncher/Views/Settings/NavigationPage.xaml.cs index ae56d68d..4379a317 100644 --- a/Natsurainko.FluentLauncher/Views/Settings/NavigationPage.xaml.cs +++ b/Natsurainko.FluentLauncher/Views/Settings/NavigationPage.xaml.cs @@ -2,7 +2,9 @@ using Microsoft.UI.Xaml.Navigation; using Natsurainko.FluentLauncher.Services.UI.Navigation; using Natsurainko.FluentLauncher.Services.UI.Pages; +using Natsurainko.FluentLauncher.Utils; using Natsurainko.FluentLauncher.ViewModels.Settings; +using System; using System.Linq; namespace Natsurainko.FluentLauncher.Views.Settings; @@ -20,13 +22,16 @@ public NavigationPage() #region Sync NavigationViewItem selection private void NavigationView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) - => VM.NavigateTo(((NavigationViewItem)args.InvokedItemContainer).Tag.ToString()); + { + var navItem = (NavigationViewItem)args.InvokedItemContainer; + VM.NavigateTo(navItem.GetTag()); + } private void ContentFrame_Navigated(object sender, NavigationEventArgs e) { foreach (NavigationViewItem item in NavigationView.MenuItems.Union(NavigationView.FooterMenuItems).Cast()) { - if (App.GetService().RegisteredPages[item.Tag.ToString()].PageType == e.SourcePageType) + if (App.GetService().RegisteredPages[item.GetTag()].PageType == e.SourcePageType) { NavigationView.SelectedItem = item; item.IsSelected = true; diff --git a/Natsurainko.FluentLauncher/Views/ShellPage.xaml.cs b/Natsurainko.FluentLauncher/Views/ShellPage.xaml.cs index 50906d9a..c63d7d1c 100644 --- a/Natsurainko.FluentLauncher/Views/ShellPage.xaml.cs +++ b/Natsurainko.FluentLauncher/Views/ShellPage.xaml.cs @@ -6,6 +6,7 @@ using Natsurainko.FluentLauncher.Services.UI; using Natsurainko.FluentLauncher.Services.UI.Navigation; using Natsurainko.FluentLauncher.Services.UI.Pages; +using Natsurainko.FluentLauncher.Utils; using Natsurainko.FluentLauncher.ViewModels; using Natsurainko.FluentLauncher.Views.Home; using System; @@ -16,8 +17,8 @@ namespace Natsurainko.FluentLauncher.Views; public sealed partial class ShellPage : Page, INavigationProvider { - public static XamlRoot _XamlRoot { get; private set; } - public static Frame ContentFrame { get; private set; } + public static XamlRoot _XamlRoot { get; private set; } = null!; // Initialized on Page_Loaded + public static Frame ContentFrame { get; private set; } = null!; // Initialized on Page_Loaded object INavigationProvider.NavigationControl => contentFrame; private ShellViewModel VM => (ShellViewModel)DataContext; @@ -44,7 +45,8 @@ public ShellPage() private void NavigationViewControl_ItemInvoked(NavigationView _, NavigationViewItemInvokedEventArgs args) { - var pageTag = ((NavigationViewItem)args.InvokedItemContainer).Tag.ToString(); + var pageTag = ((NavigationViewItem)args.InvokedItemContainer).Tag.ToString() + ?? throw new ArgumentNullException("The invoked item's tag is null."); if (pageTag == "HomePage" && _settings.UseNewHomePage) pageTag = "NewHomePage"; @@ -88,13 +90,15 @@ private void ContentFrame_Navigated(object sender, NavigationEventArgs e) { foreach (NavigationViewItem item in NavigationViewControl.MenuItems.Union(NavigationViewControl.FooterMenuItems).Cast()) { - if (App.GetService().RegisteredPages[item.Tag.ToString()].PageType == e.SourcePageType) + string tag = item.GetTag(); + + if (App.GetService().RegisteredPages[tag].PageType == e.SourcePageType) { NavigationViewControl.SelectedItem = item; item.IsSelected = true; return; } - if (e.SourcePageType == typeof(NewHomePage) && item.Tag.ToString() == "HomePage") + if (e.SourcePageType == typeof(NewHomePage) && tag == "HomePage") { NavigationViewControl.SelectedItem = item; item.IsSelected = true;