mirror of
https://github.com/PacktPublishing/Learn-WinUI-3-Second-Edition.git
synced 2026-06-20 12:23:09 +00:00
Add chapter 10 sample project
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
using TemplateStudioSampleApp.Activation;
|
||||
using TemplateStudioSampleApp.Contracts.Services;
|
||||
using TemplateStudioSampleApp.Views;
|
||||
|
||||
namespace TemplateStudioSampleApp.Services;
|
||||
|
||||
public class ActivationService : IActivationService
|
||||
{
|
||||
private readonly ActivationHandler<LaunchActivatedEventArgs> _defaultHandler;
|
||||
private readonly IEnumerable<IActivationHandler> _activationHandlers;
|
||||
private readonly IThemeSelectorService _themeSelectorService;
|
||||
private UIElement? _shell = null;
|
||||
|
||||
public ActivationService(ActivationHandler<LaunchActivatedEventArgs> defaultHandler, IEnumerable<IActivationHandler> activationHandlers, IThemeSelectorService themeSelectorService)
|
||||
{
|
||||
_defaultHandler = defaultHandler;
|
||||
_activationHandlers = activationHandlers;
|
||||
_themeSelectorService = themeSelectorService;
|
||||
}
|
||||
|
||||
public async Task ActivateAsync(object activationArgs)
|
||||
{
|
||||
// Execute tasks before activation.
|
||||
await InitializeAsync();
|
||||
|
||||
// Set the MainWindow Content.
|
||||
if (App.MainWindow.Content == null)
|
||||
{
|
||||
_shell = App.GetService<ShellPage>();
|
||||
App.MainWindow.Content = _shell ?? new Frame();
|
||||
}
|
||||
|
||||
// Handle activation via ActivationHandlers.
|
||||
await HandleActivationAsync(activationArgs);
|
||||
|
||||
// Activate the MainWindow.
|
||||
App.MainWindow.Activate();
|
||||
|
||||
// Execute tasks after activation.
|
||||
await StartupAsync();
|
||||
}
|
||||
|
||||
private async Task HandleActivationAsync(object activationArgs)
|
||||
{
|
||||
var activationHandler = _activationHandlers.FirstOrDefault(h => h.CanHandle(activationArgs));
|
||||
|
||||
if (activationHandler != null)
|
||||
{
|
||||
await activationHandler.HandleAsync(activationArgs);
|
||||
}
|
||||
|
||||
if (_defaultHandler.CanHandle(activationArgs))
|
||||
{
|
||||
await _defaultHandler.HandleAsync(activationArgs);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InitializeAsync()
|
||||
{
|
||||
await _themeSelectorService.InitializeAsync().ConfigureAwait(false);
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task StartupAsync()
|
||||
{
|
||||
await _themeSelectorService.SetRequestedThemeAsync();
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
using TemplateStudioSampleApp.Contracts.Services;
|
||||
using TemplateStudioSampleApp.Core.Contracts.Services;
|
||||
using TemplateStudioSampleApp.Core.Helpers;
|
||||
using TemplateStudioSampleApp.Helpers;
|
||||
using TemplateStudioSampleApp.Models;
|
||||
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace TemplateStudioSampleApp.Services;
|
||||
|
||||
public class LocalSettingsService : ILocalSettingsService
|
||||
{
|
||||
private const string _defaultApplicationDataFolder = "TemplateStudioSampleApp/ApplicationData";
|
||||
private const string _defaultLocalSettingsFile = "LocalSettings.json";
|
||||
|
||||
private readonly IFileService _fileService;
|
||||
private readonly LocalSettingsOptions _options;
|
||||
|
||||
private readonly string _localApplicationData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
private readonly string _applicationDataFolder;
|
||||
private readonly string _localsettingsFile;
|
||||
|
||||
private IDictionary<string, object> _settings;
|
||||
|
||||
private bool _isInitialized;
|
||||
|
||||
public LocalSettingsService(IFileService fileService, IOptions<LocalSettingsOptions> options)
|
||||
{
|
||||
_fileService = fileService;
|
||||
_options = options.Value;
|
||||
|
||||
_applicationDataFolder = Path.Combine(_localApplicationData, _options.ApplicationDataFolder ?? _defaultApplicationDataFolder);
|
||||
_localsettingsFile = _options.LocalSettingsFile ?? _defaultLocalSettingsFile;
|
||||
|
||||
_settings = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
private async Task InitializeAsync()
|
||||
{
|
||||
if (!_isInitialized)
|
||||
{
|
||||
_settings = await Task.Run(() => _fileService.Read<IDictionary<string, object>>(_applicationDataFolder, _localsettingsFile)) ?? new Dictionary<string, object>();
|
||||
|
||||
_isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<T?> ReadSettingAsync<T>(string key)
|
||||
{
|
||||
if (RuntimeHelper.IsMSIX)
|
||||
{
|
||||
if (ApplicationData.Current.LocalSettings.Values.TryGetValue(key, out var obj))
|
||||
{
|
||||
return await Json.ToObjectAsync<T>((string)obj);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await InitializeAsync();
|
||||
|
||||
if (_settings != null && _settings.TryGetValue(key, out var obj))
|
||||
{
|
||||
return await Json.ToObjectAsync<T>((string)obj);
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public async Task SaveSettingAsync<T>(string key, T value)
|
||||
{
|
||||
if (RuntimeHelper.IsMSIX)
|
||||
{
|
||||
ApplicationData.Current.LocalSettings.Values[key] = await Json.StringifyAsync(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
await InitializeAsync();
|
||||
|
||||
_settings[key] = await Json.StringifyAsync(value);
|
||||
|
||||
await Task.Run(() => _fileService.Save(_applicationDataFolder, _localsettingsFile, _settings));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
using TemplateStudioSampleApp.Contracts.Services;
|
||||
using TemplateStudioSampleApp.Contracts.ViewModels;
|
||||
using TemplateStudioSampleApp.Helpers;
|
||||
|
||||
namespace TemplateStudioSampleApp.Services;
|
||||
|
||||
// For more information on navigation between pages see
|
||||
// https://github.com/microsoft/TemplateStudio/blob/main/docs/WinUI/navigation.md
|
||||
public class NavigationService : INavigationService
|
||||
{
|
||||
private readonly IPageService _pageService;
|
||||
private object? _lastParameterUsed;
|
||||
private Frame? _frame;
|
||||
|
||||
public event NavigatedEventHandler? Navigated;
|
||||
|
||||
public Frame? Frame
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_frame == null)
|
||||
{
|
||||
_frame = App.MainWindow.Content as Frame;
|
||||
RegisterFrameEvents();
|
||||
}
|
||||
|
||||
return _frame;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
UnregisterFrameEvents();
|
||||
_frame = value;
|
||||
RegisterFrameEvents();
|
||||
}
|
||||
}
|
||||
|
||||
[MemberNotNullWhen(true, nameof(Frame), nameof(_frame))]
|
||||
public bool CanGoBack => Frame != null && Frame.CanGoBack;
|
||||
|
||||
public NavigationService(IPageService pageService)
|
||||
{
|
||||
_pageService = pageService;
|
||||
}
|
||||
|
||||
private void RegisterFrameEvents()
|
||||
{
|
||||
if (_frame != null)
|
||||
{
|
||||
_frame.Navigated += OnNavigated;
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterFrameEvents()
|
||||
{
|
||||
if (_frame != null)
|
||||
{
|
||||
_frame.Navigated -= OnNavigated;
|
||||
}
|
||||
}
|
||||
|
||||
public bool GoBack()
|
||||
{
|
||||
if (CanGoBack)
|
||||
{
|
||||
var vmBeforeNavigation = _frame.GetPageViewModel();
|
||||
_frame.GoBack();
|
||||
if (vmBeforeNavigation is INavigationAware navigationAware)
|
||||
{
|
||||
navigationAware.OnNavigatedFrom();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool NavigateTo(string pageKey, object? parameter = null, bool clearNavigation = false)
|
||||
{
|
||||
var pageType = _pageService.GetPageType(pageKey);
|
||||
|
||||
if (_frame != null && (_frame.Content?.GetType() != pageType || (parameter != null && !parameter.Equals(_lastParameterUsed))))
|
||||
{
|
||||
_frame.Tag = clearNavigation;
|
||||
var vmBeforeNavigation = _frame.GetPageViewModel();
|
||||
var navigated = _frame.Navigate(pageType, parameter);
|
||||
if (navigated)
|
||||
{
|
||||
_lastParameterUsed = parameter;
|
||||
if (vmBeforeNavigation is INavigationAware navigationAware)
|
||||
{
|
||||
navigationAware.OnNavigatedFrom();
|
||||
}
|
||||
}
|
||||
|
||||
return navigated;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnNavigated(object sender, NavigationEventArgs e)
|
||||
{
|
||||
if (sender is Frame frame)
|
||||
{
|
||||
var clearNavigation = (bool)frame.Tag;
|
||||
if (clearNavigation)
|
||||
{
|
||||
frame.BackStack.Clear();
|
||||
}
|
||||
|
||||
if (frame.GetPageViewModel() is INavigationAware navigationAware)
|
||||
{
|
||||
navigationAware.OnNavigatedTo(e.Parameter);
|
||||
}
|
||||
|
||||
Navigated?.Invoke(sender, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
using TemplateStudioSampleApp.Contracts.Services;
|
||||
using TemplateStudioSampleApp.Helpers;
|
||||
using TemplateStudioSampleApp.ViewModels;
|
||||
|
||||
namespace TemplateStudioSampleApp.Services;
|
||||
|
||||
public class NavigationViewService : INavigationViewService
|
||||
{
|
||||
private readonly INavigationService _navigationService;
|
||||
|
||||
private readonly IPageService _pageService;
|
||||
|
||||
private NavigationView? _navigationView;
|
||||
|
||||
public IList<object>? MenuItems => _navigationView?.MenuItems;
|
||||
|
||||
public object? SettingsItem => _navigationView?.SettingsItem;
|
||||
|
||||
public NavigationViewService(INavigationService navigationService, IPageService pageService)
|
||||
{
|
||||
_navigationService = navigationService;
|
||||
_pageService = pageService;
|
||||
}
|
||||
|
||||
[MemberNotNull(nameof(_navigationView))]
|
||||
public void Initialize(NavigationView navigationView)
|
||||
{
|
||||
_navigationView = navigationView;
|
||||
_navigationView.BackRequested += OnBackRequested;
|
||||
_navigationView.ItemInvoked += OnItemInvoked;
|
||||
}
|
||||
|
||||
public void UnregisterEvents()
|
||||
{
|
||||
if (_navigationView != null)
|
||||
{
|
||||
_navigationView.BackRequested -= OnBackRequested;
|
||||
_navigationView.ItemInvoked -= OnItemInvoked;
|
||||
}
|
||||
}
|
||||
|
||||
public NavigationViewItem? GetSelectedItem(Type pageType)
|
||||
{
|
||||
if (_navigationView != null)
|
||||
{
|
||||
return GetSelectedItem(_navigationView.MenuItems, pageType) ?? GetSelectedItem(_navigationView.FooterMenuItems, pageType);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void OnBackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args) => _navigationService.GoBack();
|
||||
|
||||
private void OnItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
|
||||
{
|
||||
if (args.IsSettingsInvoked)
|
||||
{
|
||||
_navigationService.NavigateTo(typeof(SettingsViewModel).FullName!);
|
||||
}
|
||||
else
|
||||
{
|
||||
var selectedItem = args.InvokedItemContainer as NavigationViewItem;
|
||||
|
||||
if (selectedItem?.GetValue(NavigationHelper.NavigateToProperty) is string pageKey)
|
||||
{
|
||||
_navigationService.NavigateTo(pageKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private NavigationViewItem? GetSelectedItem(IEnumerable<object> menuItems, Type pageType)
|
||||
{
|
||||
foreach (var item in menuItems.OfType<NavigationViewItem>())
|
||||
{
|
||||
if (IsMenuItemForPageType(item, pageType))
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
var selectedChild = GetSelectedItem(item.MenuItems, pageType);
|
||||
if (selectedChild != null)
|
||||
{
|
||||
return selectedChild;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool IsMenuItemForPageType(NavigationViewItem menuItem, Type sourcePageType)
|
||||
{
|
||||
if (menuItem.GetValue(NavigationHelper.NavigateToProperty) is string pageKey)
|
||||
{
|
||||
return _pageService.GetPageType(pageKey) == sourcePageType;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
using TemplateStudioSampleApp.Contracts.Services;
|
||||
using TemplateStudioSampleApp.ViewModels;
|
||||
using TemplateStudioSampleApp.Views;
|
||||
|
||||
namespace TemplateStudioSampleApp.Services;
|
||||
|
||||
public class PageService : IPageService
|
||||
{
|
||||
private readonly Dictionary<string, Type> _pages = new();
|
||||
|
||||
public PageService()
|
||||
{
|
||||
Configure<MainViewModel, MainPage>();
|
||||
Configure<DataGridViewModel, DataGridPage>();
|
||||
Configure<ListDetailsViewModel, ListDetailsPage>();
|
||||
Configure<WebViewViewModel, WebViewPage>();
|
||||
Configure<SettingsViewModel, SettingsPage>();
|
||||
}
|
||||
|
||||
public Type GetPageType(string key)
|
||||
{
|
||||
Type? pageType;
|
||||
lock (_pages)
|
||||
{
|
||||
if (!_pages.TryGetValue(key, out pageType))
|
||||
{
|
||||
throw new ArgumentException($"Page not found: {key}. Did you forget to call PageService.Configure?");
|
||||
}
|
||||
}
|
||||
|
||||
return pageType;
|
||||
}
|
||||
|
||||
private void Configure<VM, V>()
|
||||
where VM : ObservableObject
|
||||
where V : Page
|
||||
{
|
||||
lock (_pages)
|
||||
{
|
||||
var key = typeof(VM).FullName!;
|
||||
if (_pages.ContainsKey(key))
|
||||
{
|
||||
throw new ArgumentException($"The key {key} is already configured in PageService");
|
||||
}
|
||||
|
||||
var type = typeof(V);
|
||||
if (_pages.ContainsValue(type))
|
||||
{
|
||||
throw new ArgumentException($"This type is already configured with key {_pages.First(p => p.Value == type).Key}");
|
||||
}
|
||||
|
||||
_pages.Add(key, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
using TemplateStudioSampleApp.Contracts.Services;
|
||||
using TemplateStudioSampleApp.Helpers;
|
||||
|
||||
namespace TemplateStudioSampleApp.Services;
|
||||
|
||||
public class ThemeSelectorService : IThemeSelectorService
|
||||
{
|
||||
private const string SettingsKey = "AppBackgroundRequestedTheme";
|
||||
|
||||
public ElementTheme Theme { get; set; } = ElementTheme.Default;
|
||||
|
||||
private readonly ILocalSettingsService _localSettingsService;
|
||||
|
||||
public ThemeSelectorService(ILocalSettingsService localSettingsService)
|
||||
{
|
||||
_localSettingsService = localSettingsService;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
Theme = await LoadThemeFromSettingsAsync();
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task SetThemeAsync(ElementTheme theme)
|
||||
{
|
||||
Theme = theme;
|
||||
|
||||
await SetRequestedThemeAsync();
|
||||
await SaveThemeInSettingsAsync(Theme);
|
||||
}
|
||||
|
||||
public async Task SetRequestedThemeAsync()
|
||||
{
|
||||
if (App.MainWindow.Content is FrameworkElement rootElement)
|
||||
{
|
||||
rootElement.RequestedTheme = Theme;
|
||||
|
||||
TitleBarHelper.UpdateTitleBar(Theme);
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task<ElementTheme> LoadThemeFromSettingsAsync()
|
||||
{
|
||||
var themeName = await _localSettingsService.ReadSettingAsync<string>(SettingsKey);
|
||||
|
||||
if (Enum.TryParse(themeName, out ElementTheme cacheTheme))
|
||||
{
|
||||
return cacheTheme;
|
||||
}
|
||||
|
||||
return ElementTheme.Default;
|
||||
}
|
||||
|
||||
private async Task SaveThemeInSettingsAsync(ElementTheme theme)
|
||||
{
|
||||
await _localSettingsService.SaveSettingAsync(SettingsKey, theme.ToString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
|
||||
using TemplateStudioSampleApp.Contracts.Services;
|
||||
|
||||
namespace TemplateStudioSampleApp.Services;
|
||||
|
||||
public class WebViewService : IWebViewService
|
||||
{
|
||||
private WebView2? _webView;
|
||||
|
||||
public Uri? Source => _webView?.Source;
|
||||
|
||||
[MemberNotNullWhen(true, nameof(_webView))]
|
||||
public bool CanGoBack => _webView != null && _webView.CanGoBack;
|
||||
|
||||
[MemberNotNullWhen(true, nameof(_webView))]
|
||||
public bool CanGoForward => _webView != null && _webView.CanGoForward;
|
||||
|
||||
public event EventHandler<CoreWebView2WebErrorStatus>? NavigationCompleted;
|
||||
|
||||
public WebViewService()
|
||||
{
|
||||
}
|
||||
|
||||
[MemberNotNull(nameof(_webView))]
|
||||
public void Initialize(WebView2 webView)
|
||||
{
|
||||
_webView = webView;
|
||||
_webView.NavigationCompleted += OnWebViewNavigationCompleted;
|
||||
}
|
||||
|
||||
public void GoBack() => _webView?.GoBack();
|
||||
|
||||
public void GoForward() => _webView?.GoForward();
|
||||
|
||||
public void Reload() => _webView?.Reload();
|
||||
|
||||
public void UnregisterEvents()
|
||||
{
|
||||
if (_webView != null)
|
||||
{
|
||||
_webView.NavigationCompleted -= OnWebViewNavigationCompleted;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWebViewNavigationCompleted(WebView2 sender, CoreWebView2NavigationCompletedEventArgs args) => NavigationCompleted?.Invoke(this, args.WebErrorStatus);
|
||||
}
|
||||
Reference in New Issue
Block a user