From 88ea3e152b7e7887970bf519eb18f1470a2eb757 Mon Sep 17 00:00:00 2001 From: zlk-sys <18079031125@163.com> Date: Fri, 16 Aug 2024 04:25:22 +0800 Subject: [PATCH] 0.0.1.0 --- .editorconfig | 202 +++++++++ .gitignore | 398 ++++++++++++++++++ .vsconfig | 16 + HCM.Core/Contracts/Services/IFileService.cs | 10 + HCM.Core/HCM.Core.csproj | 13 + HCM.Core/Helpers/Json.cs | 22 + HCM.Core/Models/ListCollections.cs | 36 ++ HCM.Core/README.md | 5 + HCM.Core/Services/FileService.cs | 41 ++ HCM.sln | 69 +++ HCM/Activation/ActivationHandler.cs | 17 + .../AppNotificationActivationHandler.cs | 51 +++ HCM/Activation/DefaultActivationHandler.cs | 29 ++ HCM/Activation/IActivationHandler.cs | 8 + HCM/App.xaml | 15 + HCM/App.xaml.cs | 108 +++++ HCM/Assets/LockScreenLogo.scale-200.png | Bin 0 -> 2784 bytes HCM/Assets/SplashScreen.scale-200.png | Bin 0 -> 27165 bytes HCM/Assets/Square150x150Logo.scale-200.png | Bin 0 -> 9868 bytes HCM/Assets/Square44x44Logo.scale-200.png | Bin 0 -> 3634 bytes ...x44Logo.targetsize-24_altform-unplated.png | Bin 0 -> 2160 bytes HCM/Assets/StoreLogo.png | Bin 0 -> 2832 bytes HCM/Assets/Wide310x150Logo.scale-200.png | Bin 0 -> 8768 bytes HCM/Assets/WindowIcon.ico | Bin 0 -> 9662 bytes HCM/Behaviors/NavigationViewHeaderBehavior.cs | 122 ++++++ HCM/Behaviors/NavigationViewHeaderMode.cs | 8 + HCM/Contracts/Services/IActivationService.cs | 6 + .../Services/IAppNotificationService.cs | 14 + .../Services/ILocalSettingsService.cs | 8 + HCM/Contracts/Services/INavigationService.cs | 23 + .../Services/INavigationViewService.cs | 22 + HCM/Contracts/Services/IPageService.cs | 6 + .../Services/IThemeSelectorService.cs | 17 + HCM/Contracts/ViewModels/INavigationAware.cs | 8 + HCM/HCM.csproj | 60 +++ HCM/Helpers/EnumToBooleanConverter.cs | 38 ++ HCM/Helpers/FrameExtensions.cs | 8 + HCM/Helpers/NavigationHelper.cs | 21 + HCM/Helpers/ResourceExtensions.cs | 10 + HCM/Helpers/RuntimeHelper.cs | 20 + HCM/Helpers/SettingsStorageExtensions.cs | 112 +++++ HCM/Helpers/TitleBarHelper.cs | 96 +++++ HCM/MainWindow.xaml | 16 + HCM/MainWindow.xaml.cs | 37 ++ HCM/Models/LocalSettingsOptions.cs | 14 + HCM/Package.appinstaller | 17 + HCM/Package.appxmanifest | 76 ++++ HCM/Properties/launchsettings.json | 10 + HCM/README.md | 27 ++ HCM/Services/ActivationService.cs | 72 ++++ HCM/Services/AppNotificationService.cs | 71 ++++ HCM/Services/LocalSettingsService.cs | 88 ++++ HCM/Services/NavigationService.cs | 126 ++++++ HCM/Services/NavigationViewService.cs | 103 +++++ HCM/Services/PageService.cs | 57 +++ HCM/Services/ThemeSelectorService.cs | 63 +++ HCM/Strings/en-us/Resources.resw | 203 +++++++++ HCM/Styles/FontSizes.xaml | 9 + HCM/Styles/TextBlock.xaml | 20 + HCM/Styles/Thickness.xaml | 36 ++ HCM/TemplateStudio.xml | 11 + HCM/Usings.cs | 1 + HCM/ViewModels/EMU_RoutingViewModel.cs | 10 + HCM/ViewModels/MainViewModel.cs | 10 + HCM/ViewModels/SettingsViewModel.cs | 65 +++ HCM/ViewModels/ShellViewModel.cs | 51 +++ HCM/Views/EMU_RoutingPage.xaml | 64 +++ HCM/Views/EMU_RoutingPage.xaml.cs | 82 ++++ HCM/Views/MainPage.xaml | 12 + HCM/Views/MainPage.xaml.cs | 19 + HCM/Views/SettingsPage.xaml | 67 +++ HCM/Views/SettingsPage.xaml.cs | 20 + HCM/Views/ShellPage.xaml | 85 ++++ HCM/Views/ShellPage.xaml.cs | 85 ++++ HCM/app.manifest | 15 + HCM/appsettings.json | 6 + LICENSE | 21 + README.md | 2 + 78 files changed, 3310 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .vsconfig create mode 100644 HCM.Core/Contracts/Services/IFileService.cs create mode 100644 HCM.Core/HCM.Core.csproj create mode 100644 HCM.Core/Helpers/Json.cs create mode 100644 HCM.Core/Models/ListCollections.cs create mode 100644 HCM.Core/README.md create mode 100644 HCM.Core/Services/FileService.cs create mode 100644 HCM.sln create mode 100644 HCM/Activation/ActivationHandler.cs create mode 100644 HCM/Activation/AppNotificationActivationHandler.cs create mode 100644 HCM/Activation/DefaultActivationHandler.cs create mode 100644 HCM/Activation/IActivationHandler.cs create mode 100644 HCM/App.xaml create mode 100644 HCM/App.xaml.cs create mode 100644 HCM/Assets/LockScreenLogo.scale-200.png create mode 100644 HCM/Assets/SplashScreen.scale-200.png create mode 100644 HCM/Assets/Square150x150Logo.scale-200.png create mode 100644 HCM/Assets/Square44x44Logo.scale-200.png create mode 100644 HCM/Assets/Square44x44Logo.targetsize-24_altform-unplated.png create mode 100644 HCM/Assets/StoreLogo.png create mode 100644 HCM/Assets/Wide310x150Logo.scale-200.png create mode 100644 HCM/Assets/WindowIcon.ico create mode 100644 HCM/Behaviors/NavigationViewHeaderBehavior.cs create mode 100644 HCM/Behaviors/NavigationViewHeaderMode.cs create mode 100644 HCM/Contracts/Services/IActivationService.cs create mode 100644 HCM/Contracts/Services/IAppNotificationService.cs create mode 100644 HCM/Contracts/Services/ILocalSettingsService.cs create mode 100644 HCM/Contracts/Services/INavigationService.cs create mode 100644 HCM/Contracts/Services/INavigationViewService.cs create mode 100644 HCM/Contracts/Services/IPageService.cs create mode 100644 HCM/Contracts/Services/IThemeSelectorService.cs create mode 100644 HCM/Contracts/ViewModels/INavigationAware.cs create mode 100644 HCM/HCM.csproj create mode 100644 HCM/Helpers/EnumToBooleanConverter.cs create mode 100644 HCM/Helpers/FrameExtensions.cs create mode 100644 HCM/Helpers/NavigationHelper.cs create mode 100644 HCM/Helpers/ResourceExtensions.cs create mode 100644 HCM/Helpers/RuntimeHelper.cs create mode 100644 HCM/Helpers/SettingsStorageExtensions.cs create mode 100644 HCM/Helpers/TitleBarHelper.cs create mode 100644 HCM/MainWindow.xaml create mode 100644 HCM/MainWindow.xaml.cs create mode 100644 HCM/Models/LocalSettingsOptions.cs create mode 100644 HCM/Package.appinstaller create mode 100644 HCM/Package.appxmanifest create mode 100644 HCM/Properties/launchsettings.json create mode 100644 HCM/README.md create mode 100644 HCM/Services/ActivationService.cs create mode 100644 HCM/Services/AppNotificationService.cs create mode 100644 HCM/Services/LocalSettingsService.cs create mode 100644 HCM/Services/NavigationService.cs create mode 100644 HCM/Services/NavigationViewService.cs create mode 100644 HCM/Services/PageService.cs create mode 100644 HCM/Services/ThemeSelectorService.cs create mode 100644 HCM/Strings/en-us/Resources.resw create mode 100644 HCM/Styles/FontSizes.xaml create mode 100644 HCM/Styles/TextBlock.xaml create mode 100644 HCM/Styles/Thickness.xaml create mode 100644 HCM/TemplateStudio.xml create mode 100644 HCM/Usings.cs create mode 100644 HCM/ViewModels/EMU_RoutingViewModel.cs create mode 100644 HCM/ViewModels/MainViewModel.cs create mode 100644 HCM/ViewModels/SettingsViewModel.cs create mode 100644 HCM/ViewModels/ShellViewModel.cs create mode 100644 HCM/Views/EMU_RoutingPage.xaml create mode 100644 HCM/Views/EMU_RoutingPage.xaml.cs create mode 100644 HCM/Views/MainPage.xaml create mode 100644 HCM/Views/MainPage.xaml.cs create mode 100644 HCM/Views/SettingsPage.xaml create mode 100644 HCM/Views/SettingsPage.xaml.cs create mode 100644 HCM/Views/ShellPage.xaml create mode 100644 HCM/Views/ShellPage.xaml.cs create mode 100644 HCM/app.manifest create mode 100644 HCM/appsettings.json create mode 100644 LICENSE create mode 100644 README.md diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..fd05618 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,202 @@ +# Rules in this file were initially inferred by Visual Studio IntelliCode from the Template Studio codebase. +# You can modify the rules from these initially generated values to suit your own policies. +# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference. + +[*.cs] + +#Core editorconfig formatting - indentation + +#use soft tabs (spaces) for indentation +indent_style = space + +#Formatting - new line options + +#place else statements on a new line +csharp_new_line_before_else = true +#require braces to be on a new line for lambdas, methods, control_blocks, types, properties, and accessors (also known as "Allman" style) +csharp_new_line_before_open_brace = all + +#Formatting - organize using options + +#sort System.* using directives alphabetically, and place them before other usings +dotnet_sort_system_directives_first = true + +#Formatting - spacing options + +#require NO space between a cast and the value +csharp_space_after_cast = false +#require a space before the colon for bases or interfaces in a type declaration +csharp_space_after_colon_in_inheritance_clause = true +#require a space after a keyword in a control flow statement such as a for loop +csharp_space_after_keywords_in_control_flow_statements = true +#require a space before the colon for bases or interfaces in a type declaration +csharp_space_before_colon_in_inheritance_clause = true +#remove space within empty argument list parentheses +csharp_space_between_method_call_empty_parameter_list_parentheses = false +#remove space between method call name and opening parenthesis +csharp_space_between_method_call_name_and_opening_parenthesis = false +#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call +csharp_space_between_method_call_parameter_list_parentheses = false +#remove space within empty parameter list parentheses for a method declaration +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list. +csharp_space_between_method_declaration_parameter_list_parentheses = false + +#Formatting - wrapping options + +#leave code block on separate lines +csharp_preserve_single_line_blocks = false + +#Style - Code block preferences + +#prefer curly braces even for one line of code +csharp_prefer_braces = true:suggestion + +#Style - expression bodied member options + +#prefer expression bodies for accessors +csharp_style_expression_bodied_accessors = true:warning +#prefer block bodies for constructors +csharp_style_expression_bodied_constructors = false:suggestion +#prefer expression bodies for methods +csharp_style_expression_bodied_methods = when_on_single_line:silent +#prefer expression-bodied members for properties +csharp_style_expression_bodied_properties = true:warning + +#Style - expression level options + +#prefer out variables to be declared before the method call +csharp_style_inlined_variable_declaration = false:suggestion +#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them +dotnet_style_predefined_type_for_member_access = true:suggestion + +#Style - Expression-level preferences + +#prefer default over default(T) +csharp_prefer_simple_default_expression = true:suggestion +#prefer objects to be initialized using object initializers when possible +dotnet_style_object_initializer = true:suggestion + +#Style - implicit and explicit types + +#prefer var over explicit type in all cases, unless overridden by another code style rule +csharp_style_var_elsewhere = true:suggestion +#prefer var is used to declare variables with built-in system types such as int +csharp_style_var_for_built_in_types = true:suggestion +#prefer var when the type is already mentioned on the right-hand side of a declaration expression +csharp_style_var_when_type_is_apparent = true:suggestion + +#Style - language keyword and framework type options + +#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion + +#Style - Language rules +csharp_style_implicit_object_creation_when_type_is_apparent = true:warning +csharp_style_var_for_built_in_types = true:warning + +#Style - modifier options + +#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods. +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion + +#Style - Modifier preferences + +#when this rule is set to a list of modifiers, prefer the specified ordering. +csharp_preferred_modifier_order = public,private,protected,internal,static,async,readonly,override,sealed,abstract,virtual:warning +dotnet_style_readonly_field = true:warning + +#Style - Pattern matching + +#prefer pattern matching instead of is expression with type casts +csharp_style_pattern_matching_over_as_with_null_check = true:warning + +#Style - qualification options + +#prefer events not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_event = false:suggestion +#prefer fields not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_field = false:suggestion +#prefer methods not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_method = false:suggestion +#prefer properties not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_property = false:suggestion +csharp_indent_labels = one_less_than_current +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:warning +csharp_style_namespace_declarations = file_scoped:warning +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent + +[*.{cs,vb}] +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +[*.{cs,vb}] + +#Style - Unnecessary code rules +csharp_style_unused_value_assignment_preference = discard_variable:warning + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:warning +dotnet_style_prefer_simplified_interpolation = true:suggestion diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a30d25 --- /dev/null +++ b/.gitignore @@ -0,0 +1,398 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml diff --git a/.vsconfig b/.vsconfig new file mode 100644 index 0000000..65d1f73 --- /dev/null +++ b/.vsconfig @@ -0,0 +1,16 @@ +{ + "version": "1.0", + "components": [ + "Microsoft.Component.MSBuild", + "Microsoft.NetCore.Component.Runtime.7.0", + "Microsoft.NetCore.Component.SDK", + "Microsoft.VisualStudio.Component.ManagedDesktop.Core", + "Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites", + "Microsoft.VisualStudio.Component.NuGet", + "Microsoft.VisualStudio.Component.Windows10SDK.19041", + "Microsoft.VisualStudio.Component.Windows10SDK", + "Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging", + "Microsoft.VisualStudio.ComponentGroup.WindowsAppSDK.Cs", + "Microsoft.VisualStudio.Workload.ManagedDesktop" + ] +} diff --git a/HCM.Core/Contracts/Services/IFileService.cs b/HCM.Core/Contracts/Services/IFileService.cs new file mode 100644 index 0000000..ff7f8e8 --- /dev/null +++ b/HCM.Core/Contracts/Services/IFileService.cs @@ -0,0 +1,10 @@ +namespace HCM.Core.Contracts.Services; + +public interface IFileService +{ + T Read(string folderPath, string fileName); + + void Save(string folderPath, string fileName, T content); + + void Delete(string folderPath, string fileName); +} diff --git a/HCM.Core/HCM.Core.csproj b/HCM.Core/HCM.Core.csproj new file mode 100644 index 0000000..610a350 --- /dev/null +++ b/HCM.Core/HCM.Core.csproj @@ -0,0 +1,13 @@ + + + net7.0 + HCM.Core + AnyCPU;x64;x86 + x86;x64;arm64;AnyCPU + enable + + + + + + diff --git a/HCM.Core/Helpers/Json.cs b/HCM.Core/Helpers/Json.cs new file mode 100644 index 0000000..3957f03 --- /dev/null +++ b/HCM.Core/Helpers/Json.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace HCM.Core.Helpers; + +public static class Json +{ + public static async Task ToObjectAsync(string value) + { + return await Task.Run(() => + { + return JsonConvert.DeserializeObject(value); + }); + } + + public static async Task StringifyAsync(object value) + { + return await Task.Run(() => + { + return JsonConvert.SerializeObject(value); + }); + } +} diff --git a/HCM.Core/Models/ListCollections.cs b/HCM.Core/Models/ListCollections.cs new file mode 100644 index 0000000..9af836a --- /dev/null +++ b/HCM.Core/Models/ListCollections.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; + +namespace HCM.Core.Models; +public class TrainInfo +{ + [JsonProperty("emu_no")] + public string 车组号 + { + get; set; + } + [JsonProperty("train_no")] + public string 担当车次 + { + get; set; + } + [JsonProperty("date")] + [JsonConverter(typeof(CustomDateTimeConverter))] + public DateTime 日期 + { + get; set; + } +} +public class CustomDateTimeConverter : IsoDateTimeConverter +{ + public CustomDateTimeConverter() + { + DateTimeFormat = "yyyy-MM-dd HH:mm"; + } +} diff --git a/HCM.Core/README.md b/HCM.Core/README.md new file mode 100644 index 0000000..906c066 --- /dev/null +++ b/HCM.Core/README.md @@ -0,0 +1,5 @@ +*Recommended Markdown Viewer: [Markdown Editor](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.MarkdownEditor2)* + +## Getting Started + +The Core project contains code that can be [reused across multiple application projects](https://docs.microsoft.com/dotnet/standard/net-standard#net-5-and-net-standard). diff --git a/HCM.Core/Services/FileService.cs b/HCM.Core/Services/FileService.cs new file mode 100644 index 0000000..3969e4b --- /dev/null +++ b/HCM.Core/Services/FileService.cs @@ -0,0 +1,41 @@ +using System.Text; + +using HCM.Core.Contracts.Services; + +using Newtonsoft.Json; + +namespace HCM.Core.Services; + +public class FileService : IFileService +{ + public T Read(string folderPath, string fileName) + { + var path = Path.Combine(folderPath, fileName); + if (File.Exists(path)) + { + var json = File.ReadAllText(path); + return JsonConvert.DeserializeObject(json); + } + + return default; + } + + public void Save(string folderPath, string fileName, T content) + { + if (!Directory.Exists(folderPath)) + { + Directory.CreateDirectory(folderPath); + } + + var fileContent = JsonConvert.SerializeObject(content); + File.WriteAllText(Path.Combine(folderPath, fileName), fileContent, Encoding.UTF8); + } + + public void Delete(string folderPath, string fileName) + { + if (fileName != null && File.Exists(Path.Combine(folderPath, fileName))) + { + File.Delete(Path.Combine(folderPath, fileName)); + } + } +} diff --git a/HCM.sln b/HCM.sln new file mode 100644 index 0000000..830824f --- /dev/null +++ b/HCM.sln @@ -0,0 +1,69 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35013.160 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HCM", "HCM\HCM.csproj", "{36AEF8B0-0DC6-41E9-91C7-2532BF15558F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HCM.Core", "HCM.Core\HCM.Core.csproj", "{97469569-3F7C-4D14-858B-A48C1F2B0138}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|arm64 = Debug|arm64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|arm64 = Release|arm64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Debug|Any CPU.ActiveCfg = Debug|x64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Debug|Any CPU.Build.0 = Debug|x64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Debug|Any CPU.Deploy.0 = Debug|x64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Debug|arm64.ActiveCfg = Debug|arm64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Debug|arm64.Build.0 = Debug|arm64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Debug|arm64.Deploy.0 = Debug|arm64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Debug|x64.ActiveCfg = Debug|x64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Debug|x64.Build.0 = Debug|x64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Debug|x64.Deploy.0 = Debug|x64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Debug|x86.ActiveCfg = Debug|x86 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Debug|x86.Build.0 = Debug|x86 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Debug|x86.Deploy.0 = Debug|x86 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Release|Any CPU.ActiveCfg = Release|x64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Release|Any CPU.Build.0 = Release|x64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Release|Any CPU.Deploy.0 = Release|x64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Release|arm64.ActiveCfg = Release|arm64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Release|arm64.Build.0 = Release|arm64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Release|arm64.Deploy.0 = Release|arm64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Release|x64.ActiveCfg = Release|x64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Release|x64.Build.0 = Release|x64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Release|x64.Deploy.0 = Release|x64 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Release|x86.ActiveCfg = Release|x86 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Release|x86.Build.0 = Release|x86 + {36AEF8B0-0DC6-41E9-91C7-2532BF15558F}.Release|x86.Deploy.0 = Release|x86 + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Debug|Any CPU.Build.0 = Debug|Any CPU + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Debug|arm64.ActiveCfg = Debug|arm64 + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Debug|arm64.Build.0 = Debug|arm64 + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Debug|x64.ActiveCfg = Debug|x64 + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Debug|x64.Build.0 = Debug|x64 + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Debug|x86.ActiveCfg = Debug|x86 + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Debug|x86.Build.0 = Debug|x86 + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Release|Any CPU.ActiveCfg = Release|Any CPU + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Release|Any CPU.Build.0 = Release|Any CPU + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Release|arm64.ActiveCfg = Release|arm64 + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Release|arm64.Build.0 = Release|arm64 + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Release|x64.ActiveCfg = Release|x64 + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Release|x64.Build.0 = Release|x64 + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Release|x86.ActiveCfg = Release|x86 + {97469569-3F7C-4D14-858B-A48C1F2B0138}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C7C88BE1-DEAE-4505-8A73-BAE8DB909176} + EndGlobalSection +EndGlobal diff --git a/HCM/Activation/ActivationHandler.cs b/HCM/Activation/ActivationHandler.cs new file mode 100644 index 0000000..9065252 --- /dev/null +++ b/HCM/Activation/ActivationHandler.cs @@ -0,0 +1,17 @@ +namespace HCM.Activation; + +// Extend this class to implement new ActivationHandlers. See DefaultActivationHandler for an example. +// https://github.com/microsoft/TemplateStudio/blob/main/docs/WinUI/activation.md +public abstract class ActivationHandler : IActivationHandler + where T : class +{ + // Override this method to add the logic for whether to handle the activation. + protected virtual bool CanHandleInternal(T args) => true; + + // Override this method to add the logic for your activation handler. + protected abstract Task HandleInternalAsync(T args); + + public bool CanHandle(object args) => args is T && CanHandleInternal((args as T)!); + + public async Task HandleAsync(object args) => await HandleInternalAsync((args as T)!); +} diff --git a/HCM/Activation/AppNotificationActivationHandler.cs b/HCM/Activation/AppNotificationActivationHandler.cs new file mode 100644 index 0000000..7a78471 --- /dev/null +++ b/HCM/Activation/AppNotificationActivationHandler.cs @@ -0,0 +1,51 @@ +using HCM.Contracts.Services; +using HCM.ViewModels; + +using Microsoft.UI.Dispatching; +using Microsoft.UI.Xaml; +using Microsoft.Windows.AppLifecycle; +using Microsoft.Windows.AppNotifications; + +namespace HCM.Activation; + +public class AppNotificationActivationHandler : ActivationHandler +{ + private readonly INavigationService _navigationService; + private readonly IAppNotificationService _notificationService; + + public AppNotificationActivationHandler(INavigationService navigationService, IAppNotificationService notificationService) + { + _navigationService = navigationService; + _notificationService = notificationService; + } + + protected override bool CanHandleInternal(LaunchActivatedEventArgs args) + { + return AppInstance.GetCurrent().GetActivatedEventArgs()?.Kind == ExtendedActivationKind.AppNotification; + } + + protected async override Task HandleInternalAsync(LaunchActivatedEventArgs args) + { + // TODO: Handle notification activations. + + //// // Access the AppNotificationActivatedEventArgs. + //// var activatedEventArgs = (AppNotificationActivatedEventArgs)AppInstance.GetCurrent().GetActivatedEventArgs().Data; + + //// // Navigate to a specific page based on the notification arguments. + //// if (_notificationService.ParseArguments(activatedEventArgs.Argument)["action"] == "Settings") + //// { + //// // Queue navigation with low priority to allow the UI to initialize. + //// App.MainWindow.DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () => + //// { + //// _navigationService.NavigateTo(typeof(SettingsViewModel).FullName!); + //// }); + //// } + + App.MainWindow.DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () => + { + App.MainWindow.ShowMessageDialogAsync("TODO: Handle notification activations.", "Notification Activation"); + }); + + await Task.CompletedTask; + } +} diff --git a/HCM/Activation/DefaultActivationHandler.cs b/HCM/Activation/DefaultActivationHandler.cs new file mode 100644 index 0000000..f31232d --- /dev/null +++ b/HCM/Activation/DefaultActivationHandler.cs @@ -0,0 +1,29 @@ +using HCM.Contracts.Services; +using HCM.ViewModels; + +using Microsoft.UI.Xaml; + +namespace HCM.Activation; + +public class DefaultActivationHandler : ActivationHandler +{ + private readonly INavigationService _navigationService; + + public DefaultActivationHandler(INavigationService navigationService) + { + _navigationService = navigationService; + } + + protected override bool CanHandleInternal(LaunchActivatedEventArgs args) + { + // None of the ActivationHandlers has handled the activation. + return _navigationService.Frame?.Content == null; + } + + protected async override Task HandleInternalAsync(LaunchActivatedEventArgs args) + { + _navigationService.NavigateTo(typeof(MainViewModel).FullName!, args.Arguments); + + await Task.CompletedTask; + } +} diff --git a/HCM/Activation/IActivationHandler.cs b/HCM/Activation/IActivationHandler.cs new file mode 100644 index 0000000..c409686 --- /dev/null +++ b/HCM/Activation/IActivationHandler.cs @@ -0,0 +1,8 @@ +namespace HCM.Activation; + +public interface IActivationHandler +{ + bool CanHandle(object args); + + Task HandleAsync(object args); +} diff --git a/HCM/App.xaml b/HCM/App.xaml new file mode 100644 index 0000000..743f479 --- /dev/null +++ b/HCM/App.xaml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/HCM/App.xaml.cs b/HCM/App.xaml.cs new file mode 100644 index 0000000..ca73d51 --- /dev/null +++ b/HCM/App.xaml.cs @@ -0,0 +1,108 @@ +using HCM.Activation; +using HCM.Contracts.Services; +using HCM.Core.Contracts.Services; +using HCM.Core.Services; +using HCM.Helpers; +using HCM.Models; +using HCM.Notifications; +using HCM.Services; +using HCM.ViewModels; +using HCM.Views; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.UI.Xaml; + +namespace HCM; + +// To learn more about WinUI 3, see https://docs.microsoft.com/windows/apps/winui/winui3/. +public partial class App : Application +{ + // The .NET Generic Host provides dependency injection, configuration, logging, and other services. + // https://docs.microsoft.com/dotnet/core/extensions/generic-host + // https://docs.microsoft.com/dotnet/core/extensions/dependency-injection + // https://docs.microsoft.com/dotnet/core/extensions/configuration + // https://docs.microsoft.com/dotnet/core/extensions/logging + public IHost Host + { + get; + } + + public static T GetService() + where T : class + { + if ((App.Current as App)!.Host.Services.GetService(typeof(T)) is not T service) + { + throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs."); + } + + return service; + } + + public static WindowEx MainWindow { get; } = new MainWindow(); + + public static UIElement? AppTitlebar { get; set; } + + public App() + { + InitializeComponent(); + + Host = Microsoft.Extensions.Hosting.Host. + CreateDefaultBuilder(). + UseContentRoot(AppContext.BaseDirectory). + ConfigureServices((context, services) => + { + // Default Activation Handler + services.AddTransient, DefaultActivationHandler>(); + + // Other Activation Handlers + services.AddTransient(); + + // Services + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddTransient(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + // Core Services + services.AddSingleton(); + + // Views and ViewModels + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + // Configuration + services.Configure(context.Configuration.GetSection(nameof(LocalSettingsOptions))); + }). + Build(); + + App.GetService().Initialize(); + + UnhandledException += App_UnhandledException; + } + + private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) + { + // TODO: Log and handle exceptions as appropriate. + // https://docs.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.application.unhandledexception. + } + + protected async override void OnLaunched(LaunchActivatedEventArgs args) + { + base.OnLaunched(args); + + App.GetService().Show(string.Format("AppNotificationSamplePayload".GetLocalized(), AppContext.BaseDirectory)); + + await App.GetService().ActivateAsync(args); + } +} diff --git a/HCM/Assets/LockScreenLogo.scale-200.png b/HCM/Assets/LockScreenLogo.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..60f900dd818852ad944030ce762b2a5142814af5 GIT binary patch literal 2784 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 SGyY=!29*Ubl%5V+vEg4C^)f#I literal 0 HcmV?d00001 diff --git a/HCM/Assets/SplashScreen.scale-200.png b/HCM/Assets/SplashScreen.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..fed56086703a83ba22496a7b1415ac8dcd0d981b GIT binary patch literal 27165 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 literal 0 HcmV?d00001 diff --git a/HCM/Assets/Square150x150Logo.scale-200.png b/HCM/Assets/Square150x150Logo.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..f995ebaa5e146a1564f4eb6ed398b69d3c54c922 GIT binary patch literal 9868 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 Z(8t~^U4fbBh~NJgGdH$2dV;|b{|{~mX%zqf literal 0 HcmV?d00001 diff --git a/HCM/Assets/Square44x44Logo.scale-200.png b/HCM/Assets/Square44x44Logo.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..591173735147a433097dfe52aa13cb2ffdb10c36 GIT binary patch literal 3634 zcma)93piA37am2JRLUVqnI^d$=4$RWWX!l7gg7oy_RQ=t6LT>$t|!GDp;D3KmWtF# zsU*4Nl+ev39z`quO8ZSHOkbKnMW z7z{SY$&u*^-F=mp#w_SRECWk`ZkiHDKN$=*TSs}R!1nIbhr!f(1zx^#Uso3zM=V4G zTrnF&M+qg6H4L`OCQ1TuLO?l!4F(HDbmU*>YLEy4myYxyx?)`=b|7Ei7$XJO#<+QL zVnR4nF4D#tu_}rN0SG}kfQS-?ie$7XI&un^20bg6F-XLei9Cdkv{e!!d|llUc48@r zprUao4i<|?kf~@c8?fR6R+b1n7H@^Y;W0QX6qZQCl4t}f;`0v)(Mh>HnkUo#Ga2+o zNAl%z2@QjZjEqD_TA{_#U<{5*rDCvn3?7eyEKss&ksOFZiDb*aATU80M=FrW1!56G zi3qU8;c_|>3iXo)p~Tg78dxOzoGB=0m?%Jk!J)Ajp-?Go%33D(1iyyyU2B zf--TqlmkNM%fC9aA|U3^8vcPZD*_bBr`@64|8M6nJY3GSog`cuI+Z3a2LpzJLI_U= zX~InllJLcHv5YVNFVNH5Q;AR#K-r?LkqV%$0YaH#PPh`%iAhI7J7}(eOCtk3GE`S0 z3eUv@C=!PNpr|}N7lp&)*mxX=Lc~(YUvR$X4{Z~v_Bgyf7SF=rSOf;%io#^#Y^hWV z%bv=#|KjT;lF0!P2mHbzBZQcyUe7YOQB&u!@G*P0$%ign3BA(Rx~`8h9mx)DRgeQfE@V0##|1KCzc8UsDlJTAQ;3* zM8QbJbb)Ag;!v>^B8H-~Quh9*QZYfPAOhstOT|LORP)jV-$@)u2B}z{6%Iw_k;y24 zKmbux0*;JgQ^+JPk%Xg=2o&UJf9^L$|8)AH6)5>%BcTvDR1O7CMe(qBG75(S2`DyH z8Wfj`=digTmB#@|NX&GIjsh7}is;V=ek~aG`6*NYF+wMX25^*zmyVPHkP>AW%Ej-= z_-FP1bugOGgqR>dA8q@BCtk15DQh{0cN^Pc9qYxu=>A@IIt z@ZSMq-zN)~WV6f2ufmcXjL3*GwL&jbvfz!i z;=J0mwA;5|`oYg9Y6Y-j1!+TuHy<$kCCpdcsxo_c-amNqXT6W)7KgWbu zZMeJBEVgxRg+)xGrJcNJ6qgfWG>bLjD1oOnfW z6$`H3pBV3*Ny?4KY)5AYTqZmE9W!_FO9E8GYrTWqUiXr5c`YTgG#*}f&e|+63CK78 z$VN^$cUEaGTZ&*gYF0l;8(-w?uo(U*dsE5IyV_BClz~03x?yu22G`9=SF2pE;QI_e zGd%p^#E&;PeS7;u>qzhKhhZ9mI?-LT%opDPT1@n=Hd(8F916pfMP|H-Xn9BG?A{3f zRmDhQN`J04j%`-g5vF?RkG|Co&9%N4YDS=6nB$Hs;7U{CCC@#Ilu8GJJqQ4L+scJ6RB;%j`qr4J=VUu1J}3IwTk4`q57*lR4F5dwO+a~JW`AKTK zgH8>!99$X_Fjkk_=6!BlrGCk~=f;VxX~(x4`g?$dk9GUqe#alGeONpLuHB(1&v=%x zi+uEto_pX*VREeOw1#!c0J==m;xV@D>7KNRp(hBu&hg&)ryPIZoq0E5yXmZ-S5R!` zwLhZ7{Sd9Ya%Z{A_yYYot!=FZ^k3#`)W_Fp-c@se6? z&NU2>KW81-tF<|EUGqcXj#(K+Zyw-RJoXqiyZUqQXdbRK&LgBFCUxy89p(%D#)+bY zm)Oa(#Y=ZzXjUNoC1x)@56lQmxv=@r^1;NXO>q#j!0qI? z^X*=R4a=E(twD%L-*=~=SQTUE%%}v=aKqJ2C(+BD_T4!+7yUqV_`OV*(e6LcV41Zf zU#9^+raGeInIhGiPef} zqjqt!u`w%tU0dGh2A{IG!w3!CBd)EJ0~JW$3IE%asLuW^L&Jfmg5MsQqx`CKV!1Jk I89|DF0b4-cRsaA1 literal 0 HcmV?d00001 diff --git a/HCM/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/HCM/Assets/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 0000000000000000000000000000000000000000..1dc51a481c32521eb980b7c9018e75d72f26d71f GIT binary patch literal 2160 zcma)8Yj6`)6b@LRMaoN2L>|i)p_Fd+oy{(!Oq!%LNDGt#twmw8dy_6rvP*W`q(z=n zXHs-fnL(ix21Xe~9T}9Dyetf*4lf5pz){9l(EgAL2#U(fo1_gWsAOidyZ3(QJLfy+ z+%sD@rEo%e&p|y+CR4gQ-&q9j17lZeclh5Dw*3xoXl) zAuz3`uvn_8ka!s#pM#8x2#_EE3=xS0{6Sra*w8qy0H0&aIEus}MuiQv#~dOh1yc}* z8UhG!CNYU1D1_t9vPYz4kX!JsFU0A zM8q7xYzdP?3hWxu?^LDA7^T~3L*b5DR%C$_y&SA7i%~KqVit)JG2Tnb7)g;Hij=G@ zVdXk`9%fIwc#fo5GLN8KBlEktkU})BxuooO5AR20@SXKRS(} zxX*+QaEb=7p->@%L1vOICMki6HSZY0DiA~hTA<2`xB4GYOEEzU6eG}ohr)9si-!2W zU|ABpYA7JW4pIW558ztRha$-W2@ch-hM;2@9UbfaM5&x0q*MVpFQf*Lc=HO%LlOr# zz!P4Y#5ga?Z0+W9)5-TI|l9l2yFF|n_ z2@{Wbc#gqjo{~H=;Jp&Cpm;LGd_{+)sO~uM)4;rrC%*zc!V@EiQta^BP+f#dVqwG< zA133`>YuO=mjg(dtmA{lNx*c~YlOuRu=`-2KiDc1Yqs3qCkZ2|TnzdEoThkOCLS9* z30-)iD)%JE_wE}}P)zcHmud-8mmHg(Xd!?wL;y$_I=mNll|I?UaQ{uaExe$47?-FKD^ zx7M|9z1w5*m5)8$=GR`YY`QuynC;Rsg=oLon@#2;&s=Z1Gi}_7^rmlzE^j|gHMVB$ zZ@zQj^c(#LzkXrSu5G=p%rZ+u*@n!FbAy{N&or$&`b~7|7XyZDZEu>pVNl-lgLjIi zMHeC~wcg7%^2^qq%vpT3Vg0c7el3mz%J}6aduOMQJ9N#-E%|hyfBa#66T5ZomgxRV z>$i&)bqmc+$A(;hpx1BbFtl zV(ENxo%MVZ-TK<4r;c3Nt{r?eH7FxD%QtURO2ZWUDbuv2 zYw8uD?E6V2HKn^#&$@hvc1_PCwp4HawZ%xw#}>Nyjnz-@>(A}VH{D?0p4m)TW;lEO zq}d~5Q=6z2g_$2*uBhKqJa*y89mfuyx;3D!aYE~`!T=U_8tPu;Yx7BVkiax)+YQ?a{)Y6;{7vCTHXO_~k e`^W08ce^xhIqR5p_d-?do51ZVbbgdOXX#&mocN~z literal 0 HcmV?d00001 diff --git a/HCM/Assets/StoreLogo.png b/HCM/Assets/StoreLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..debfb0a47db16cebefe22cc5887bdb0efd64020c GIT binary patch literal 2832 zcma)83se(l77oZmK$Mq?&_WonYITy#iR;(qP5i`s7OUY9*e65wJdmCKvYay6dwq?)T*f4-3c$n$vU4_FW~A~1t|XG>9a&7g zHeUP(9;%d65KJk-DLT1|K$A$m{yG(cX5d<~1W%PI_`qmm3qY1(d?1b~rioPoJWUpo zr^ci6!eh|943vuj{(fX%9gi>|$F&GqC(l%9csf2XVV6fdo0h2nc>3KD&zyi4h558JT@X_6Ln>R zFa{$a3uPc6R|;bw1Va)SLODzthdpiQU3@rD0KqVw8$^Re5F}y*vPGN#K_C}q1_aT9 zMDR2>RH4x#3KXCAEhBtO7$}4=>0FS;l1M?6!=Qm&8qNY~5)45g6r({9KEdY+)HtG5 zs$-PO%;{E*oX!MUARsyuVvv`{$rP9}N3-lL4lYErI3FMsGC&#!gqSf9%!5tMyPU@1 z(P)!Uu@aL>bN>U%ilNa>P$u+mP~zNRh!*+3U<~C+m1;RcbdXGrq~cVSA{8Ld6o@BK zW-8T$VGN8-fr5Y%*d`G5u?l$gRWExt>}@2meLKPL?*OwQEt zx5b%+X_QiJ4x+{bQi(qQZL7dc3hV7YnIxfUh$0myrYUtoCe<`{CUsFKtMW$j#NJyY ziqJl>M9e+Y03;jXQ0NfX~mm-H^ym_3L+Cc5?C- z^Y`b8d_o>xtkj1XD4wz5jei+tzPeV?6tT&*Z_mDOdsriUv#bi>v{q0#kmZQ$bnuc99Cqg@U##m#z@o>SfaipJG9aEe_ zs@zT;{?YBcjn38esBXd5GILF9xN%!rn9%2|>V<}RqA$IEsq_Mz0oVnNo>M%O{y~ak z|3^JFwKdGSCB~d1E}Xg7X0?#EtF>>R8W#X?+)$U9eMGJ!#;B>-z!6fHb%5< z;Ygk_oUysoZ8Z9+_xgFSd)KTp-Vo>BKJEpq6k=HkaqV@xfU(-(W_`QCf$O#PGJmyH zh}D^!)!T1v_Y%YjgwNJ4mVm=W`r(C!3b#<~))J3{CoefA{N$+nBFv)p34Mn`nMQFs zn>e;N!OE#RyS-~P3reoZvM+yqe$8NEt3JkhWU;4N;_=A2en3Q*hcr56hu69N@fHIc zv>&v_o~eh#J^g!=g7&Cy)uCYmVUb|{kjzF3lnH#&q~Y ztHuJ$p?w+X_7Lj$%@>X@j`%gMK2rSEc)|(6&u7o5?5ZrlyxP6XDzz)utuOTVf0S^2 zrS%1;r8!nQn#O<2{q$|s))a%8p#5aw6+aTo;iHslORK__<}cT=>ee2v$w*3>6VIqP z-?xhEq@tAGeXpxm{e+d61XPupeNPf)=$B_Xx212qS~M_R8s_sX=AL$tQ;@&Bgm2gT zxBL>nPqLPNGy7_hiLj)^ES40nh#%>dR)q~x(bj&FsLI^FHl_NigM0txvam0+RArYg zlSE63!GgP7R48`AOW)p%?4`|DfVPG9?_TmScYKlprhEbNn^?5FaASp1o5wr%v5 zwH1%MPE*TH#(a08(+}ABVDlBAa%17zd(ZdSjQ#0Hi;DhZA>FJo3X8PSMZS)6xvSiJ z+aEXAwdmW;$7g+JQEe${-_qIX>eigJr`)2sbpEd&j=MC7q+3s2W4ipKb<>#7D)#ME zyI|`rEgp{J1NwVF1KZadXsS3JA${5W;Gw4C!LMVV1_uj-FL^f?!FN&%+&jLed@+9@ zw(gj%0Yzr(%3l|fkA>ZtbKsGW?+s-{<&uTT*PEqL#>EU#@um0j$PcdYOM3p9Uo?A} zvq!{{_=!5KE#ad?g);Su|SbCj{RY&*W-;|GgW2WR(;`@4gEUAx<#XPUlQLq*}j Jn!uFJ{{kvfH39$t literal 0 HcmV?d00001 diff --git a/HCM/Assets/Wide310x150Logo.scale-200.png b/HCM/Assets/Wide310x150Logo.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..c92ae8cf4d0ecc7249d53ade29db106ab588dca6 GIT binary patch literal 8768 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 literal 0 HcmV?d00001 diff --git a/HCM/Assets/WindowIcon.ico b/HCM/Assets/WindowIcon.ico new file mode 100644 index 0000000000000000000000000000000000000000..14f2e2f158b2415bd2f0c67abe053a563ecc45e7 GIT binary patch literal 9662 zcmdU#ziSjx5XWD*;>vVl7YhYTK`WcJ_aD&C)_=e%co*z#tOUVA0v0wRHa5A+#;9l^ zD2OK_T3B2x^dM{2_qk-?G0eQTo4ZRiVQ==mogd%%&dl2*B!rdtd+uC_pQpmZGa;M^ zA)Jj@+zQv?#s2K)^uFl&xqbJ1a~xM!oSy!1U?--Br|bUzj{o)jOTW|W)xC?mave4| zu@{;8m;0_fx6%Lkb^8j}j}K4RzrMKp;Qf>9@83PT`b}b%@CQ=VA|E)_d9e3sYhw$%%fwOg4Nfq8f3tbV(&afgHGa6Z^w|@i%dP|d zzdXPF7;N-_7mqq<{5jsyr#|#yPS4A~%|3p?EVzi5<5cuSdNJUhSr9$^Qd{;tn=bqK z!Jqg~CB5$XO7*z+^fWJGN1f-*A%~qf)Pvev*aU}2ONTvcR>X$-&Xa@3yZA?XJ(TKj zZ{cTtsG-(P*N3eGb)^?mU+F#Pc+PjK%e{r4Jvd6|O53saYV7%>W;vg^z7d}qhwNJt zu5xvNM|3P(y$5zVBQ*xrZO=GutK;Z3aXE*)nv-9fS)eC%Y+1v5TOPzlq{eHl!ROV1 zyxR1UcOJC07hHl{r0ka@{7_@DOC|fc8o<3(KlaG06gz#M#^pYJR=St>vG&0_SAXxk zq;I_M3LX|6+tc&*=_89vc=T#u)vXKnso#0=Qv=Ru?T5WIj^xjt)@6-;iF;yEJGkNO zvSyGyp7FjzzvL4<7N20UZ0RctKRzT!Er|gptIn3MbdP*;y-Y0cEP*SH#p?Z1@4#b< zsp~yCSX>G=uZQ4FbVO2D9UuHwu3}^5tbOFx^k%LG;J3a9@SYP4B0c>05FITon;c&K ztc0K3Ec{kaTUhX)%NgS{^3yD zwOtbvj>Vsr-uznM1<-Pc%jGI&QJ+!?m7GrYY}-cGtA4fIM3w)SIjr>mM*!_-}EB2 zJPN*ZImk2kqj#krJV~D9EO!5rY>R)9n44kz|DgESZCCEaXFP8vt26m$x;B$Lb@iJ4 zab4Y-+1J$uGSpT42zz0@3Ik+ZM_KpkYE*?V?bp?KEy~rpnnXEStE=hizO17hS9MkQ z_vIdPFzWBiu(H<+VHABe$eRZeeAfCvZ#)>z?8bv%pRgYfnr7y&Y8v89M8>RUIAWH_ zX_Vlb_L>H~^-9wWEAoli<2zI}c{an|XvW_tgdXd02z}% +{ + private static NavigationViewHeaderBehavior? _current; + + private Page? _currentPage; + + public DataTemplate? DefaultHeaderTemplate + { + get; set; + } + + public object DefaultHeader + { + get => GetValue(DefaultHeaderProperty); + set => SetValue(DefaultHeaderProperty, value); + } + + public static readonly DependencyProperty DefaultHeaderProperty = + DependencyProperty.Register("DefaultHeader", typeof(object), typeof(NavigationViewHeaderBehavior), new PropertyMetadata(null, (d, e) => _current!.UpdateHeader())); + + public static NavigationViewHeaderMode GetHeaderMode(Page item) => (NavigationViewHeaderMode)item.GetValue(HeaderModeProperty); + + public static void SetHeaderMode(Page item, NavigationViewHeaderMode value) => item.SetValue(HeaderModeProperty, value); + + public static readonly DependencyProperty HeaderModeProperty = + DependencyProperty.RegisterAttached("HeaderMode", typeof(bool), typeof(NavigationViewHeaderBehavior), new PropertyMetadata(NavigationViewHeaderMode.Always, (d, e) => _current!.UpdateHeader())); + + public static object GetHeaderContext(Page item) => item.GetValue(HeaderContextProperty); + + public static void SetHeaderContext(Page item, object value) => item.SetValue(HeaderContextProperty, value); + + public static readonly DependencyProperty HeaderContextProperty = + DependencyProperty.RegisterAttached("HeaderContext", typeof(object), typeof(NavigationViewHeaderBehavior), new PropertyMetadata(null, (d, e) => _current!.UpdateHeader())); + + public static DataTemplate GetHeaderTemplate(Page item) => (DataTemplate)item.GetValue(HeaderTemplateProperty); + + public static void SetHeaderTemplate(Page item, DataTemplate value) => item.SetValue(HeaderTemplateProperty, value); + + public static readonly DependencyProperty HeaderTemplateProperty = + DependencyProperty.RegisterAttached("HeaderTemplate", typeof(DataTemplate), typeof(NavigationViewHeaderBehavior), new PropertyMetadata(null, (d, e) => _current!.UpdateHeaderTemplate())); + + protected override void OnAttached() + { + base.OnAttached(); + + var navigationService = App.GetService(); + navigationService.Navigated += OnNavigated; + + _current = this; + } + + protected override void OnDetaching() + { + base.OnDetaching(); + + var navigationService = App.GetService(); + navigationService.Navigated -= OnNavigated; + } + + private void OnNavigated(object sender, NavigationEventArgs e) + { + if (sender is Frame frame && frame.Content is Page page) + { + _currentPage = page; + + UpdateHeader(); + UpdateHeaderTemplate(); + } + } + + private void UpdateHeader() + { + if (_currentPage != null) + { + var headerMode = GetHeaderMode(_currentPage); + if (headerMode == NavigationViewHeaderMode.Never) + { + AssociatedObject.Header = null; + AssociatedObject.AlwaysShowHeader = false; + } + else + { + var headerFromPage = GetHeaderContext(_currentPage); + if (headerFromPage != null) + { + AssociatedObject.Header = headerFromPage; + } + else + { + AssociatedObject.Header = DefaultHeader; + } + + if (headerMode == NavigationViewHeaderMode.Always) + { + AssociatedObject.AlwaysShowHeader = true; + } + else + { + AssociatedObject.AlwaysShowHeader = false; + } + } + } + } + + private void UpdateHeaderTemplate() + { + if (_currentPage != null) + { + var headerTemplate = GetHeaderTemplate(_currentPage); + AssociatedObject.HeaderTemplate = headerTemplate ?? DefaultHeaderTemplate; + } + } +} diff --git a/HCM/Behaviors/NavigationViewHeaderMode.cs b/HCM/Behaviors/NavigationViewHeaderMode.cs new file mode 100644 index 0000000..f05bbe8 --- /dev/null +++ b/HCM/Behaviors/NavigationViewHeaderMode.cs @@ -0,0 +1,8 @@ +namespace HCM.Behaviors; + +public enum NavigationViewHeaderMode +{ + Always, + Never, + Minimal +} diff --git a/HCM/Contracts/Services/IActivationService.cs b/HCM/Contracts/Services/IActivationService.cs new file mode 100644 index 0000000..24fc0c0 --- /dev/null +++ b/HCM/Contracts/Services/IActivationService.cs @@ -0,0 +1,6 @@ +namespace HCM.Contracts.Services; + +public interface IActivationService +{ + Task ActivateAsync(object activationArgs); +} diff --git a/HCM/Contracts/Services/IAppNotificationService.cs b/HCM/Contracts/Services/IAppNotificationService.cs new file mode 100644 index 0000000..efba4a2 --- /dev/null +++ b/HCM/Contracts/Services/IAppNotificationService.cs @@ -0,0 +1,14 @@ +using System.Collections.Specialized; + +namespace HCM.Contracts.Services; + +public interface IAppNotificationService +{ + void Initialize(); + + bool Show(string payload); + + NameValueCollection ParseArguments(string arguments); + + void Unregister(); +} diff --git a/HCM/Contracts/Services/ILocalSettingsService.cs b/HCM/Contracts/Services/ILocalSettingsService.cs new file mode 100644 index 0000000..59f7582 --- /dev/null +++ b/HCM/Contracts/Services/ILocalSettingsService.cs @@ -0,0 +1,8 @@ +namespace HCM.Contracts.Services; + +public interface ILocalSettingsService +{ + Task ReadSettingAsync(string key); + + Task SaveSettingAsync(string key, T value); +} diff --git a/HCM/Contracts/Services/INavigationService.cs b/HCM/Contracts/Services/INavigationService.cs new file mode 100644 index 0000000..98cdd12 --- /dev/null +++ b/HCM/Contracts/Services/INavigationService.cs @@ -0,0 +1,23 @@ +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Navigation; + +namespace HCM.Contracts.Services; + +public interface INavigationService +{ + event NavigatedEventHandler Navigated; + + bool CanGoBack + { + get; + } + + Frame? Frame + { + get; set; + } + + bool NavigateTo(string pageKey, object? parameter = null, bool clearNavigation = false); + + bool GoBack(); +} diff --git a/HCM/Contracts/Services/INavigationViewService.cs b/HCM/Contracts/Services/INavigationViewService.cs new file mode 100644 index 0000000..89bb162 --- /dev/null +++ b/HCM/Contracts/Services/INavigationViewService.cs @@ -0,0 +1,22 @@ +using Microsoft.UI.Xaml.Controls; + +namespace HCM.Contracts.Services; + +public interface INavigationViewService +{ + IList? MenuItems + { + get; + } + + object? SettingsItem + { + get; + } + + void Initialize(NavigationView navigationView); + + void UnregisterEvents(); + + NavigationViewItem? GetSelectedItem(Type pageType); +} diff --git a/HCM/Contracts/Services/IPageService.cs b/HCM/Contracts/Services/IPageService.cs new file mode 100644 index 0000000..dcf75b3 --- /dev/null +++ b/HCM/Contracts/Services/IPageService.cs @@ -0,0 +1,6 @@ +namespace HCM.Contracts.Services; + +public interface IPageService +{ + Type GetPageType(string key); +} diff --git a/HCM/Contracts/Services/IThemeSelectorService.cs b/HCM/Contracts/Services/IThemeSelectorService.cs new file mode 100644 index 0000000..8602974 --- /dev/null +++ b/HCM/Contracts/Services/IThemeSelectorService.cs @@ -0,0 +1,17 @@ +using Microsoft.UI.Xaml; + +namespace HCM.Contracts.Services; + +public interface IThemeSelectorService +{ + ElementTheme Theme + { + get; + } + + Task InitializeAsync(); + + Task SetThemeAsync(ElementTheme theme); + + Task SetRequestedThemeAsync(); +} diff --git a/HCM/Contracts/ViewModels/INavigationAware.cs b/HCM/Contracts/ViewModels/INavigationAware.cs new file mode 100644 index 0000000..6f5ef64 --- /dev/null +++ b/HCM/Contracts/ViewModels/INavigationAware.cs @@ -0,0 +1,8 @@ +namespace HCM.Contracts.ViewModels; + +public interface INavigationAware +{ + void OnNavigatedTo(object parameter); + + void OnNavigatedFrom(); +} diff --git a/HCM/HCM.csproj b/HCM/HCM.csproj new file mode 100644 index 0000000..fae2b9b --- /dev/null +++ b/HCM/HCM.csproj @@ -0,0 +1,60 @@ + + + WinExe + net7.0-windows10.0.19041.0 + 10.0.17763.0 + HCM + Assets/WindowIcon.ico + app.manifest + x86;x64;arm64 + win10-x86;win10-x64;win10-arm64 + Properties\PublishProfiles\win10-$(Platform).pubxml + enable + enable + true + true + False + True + 344CF9B654F502D2BD78D20CE13AC26E0384D105 + SHA256 + False + False + True + Never + True + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + + + + + + + true + + diff --git a/HCM/Helpers/EnumToBooleanConverter.cs b/HCM/Helpers/EnumToBooleanConverter.cs new file mode 100644 index 0000000..2ea89c1 --- /dev/null +++ b/HCM/Helpers/EnumToBooleanConverter.cs @@ -0,0 +1,38 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Data; + +namespace HCM.Helpers; + +public class EnumToBooleanConverter : IValueConverter +{ + public EnumToBooleanConverter() + { + } + + public object Convert(object value, Type targetType, object parameter, string language) + { + if (parameter is string enumString) + { + if (!Enum.IsDefined(typeof(ElementTheme), value)) + { + throw new ArgumentException("ExceptionEnumToBooleanConverterValueMustBeAnEnum"); + } + + var enumValue = Enum.Parse(typeof(ElementTheme), enumString); + + return enumValue.Equals(value); + } + + throw new ArgumentException("ExceptionEnumToBooleanConverterParameterMustBeAnEnumName"); + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + if (parameter is string enumString) + { + return Enum.Parse(typeof(ElementTheme), enumString); + } + + throw new ArgumentException("ExceptionEnumToBooleanConverterParameterMustBeAnEnumName"); + } +} diff --git a/HCM/Helpers/FrameExtensions.cs b/HCM/Helpers/FrameExtensions.cs new file mode 100644 index 0000000..8f423a5 --- /dev/null +++ b/HCM/Helpers/FrameExtensions.cs @@ -0,0 +1,8 @@ +using Microsoft.UI.Xaml.Controls; + +namespace HCM.Helpers; + +public static class FrameExtensions +{ + public static object? GetPageViewModel(this Frame frame) => frame?.Content?.GetType().GetProperty("ViewModel")?.GetValue(frame.Content, null); +} diff --git a/HCM/Helpers/NavigationHelper.cs b/HCM/Helpers/NavigationHelper.cs new file mode 100644 index 0000000..af9a763 --- /dev/null +++ b/HCM/Helpers/NavigationHelper.cs @@ -0,0 +1,21 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace HCM.Helpers; + +// Helper class to set the navigation target for a NavigationViewItem. +// +// Usage in XAML: +// +// +// Usage in code: +// NavigationHelper.SetNavigateTo(navigationViewItem, typeof(MainViewModel).FullName); +public class NavigationHelper +{ + public static string GetNavigateTo(NavigationViewItem item) => (string)item.GetValue(NavigateToProperty); + + public static void SetNavigateTo(NavigationViewItem item, string value) => item.SetValue(NavigateToProperty, value); + + public static readonly DependencyProperty NavigateToProperty = + DependencyProperty.RegisterAttached("NavigateTo", typeof(string), typeof(NavigationHelper), new PropertyMetadata(null)); +} diff --git a/HCM/Helpers/ResourceExtensions.cs b/HCM/Helpers/ResourceExtensions.cs new file mode 100644 index 0000000..a3f8e19 --- /dev/null +++ b/HCM/Helpers/ResourceExtensions.cs @@ -0,0 +1,10 @@ +using Microsoft.Windows.ApplicationModel.Resources; + +namespace HCM.Helpers; + +public static class ResourceExtensions +{ + private static readonly ResourceLoader _resourceLoader = new(); + + public static string GetLocalized(this string resourceKey) => _resourceLoader.GetString(resourceKey); +} diff --git a/HCM/Helpers/RuntimeHelper.cs b/HCM/Helpers/RuntimeHelper.cs new file mode 100644 index 0000000..9bfe093 --- /dev/null +++ b/HCM/Helpers/RuntimeHelper.cs @@ -0,0 +1,20 @@ +using System.Runtime.InteropServices; +using System.Text; + +namespace HCM.Helpers; + +public class RuntimeHelper +{ + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern int GetCurrentPackageFullName(ref int packageFullNameLength, StringBuilder? packageFullName); + + public static bool IsMSIX + { + get + { + var length = 0; + + return GetCurrentPackageFullName(ref length, null) != 15700L; + } + } +} diff --git a/HCM/Helpers/SettingsStorageExtensions.cs b/HCM/Helpers/SettingsStorageExtensions.cs new file mode 100644 index 0000000..86804f9 --- /dev/null +++ b/HCM/Helpers/SettingsStorageExtensions.cs @@ -0,0 +1,112 @@ +using HCM.Core.Helpers; + +using Windows.Storage; +using Windows.Storage.Streams; + +namespace HCM.Helpers; + +// Use these extension methods to store and retrieve local and roaming app data +// More details regarding storing and retrieving app data at https://docs.microsoft.com/windows/apps/design/app-settings/store-and-retrieve-app-data +public static class SettingsStorageExtensions +{ + private const string FileExtension = ".json"; + + public static bool IsRoamingStorageAvailable(this ApplicationData appData) + { + return appData.RoamingStorageQuota == 0; + } + + public static async Task SaveAsync(this StorageFolder folder, string name, T content) + { + var file = await folder.CreateFileAsync(GetFileName(name), CreationCollisionOption.ReplaceExisting); + var fileContent = await Json.StringifyAsync(content); + + await FileIO.WriteTextAsync(file, fileContent); + } + + public static async Task ReadAsync(this StorageFolder folder, string name) + { + if (!File.Exists(Path.Combine(folder.Path, GetFileName(name)))) + { + return default; + } + + var file = await folder.GetFileAsync($"{name}.json"); + var fileContent = await FileIO.ReadTextAsync(file); + + return await Json.ToObjectAsync(fileContent); + } + + public static async Task SaveAsync(this ApplicationDataContainer settings, string key, T value) + { + settings.SaveString(key, await Json.StringifyAsync(value)); + } + + public static void SaveString(this ApplicationDataContainer settings, string key, string value) + { + settings.Values[key] = value; + } + + public static async Task ReadAsync(this ApplicationDataContainer settings, string key) + { + object? obj; + + if (settings.Values.TryGetValue(key, out obj)) + { + return await Json.ToObjectAsync((string)obj); + } + + return default; + } + + public static async Task SaveFileAsync(this StorageFolder folder, byte[] content, string fileName, CreationCollisionOption options = CreationCollisionOption.ReplaceExisting) + { + if (content == null) + { + throw new ArgumentNullException(nameof(content)); + } + + if (string.IsNullOrEmpty(fileName)) + { + throw new ArgumentException("File name is null or empty. Specify a valid file name", nameof(fileName)); + } + + var storageFile = await folder.CreateFileAsync(fileName, options); + await FileIO.WriteBytesAsync(storageFile, content); + return storageFile; + } + + public static async Task ReadFileAsync(this StorageFolder folder, string fileName) + { + var item = await folder.TryGetItemAsync(fileName).AsTask().ConfigureAwait(false); + + if ((item != null) && item.IsOfType(StorageItemTypes.File)) + { + var storageFile = await folder.GetFileAsync(fileName); + var content = await storageFile.ReadBytesAsync(); + return content; + } + + return null; + } + + public static async Task ReadBytesAsync(this StorageFile file) + { + if (file != null) + { + using IRandomAccessStream stream = await file.OpenReadAsync(); + using var reader = new DataReader(stream.GetInputStreamAt(0)); + await reader.LoadAsync((uint)stream.Size); + var bytes = new byte[stream.Size]; + reader.ReadBytes(bytes); + return bytes; + } + + return null; + } + + private static string GetFileName(string name) + { + return string.Concat(name, FileExtension); + } +} diff --git a/HCM/Helpers/TitleBarHelper.cs b/HCM/Helpers/TitleBarHelper.cs new file mode 100644 index 0000000..7f3843e --- /dev/null +++ b/HCM/Helpers/TitleBarHelper.cs @@ -0,0 +1,96 @@ +using System.Runtime.InteropServices; + +using Microsoft.UI; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Media; + +using Windows.UI; +using Windows.UI.ViewManagement; + +namespace HCM.Helpers; + +// Helper class to workaround custom title bar bugs. +// DISCLAIMER: The resource key names and color values used below are subject to change. Do not depend on them. +// https://github.com/microsoft/TemplateStudio/issues/4516 +internal class TitleBarHelper +{ + private const int WAINACTIVE = 0x00; + private const int WAACTIVE = 0x01; + private const int WMACTIVATE = 0x0006; + + [DllImport("user32.dll")] + private static extern IntPtr GetActiveWindow(); + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + private static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam); + + public static void UpdateTitleBar(ElementTheme theme) + { + if (App.MainWindow.ExtendsContentIntoTitleBar) + { + if (theme == ElementTheme.Default) + { + var uiSettings = new UISettings(); + var background = uiSettings.GetColorValue(UIColorType.Background); + + theme = background == Colors.White ? ElementTheme.Light : ElementTheme.Dark; + } + + if (theme == ElementTheme.Default) + { + theme = Application.Current.RequestedTheme == ApplicationTheme.Light ? ElementTheme.Light : ElementTheme.Dark; + } + + App.MainWindow.AppWindow.TitleBar.ButtonForegroundColor = theme switch + { + ElementTheme.Dark => Colors.White, + ElementTheme.Light => Colors.Black, + _ => Colors.Transparent + }; + + App.MainWindow.AppWindow.TitleBar.ButtonHoverForegroundColor = theme switch + { + ElementTheme.Dark => Colors.White, + ElementTheme.Light => Colors.Black, + _ => Colors.Transparent + }; + + App.MainWindow.AppWindow.TitleBar.ButtonHoverBackgroundColor = theme switch + { + ElementTheme.Dark => Color.FromArgb(0x33, 0xFF, 0xFF, 0xFF), + ElementTheme.Light => Color.FromArgb(0x33, 0x00, 0x00, 0x00), + _ => Colors.Transparent + }; + + App.MainWindow.AppWindow.TitleBar.ButtonPressedBackgroundColor = theme switch + { + ElementTheme.Dark => Color.FromArgb(0x66, 0xFF, 0xFF, 0xFF), + ElementTheme.Light => Color.FromArgb(0x66, 0x00, 0x00, 0x00), + _ => Colors.Transparent + }; + + App.MainWindow.AppWindow.TitleBar.BackgroundColor = Colors.Transparent; + + var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow); + if (hwnd == GetActiveWindow()) + { + SendMessage(hwnd, WMACTIVATE, WAINACTIVE, IntPtr.Zero); + SendMessage(hwnd, WMACTIVATE, WAACTIVE, IntPtr.Zero); + } + else + { + SendMessage(hwnd, WMACTIVATE, WAACTIVE, IntPtr.Zero); + SendMessage(hwnd, WMACTIVATE, WAINACTIVE, IntPtr.Zero); + } + } + } + + public static void ApplySystemThemeToCaptionButtons() + { + var frame = App.AppTitlebar as FrameworkElement; + if (frame != null) + { + UpdateTitleBar(frame.ActualTheme); + } + } +} diff --git a/HCM/MainWindow.xaml b/HCM/MainWindow.xaml new file mode 100644 index 0000000..73eb46c --- /dev/null +++ b/HCM/MainWindow.xaml @@ -0,0 +1,16 @@ + + + + + diff --git a/HCM/MainWindow.xaml.cs b/HCM/MainWindow.xaml.cs new file mode 100644 index 0000000..bc2a9c3 --- /dev/null +++ b/HCM/MainWindow.xaml.cs @@ -0,0 +1,37 @@ +using HCM.Helpers; + +using Windows.UI.ViewManagement; + +namespace HCM; + +public sealed partial class MainWindow : WindowEx +{ + private Microsoft.UI.Dispatching.DispatcherQueue dispatcherQueue; + + private UISettings settings; + + public MainWindow() + { + InitializeComponent(); + + AppWindow.SetIcon(Path.Combine(AppContext.BaseDirectory, "Assets/WindowIcon.ico")); + Content = null; + Title = "AppDisplayName".GetLocalized(); + + // Theme change code picked from https://github.com/microsoft/WinUI-Gallery/pull/1239 + dispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread(); + settings = new UISettings(); + settings.ColorValuesChanged += Settings_ColorValuesChanged; // cannot use FrameworkElement.ActualThemeChanged event + } + + // this handles updating the caption button colors correctly when indows system theme is changed + // while the app is open + private void Settings_ColorValuesChanged(UISettings sender, object args) + { + // This calls comes off-thread, hence we will need to dispatch it to current app's thread + dispatcherQueue.TryEnqueue(() => + { + TitleBarHelper.ApplySystemThemeToCaptionButtons(); + }); + } +} diff --git a/HCM/Models/LocalSettingsOptions.cs b/HCM/Models/LocalSettingsOptions.cs new file mode 100644 index 0000000..ea5006f --- /dev/null +++ b/HCM/Models/LocalSettingsOptions.cs @@ -0,0 +1,14 @@ +namespace HCM.Models; + +public class LocalSettingsOptions +{ + public string? ApplicationDataFolder + { + get; set; + } + + public string? LocalSettingsFile + { + get; set; + } +} diff --git a/HCM/Package.appinstaller b/HCM/Package.appinstaller new file mode 100644 index 0000000..c619457 --- /dev/null +++ b/HCM/Package.appinstaller @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/HCM/Package.appxmanifest b/HCM/Package.appxmanifest new file mode 100644 index 0000000..b3ff4f8 --- /dev/null +++ b/HCM/Package.appxmanifest @@ -0,0 +1,76 @@ + + + + + + + + + + 动车组交路查询 + mstouk57g + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HCM/Properties/launchsettings.json b/HCM/Properties/launchsettings.json new file mode 100644 index 0000000..e997418 --- /dev/null +++ b/HCM/Properties/launchsettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "HCM (Package)": { + "commandName": "MsixPackage" + }, + "HCM (Unpackaged)": { + "commandName": "Project" + } + } +} diff --git a/HCM/README.md b/HCM/README.md new file mode 100644 index 0000000..e24cced --- /dev/null +++ b/HCM/README.md @@ -0,0 +1,27 @@ +*Recommended Markdown Viewer: [Markdown Editor](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.MarkdownEditor2)* + +## Getting Started + +Browse and address `TODO:` comments in `View -> Task List` to learn the codebase and understand next steps for turning the generated code into production code. + +Explore the [WinUI Gallery](https://www.microsoft.com/store/productId/9P3JFPWWDZRC) to learn about available controls and design patterns. + +Relaunch Template Studio to modify the project by right-clicking on the project in `View -> Solution Explorer` then selecting `Add -> New Item (Template Studio)`. + +## Publishing + +For projects with MSIX packaging, right-click on the application project and select `Package and Publish -> Create App Packages...` to create an MSIX package. + +For projects without MSIX packaging, follow the [deployment guide](https://docs.microsoft.com/windows/apps/windows-app-sdk/deploy-unpackaged-apps) or add the `Self-Contained` Feature to enable xcopy deployment. + +## CI Pipelines + +See [README.md](https://github.com/microsoft/TemplateStudio/blob/main/docs/WinUI/pipelines/README.md) for guidance on building and testing projects in CI pipelines. + +## Changelog + +See [releases](https://github.com/microsoft/TemplateStudio/releases) and [milestones](https://github.com/microsoft/TemplateStudio/milestones). + +## Feedback + +Bugs and feature requests should be filed at https://aka.ms/templatestudio. diff --git a/HCM/Services/ActivationService.cs b/HCM/Services/ActivationService.cs new file mode 100644 index 0000000..ba3550a --- /dev/null +++ b/HCM/Services/ActivationService.cs @@ -0,0 +1,72 @@ +using HCM.Activation; +using HCM.Contracts.Services; +using HCM.Views; + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace HCM.Services; + +public class ActivationService : IActivationService +{ + private readonly ActivationHandler _defaultHandler; + private readonly IEnumerable _activationHandlers; + private readonly IThemeSelectorService _themeSelectorService; + private UIElement? _shell = null; + + public ActivationService(ActivationHandler defaultHandler, IEnumerable 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(); + 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; + } +} diff --git a/HCM/Services/AppNotificationService.cs b/HCM/Services/AppNotificationService.cs new file mode 100644 index 0000000..02c2014 --- /dev/null +++ b/HCM/Services/AppNotificationService.cs @@ -0,0 +1,71 @@ +using System.Collections.Specialized; +using System.Web; + +using HCM.Contracts.Services; +using HCM.ViewModels; + +using Microsoft.Windows.AppNotifications; + +namespace HCM.Notifications; + +public class AppNotificationService : IAppNotificationService +{ + private readonly INavigationService _navigationService; + + public AppNotificationService(INavigationService navigationService) + { + _navigationService = navigationService; + } + + ~AppNotificationService() + { + Unregister(); + } + + public void Initialize() + { + AppNotificationManager.Default.NotificationInvoked += OnNotificationInvoked; + + AppNotificationManager.Default.Register(); + } + + public void OnNotificationInvoked(AppNotificationManager sender, AppNotificationActivatedEventArgs args) + { + // TODO: Handle notification invocations when your app is already running. + + //// // Navigate to a specific page based on the notification arguments. + //// if (ParseArguments(args.Argument)["action"] == "Settings") + //// { + //// App.MainWindow.DispatcherQueue.TryEnqueue(() => + //// { + //// _navigationService.NavigateTo(typeof(SettingsViewModel).FullName!); + //// }); + //// } + + App.MainWindow.DispatcherQueue.TryEnqueue(() => + { + App.MainWindow.ShowMessageDialogAsync("TODO: Handle notification invocations when your app is already running.", "Notification Invoked"); + + App.MainWindow.BringToFront(); + }); + } + + public bool Show(string payload) + { + var appNotification = new AppNotification(payload); + + AppNotificationManager.Default.Show(appNotification); + + return appNotification.Id != 0; + } + + public NameValueCollection ParseArguments(string arguments) + { + return HttpUtility.ParseQueryString(arguments); + } + + public void Unregister() + { + AppNotificationManager.Default.Unregister(); + } +} diff --git a/HCM/Services/LocalSettingsService.cs b/HCM/Services/LocalSettingsService.cs new file mode 100644 index 0000000..2aac3a5 --- /dev/null +++ b/HCM/Services/LocalSettingsService.cs @@ -0,0 +1,88 @@ +using HCM.Contracts.Services; +using HCM.Core.Contracts.Services; +using HCM.Core.Helpers; +using HCM.Helpers; +using HCM.Models; + +using Microsoft.Extensions.Options; + +using Windows.ApplicationModel; +using Windows.Storage; + +namespace HCM.Services; + +public class LocalSettingsService : ILocalSettingsService +{ + private const string _defaultApplicationDataFolder = "HCM/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 _settings; + + private bool _isInitialized; + + public LocalSettingsService(IFileService fileService, IOptions options) + { + _fileService = fileService; + _options = options.Value; + + _applicationDataFolder = Path.Combine(_localApplicationData, _options.ApplicationDataFolder ?? _defaultApplicationDataFolder); + _localsettingsFile = _options.LocalSettingsFile ?? _defaultLocalSettingsFile; + + _settings = new Dictionary(); + } + + private async Task InitializeAsync() + { + if (!_isInitialized) + { + _settings = await Task.Run(() => _fileService.Read>(_applicationDataFolder, _localsettingsFile)) ?? new Dictionary(); + + _isInitialized = true; + } + } + + public async Task ReadSettingAsync(string key) + { + if (RuntimeHelper.IsMSIX) + { + if (ApplicationData.Current.LocalSettings.Values.TryGetValue(key, out var obj)) + { + return await Json.ToObjectAsync((string)obj); + } + } + else + { + await InitializeAsync(); + + if (_settings != null && _settings.TryGetValue(key, out var obj)) + { + return await Json.ToObjectAsync((string)obj); + } + } + + return default; + } + + public async Task SaveSettingAsync(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)); + } + } +} diff --git a/HCM/Services/NavigationService.cs b/HCM/Services/NavigationService.cs new file mode 100644 index 0000000..400164b --- /dev/null +++ b/HCM/Services/NavigationService.cs @@ -0,0 +1,126 @@ +using System.Diagnostics.CodeAnalysis; + +using HCM.Contracts.Services; +using HCM.Contracts.ViewModels; +using HCM.Helpers; + +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Navigation; + +namespace HCM.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); + } + } +} diff --git a/HCM/Services/NavigationViewService.cs b/HCM/Services/NavigationViewService.cs new file mode 100644 index 0000000..6c72d2b --- /dev/null +++ b/HCM/Services/NavigationViewService.cs @@ -0,0 +1,103 @@ +using System.Diagnostics.CodeAnalysis; + +using HCM.Contracts.Services; +using HCM.Helpers; +using HCM.ViewModels; + +using Microsoft.UI.Xaml.Controls; + +namespace HCM.Services; + +public class NavigationViewService : INavigationViewService +{ + private readonly INavigationService _navigationService; + + private readonly IPageService _pageService; + + private NavigationView? _navigationView; + + public IList? 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 menuItems, Type pageType) + { + foreach (var item in menuItems.OfType()) + { + 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; + } +} diff --git a/HCM/Services/PageService.cs b/HCM/Services/PageService.cs new file mode 100644 index 0000000..cd9ef8a --- /dev/null +++ b/HCM/Services/PageService.cs @@ -0,0 +1,57 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +using HCM.Contracts.Services; +using HCM.ViewModels; +using HCM.Views; + +using Microsoft.UI.Xaml.Controls; + +namespace HCM.Services; + +public class PageService : IPageService +{ + private readonly Dictionary _pages = new(); + + public PageService() + { + Configure(); + Configure(); + Configure(); + } + + 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() + 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); + } + } +} diff --git a/HCM/Services/ThemeSelectorService.cs b/HCM/Services/ThemeSelectorService.cs new file mode 100644 index 0000000..e3b324d --- /dev/null +++ b/HCM/Services/ThemeSelectorService.cs @@ -0,0 +1,63 @@ +using HCM.Contracts.Services; +using HCM.Helpers; + +using Microsoft.UI.Xaml; + +namespace HCM.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 LoadThemeFromSettingsAsync() + { + var themeName = await _localSettingsService.ReadSettingAsync(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()); + } +} diff --git a/HCM/Strings/en-us/Resources.resw b/HCM/Strings/en-us/Resources.resw new file mode 100644 index 0000000..00c6e30 --- /dev/null +++ b/HCM/Strings/en-us/Resources.resw @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 动车组交路查询 + + + 动车组交路查询 + + + 主页 + + + 个性化 + + + 主题 + + + 浅色 + + + 深色 + + + 默认 + + + 关于 + + + 马上开学了赶紧写点代码爽一把 + + + 隐私政策 + + + https://YourPrivacyUrlGoesHere/ + + + <toast launch="action=ToastClick"> + <visual> + <binding template="ToastGeneric"> + <text>App Notification</text> + <text></text> + <image placement="appLogoOverride" hint-crop="circle" src="{0}Assets/WindowIcon.ico"/> + </binding> + </visual> + <actions> + <action content="Settings" arguments="action=Settings"/> + </actions> +</toast> + + + 动车组交路查询 + + + 打开china-emu网站 + + + 动车组列车 + + + 请输入列车编号 + + + 数据源:rail.re + + + 动车组交路查询 + + + 请检查网络链接和输入内容 + + + 查询失败 + + + 查询 + + + 打开下关站网站 + + + 下关站 + + \ No newline at end of file diff --git a/HCM/Styles/FontSizes.xaml b/HCM/Styles/FontSizes.xaml new file mode 100644 index 0000000..44904b1 --- /dev/null +++ b/HCM/Styles/FontSizes.xaml @@ -0,0 +1,9 @@ + + + 24 + + 16 + + diff --git a/HCM/Styles/TextBlock.xaml b/HCM/Styles/TextBlock.xaml new file mode 100644 index 0000000..0f9eb84 --- /dev/null +++ b/HCM/Styles/TextBlock.xaml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/HCM/Styles/Thickness.xaml b/HCM/Styles/Thickness.xaml new file mode 100644 index 0000000..96ef0c9 --- /dev/null +++ b/HCM/Styles/Thickness.xaml @@ -0,0 +1,36 @@ + + + 0,36,0,0 + 0,36,0,36 + + 0,24,0,0 + 0,24,0,24 + 24,0,24,0 + 0,0,0,24 + + 12,0,0,0 + 12,0,12,0 + 0,12,0,0 + 0,0,12,0 + 0,12,0,12 + + 8,0,0,0 + 0,8,0,0 + 8,8,8,8 + + 0,4,0,0 + 4,4,4,4 + + 1,1,0,0 + 8,0,0,0 + 0,48,0,0 + 56,34,0,0 + 56,24,56,0 + + 36,24,36,0 + + -12,4,0,0 + + diff --git a/HCM/TemplateStudio.xml b/HCM/TemplateStudio.xml new file mode 100644 index 0000000..0c8e42d --- /dev/null +++ b/HCM/TemplateStudio.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/HCM/Usings.cs b/HCM/Usings.cs new file mode 100644 index 0000000..4cc487e --- /dev/null +++ b/HCM/Usings.cs @@ -0,0 +1 @@ +global using WinUIEx; diff --git a/HCM/ViewModels/EMU_RoutingViewModel.cs b/HCM/ViewModels/EMU_RoutingViewModel.cs new file mode 100644 index 0000000..abf5c92 --- /dev/null +++ b/HCM/ViewModels/EMU_RoutingViewModel.cs @@ -0,0 +1,10 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace HCM.ViewModels; + +public partial class EMU_RoutingViewModel : ObservableRecipient +{ + public EMU_RoutingViewModel() + { + } +} diff --git a/HCM/ViewModels/MainViewModel.cs b/HCM/ViewModels/MainViewModel.cs new file mode 100644 index 0000000..729aa7a --- /dev/null +++ b/HCM/ViewModels/MainViewModel.cs @@ -0,0 +1,10 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace HCM.ViewModels; + +public partial class MainViewModel : ObservableRecipient +{ + public MainViewModel() + { + } +} diff --git a/HCM/ViewModels/SettingsViewModel.cs b/HCM/ViewModels/SettingsViewModel.cs new file mode 100644 index 0000000..b926eac --- /dev/null +++ b/HCM/ViewModels/SettingsViewModel.cs @@ -0,0 +1,65 @@ +using System.Reflection; +using System.Windows.Input; + +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; + +using HCM.Contracts.Services; +using HCM.Helpers; + +using Microsoft.UI.Xaml; + +using Windows.ApplicationModel; + +namespace HCM.ViewModels; + +public partial class SettingsViewModel : ObservableRecipient +{ + private readonly IThemeSelectorService _themeSelectorService; + + [ObservableProperty] + private ElementTheme _elementTheme; + + [ObservableProperty] + private string _versionDescription; + + public ICommand SwitchThemeCommand + { + get; + } + + public SettingsViewModel(IThemeSelectorService themeSelectorService) + { + _themeSelectorService = themeSelectorService; + _elementTheme = _themeSelectorService.Theme; + _versionDescription = GetVersionDescription(); + + SwitchThemeCommand = new RelayCommand( + async (param) => + { + if (ElementTheme != param) + { + ElementTheme = param; + await _themeSelectorService.SetThemeAsync(param); + } + }); + } + + private static string GetVersionDescription() + { + Version version; + + if (RuntimeHelper.IsMSIX) + { + var packageVersion = Package.Current.Id.Version; + + version = new(packageVersion.Major, packageVersion.Minor, packageVersion.Build, packageVersion.Revision); + } + else + { + version = Assembly.GetExecutingAssembly().GetName().Version!; + } + + return $"{"AppDisplayName".GetLocalized()} - {version.Major}.{version.Minor}.{version.Build}.{version.Revision}"; + } +} diff --git a/HCM/ViewModels/ShellViewModel.cs b/HCM/ViewModels/ShellViewModel.cs new file mode 100644 index 0000000..7bee4b1 --- /dev/null +++ b/HCM/ViewModels/ShellViewModel.cs @@ -0,0 +1,51 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +using HCM.Contracts.Services; +using HCM.Views; + +using Microsoft.UI.Xaml.Navigation; + +namespace HCM.ViewModels; + +public partial class ShellViewModel : ObservableRecipient +{ + [ObservableProperty] + private bool isBackEnabled; + + [ObservableProperty] + private object? selected; + + public INavigationService NavigationService + { + get; + } + + public INavigationViewService NavigationViewService + { + get; + } + + public ShellViewModel(INavigationService navigationService, INavigationViewService navigationViewService) + { + NavigationService = navigationService; + NavigationService.Navigated += OnNavigated; + NavigationViewService = navigationViewService; + } + + private void OnNavigated(object sender, NavigationEventArgs e) + { + IsBackEnabled = NavigationService.CanGoBack; + + if (e.SourcePageType == typeof(SettingsPage)) + { + Selected = NavigationViewService.SettingsItem; + return; + } + + var selectedItem = NavigationViewService.GetSelectedItem(e.SourcePageType); + if (selectedItem != null) + { + Selected = selectedItem; + } + } +} diff --git a/HCM/Views/EMU_RoutingPage.xaml b/HCM/Views/EMU_RoutingPage.xaml new file mode 100644 index 0000000..2a1a7e2 --- /dev/null +++ b/HCM/Views/EMU_RoutingPage.xaml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + +