Add project files.
No files matched your search
@@ -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
|
||||||
@@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace GZStudio.Core.Contracts.Services;
|
||||||
|
|
||||||
|
public interface IFileService
|
||||||
|
{
|
||||||
|
T Read<T>(string folderPath, string fileName);
|
||||||
|
|
||||||
|
void Save<T>(string folderPath, string fileName, T content);
|
||||||
|
|
||||||
|
void Delete(string folderPath, string fileName);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<RootNamespace>GZStudio.Core</RootNamespace>
|
||||||
|
<Platforms>AnyCPU;x64;x86</Platforms>
|
||||||
|
<Platforms>x86;x64;arm64;AnyCPU</Platforms>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Models\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace GZStudio.Core.Helpers;
|
||||||
|
|
||||||
|
public static class Json
|
||||||
|
{
|
||||||
|
public static async Task<T> ToObjectAsync<T>(string value)
|
||||||
|
{
|
||||||
|
return await Task.Run<T>(() =>
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<T>(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<string> StringifyAsync(object value)
|
||||||
|
{
|
||||||
|
return await Task.Run<string>(() =>
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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).
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using GZStudio.Core.Contracts.Services;
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace GZStudio.Core.Services;
|
||||||
|
|
||||||
|
public class FileService : IFileService
|
||||||
|
{
|
||||||
|
public T Read<T>(string folderPath, string fileName)
|
||||||
|
{
|
||||||
|
var path = Path.Combine(folderPath, fileName);
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
var json = File.ReadAllText(path);
|
||||||
|
return JsonConvert.DeserializeObject<T>(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save<T>(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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
|
||||||
|
<RootNamespace>GZStudio.Tests.MSTest</RootNamespace>
|
||||||
|
<Platforms>x86;x64;arm64</Platforms>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<UseWinUI>true</UseWinUI>
|
||||||
|
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||||
|
<ProjectPriFileName>resources.pri</ProjectPriFileName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="3.1.2" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||||
|
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
||||||
|
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\GZStudio\GZStudio.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using Microsoft.Windows.ApplicationModel.DynamicDependency;
|
||||||
|
|
||||||
|
[assembly: WinUITestTarget(typeof(GZStudio.App))]
|
||||||
|
|
||||||
|
namespace GZStudio.Tests.MSTest;
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class Initialize
|
||||||
|
{
|
||||||
|
[AssemblyInitialize]
|
||||||
|
public static void AssemblyInitialize(TestContext context)
|
||||||
|
{
|
||||||
|
// TODO: Initialize the appropriate version of the Windows App SDK.
|
||||||
|
// This is required when testing MSIX apps that are framework-dependent on the Windows App SDK.
|
||||||
|
Bootstrap.TryInitialize(0x00010001, out var _);
|
||||||
|
}
|
||||||
|
|
||||||
|
[AssemblyCleanup]
|
||||||
|
public static void AssemblyCleanup()
|
||||||
|
{
|
||||||
|
Bootstrap.Shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
*Recommended Markdown Viewer: [Markdown Editor](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.MarkdownEditor2)*
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
[Get started with unit testing](https://docs.microsoft.com/visualstudio/test/getting-started-with-unit-testing?view=vs-2022&tabs=dotnet%2Cmstest), [Use the MSTest framework in unit tests](https://docs.microsoft.com/visualstudio/test/using-microsoft-visualstudio-testtools-unittesting-members-in-unit-tests), and [Run unit tests with Test Explorer](https://docs.microsoft.com/visualstudio/test/run-unit-tests-with-test-explorer) provide an overview of the MSTest framework and Test Explorer.
|
||||||
|
|
||||||
|
## Testing UI Controls
|
||||||
|
|
||||||
|
Unit tests that exercise UI controls must run on the WinUI UI thread or they will throw an exception. To run a test on the WinUI UI thread, mark the test method with `[UITestMethod]` instead of `[TestMethod]`. During test execution, the test host will launch the app and dispatch the test to the app's UI thread.
|
||||||
|
|
||||||
|
The below example creates a `new Grid()` and then validates that its `ActualWidth` is `0`.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[UITestMethod]
|
||||||
|
public void UITestMethod()
|
||||||
|
{
|
||||||
|
Assert.AreEqual(0, new Grid().ActualWidth);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependency Injection and Mocking
|
||||||
|
|
||||||
|
Template Studio uses [dependency injection](https://docs.microsoft.com/dotnet/core/extensions/dependency-injection) which means class dependencies implement interfaces and those dependencies are injected via class constructors.
|
||||||
|
|
||||||
|
One of the many benefits of this approach is improved testability, since tests can produce mock implementations of the interfaces and pass them into the object being tested, isolating the object being tested from its dependencies. To mock an interface, create a class that implements the interface, create stub implementations of the interface members, then pass an instance of the class into the object constructor.
|
||||||
|
|
||||||
|
The below example demonstrates testing the ViewModel for the Settings page. `SettingsViewModel` depends on `IThemeSelectorService`, so a `MockThemeSelectorService` class is introduced that implements the interface with stub implementations, and then an instance of that class is passed into the `SettingsViewModel` constructor. The `VerifyVersionDescription` test then validates that the `VersionDescription` property of the `SettingsViewModel` returns the expected value.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// SettingsViewModelTests.cs
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class SettingsViewModelTests
|
||||||
|
{
|
||||||
|
private readonly SettingsViewModel _viewModel;
|
||||||
|
|
||||||
|
public SettingsViewModelTests()
|
||||||
|
{
|
||||||
|
_viewModel = new SettingsViewModel(new MockThemeSelectorService());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void VerifyVersionDescription()
|
||||||
|
{
|
||||||
|
Assert.IsTrue(Regex.IsMatch(_viewModel.VersionDescription, @"App1 - \d\.\d\.\d\.\d"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Mocks/MockThemeSelectorService.cs
|
||||||
|
|
||||||
|
internal class MockThemeSelectorService : IThemeSelectorService
|
||||||
|
{
|
||||||
|
public ElementTheme Theme => ElementTheme.Default;
|
||||||
|
|
||||||
|
public Task InitializeAsync() => Task.CompletedTask;
|
||||||
|
|
||||||
|
public Task SetRequestedThemeAsync() => Task.CompletedTask;
|
||||||
|
|
||||||
|
public Task SetThemeAsync(ElementTheme theme) => Task.CompletedTask;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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.
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace GZStudio.Tests.MSTest;
|
||||||
|
|
||||||
|
// TODO: Write unit tests.
|
||||||
|
// https://docs.microsoft.com/visualstudio/test/getting-started-with-unit-testing
|
||||||
|
// https://docs.microsoft.com/visualstudio/test/using-microsoft-visualstudio-testtools-unittesting-members-in-unit-tests
|
||||||
|
// https://docs.microsoft.com/visualstudio/test/run-unit-tests-with-test-explorer
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class TestClass
|
||||||
|
{
|
||||||
|
[ClassInitialize]
|
||||||
|
public static void ClassInitialize(TestContext context)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("ClassInitialize");
|
||||||
|
}
|
||||||
|
|
||||||
|
[ClassCleanup]
|
||||||
|
public static void ClassCleanup()
|
||||||
|
{
|
||||||
|
Debug.WriteLine("ClassCleanup");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public void TestInitialize()
|
||||||
|
{
|
||||||
|
Debug.WriteLine("TestInitialize");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCleanup]
|
||||||
|
public void TestCleanup()
|
||||||
|
{
|
||||||
|
Debug.WriteLine("TestCleanup");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestMethod()
|
||||||
|
{
|
||||||
|
Assert.IsTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UITestMethod]
|
||||||
|
public void UITestMethod()
|
||||||
|
{
|
||||||
|
Assert.AreEqual(0, new Grid().ActualWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
global using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
global using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer;
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.7.34024.191
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GZStudio", "GZStudio\GZStudio.csproj", "{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GZStudio.Core", "GZStudio.Core\GZStudio.Core.csproj", "{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GZStudio.Tests.MSTest", "GZStudio.Tests.MSTest\GZStudio.Tests.MSTest.csproj", "{71ED1C68-527A-4840-9AF2-B072E7657014}"
|
||||||
|
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
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Debug|Any CPU.Build.0 = Debug|x64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Debug|Any CPU.Deploy.0 = Debug|x64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Debug|arm64.ActiveCfg = Debug|arm64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Debug|arm64.Build.0 = Debug|arm64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Debug|arm64.Deploy.0 = Debug|arm64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Debug|x64.Deploy.0 = Debug|x64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Debug|x86.Deploy.0 = Debug|x86
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Release|Any CPU.Build.0 = Release|x64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Release|Any CPU.Deploy.0 = Release|x64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Release|arm64.ActiveCfg = Release|arm64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Release|arm64.Build.0 = Release|arm64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Release|arm64.Deploy.0 = Release|arm64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Release|x64.Build.0 = Release|x64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Release|x64.Deploy.0 = Release|x64
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Release|x86.Build.0 = Release|x86
|
||||||
|
{DF204A25-57A3-4C07-BF8E-8CECCBAE7CE1}.Release|x86.Deploy.0 = Release|x86
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Debug|arm64.ActiveCfg = Debug|arm64
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Debug|arm64.Build.0 = Debug|arm64
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Release|arm64.ActiveCfg = Release|arm64
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Release|arm64.Build.0 = Release|arm64
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Release|x64.Build.0 = Release|x64
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{23F739DF-34E6-492C-A5C8-DDBCE1CEF575}.Release|x86.Build.0 = Release|x86
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Debug|Any CPU.Build.0 = Debug|x64
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Debug|arm64.ActiveCfg = Debug|arm64
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Debug|arm64.Build.0 = Debug|arm64
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Release|Any CPU.Build.0 = Release|x64
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Release|arm64.ActiveCfg = Release|arm64
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Release|arm64.Build.0 = Release|arm64
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Release|x64.Build.0 = Release|x64
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{71ED1C68-527A-4840-9AF2-B072E7657014}.Release|x86.Build.0 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {E6C3F9DC-AEED-44D1-85E7-708B75607331}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
namespace GZStudio.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<T> : 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)!);
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using GZStudio.Contracts.Services;
|
||||||
|
using GZStudio.ViewModels;
|
||||||
|
|
||||||
|
using Microsoft.UI.Dispatching;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.Windows.AppLifecycle;
|
||||||
|
using Microsoft.Windows.AppNotifications;
|
||||||
|
|
||||||
|
namespace GZStudio.Activation;
|
||||||
|
|
||||||
|
public class AppNotificationActivationHandler : ActivationHandler<LaunchActivatedEventArgs>
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using GZStudio.Contracts.Services;
|
||||||
|
using GZStudio.ViewModels;
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
|
||||||
|
namespace GZStudio.Activation;
|
||||||
|
|
||||||
|
public class DefaultActivationHandler : ActivationHandler<LaunchActivatedEventArgs>
|
||||||
|
{
|
||||||
|
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(AppListViewModel).FullName!, args.Arguments);
|
||||||
|
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace GZStudio.Activation;
|
||||||
|
|
||||||
|
public interface IActivationHandler
|
||||||
|
{
|
||||||
|
bool CanHandle(object args);
|
||||||
|
|
||||||
|
Task HandleAsync(object args);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<Application
|
||||||
|
x:Class="GZStudio.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<Application.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||||
|
<ResourceDictionary Source="/Styles/FontSizes.xaml" />
|
||||||
|
<ResourceDictionary Source="/Styles/Thickness.xaml" />
|
||||||
|
<ResourceDictionary Source="/Styles/TextBlock.xaml" />
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
using GZStudio.Activation;
|
||||||
|
using GZStudio.Contracts.Services;
|
||||||
|
using GZStudio.Core.Contracts.Services;
|
||||||
|
using GZStudio.Core.Services;
|
||||||
|
using GZStudio.Helpers;
|
||||||
|
using GZStudio.Models;
|
||||||
|
using GZStudio.Notifications;
|
||||||
|
using GZStudio.Services;
|
||||||
|
using GZStudio.ViewModels;
|
||||||
|
using GZStudio.Views;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
|
||||||
|
namespace GZStudio;
|
||||||
|
|
||||||
|
// 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<T>()
|
||||||
|
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<ActivationHandler<LaunchActivatedEventArgs>, DefaultActivationHandler>();
|
||||||
|
|
||||||
|
// Other Activation Handlers
|
||||||
|
services.AddTransient<IActivationHandler, AppNotificationActivationHandler>();
|
||||||
|
|
||||||
|
// Services
|
||||||
|
services.AddSingleton<IAppNotificationService, AppNotificationService>();
|
||||||
|
services.AddSingleton<ILocalSettingsService, LocalSettingsService>();
|
||||||
|
services.AddSingleton<IThemeSelectorService, ThemeSelectorService>();
|
||||||
|
services.AddTransient<INavigationViewService, NavigationViewService>();
|
||||||
|
|
||||||
|
services.AddSingleton<IActivationService, ActivationService>();
|
||||||
|
services.AddSingleton<IPageService, PageService>();
|
||||||
|
services.AddSingleton<INavigationService, NavigationService>();
|
||||||
|
|
||||||
|
// Core Services
|
||||||
|
services.AddSingleton<IFileService, FileService>();
|
||||||
|
|
||||||
|
// Views and ViewModels
|
||||||
|
services.AddTransient<SettingsViewModel>();
|
||||||
|
services.AddTransient<SettingsPage>();
|
||||||
|
services.AddTransient<AppListViewModel>();
|
||||||
|
services.AddTransient<AppListPage>();
|
||||||
|
services.AddTransient<ShellPage>();
|
||||||
|
services.AddTransient<ShellViewModel>();
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
services.Configure<LocalSettingsOptions>(context.Configuration.GetSection(nameof(LocalSettingsOptions)));
|
||||||
|
}).
|
||||||
|
Build();
|
||||||
|
|
||||||
|
App.GetService<IAppNotificationService>().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<IAppNotificationService>().Show(string.Format("AppNotificationSamplePayload".GetLocalized(), AppContext.BaseDirectory));
|
||||||
|
|
||||||
|
await App.GetService<IActivationService>().ActivateAsync(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 65 KiB |
@@ -0,0 +1,122 @@
|
|||||||
|
using GZStudio.Contracts.Services;
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
|
using Microsoft.Xaml.Interactivity;
|
||||||
|
|
||||||
|
namespace GZStudio.Behaviors;
|
||||||
|
|
||||||
|
public class NavigationViewHeaderBehavior : Behavior<NavigationView>
|
||||||
|
{
|
||||||
|
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<INavigationService>();
|
||||||
|
navigationService.Navigated += OnNavigated;
|
||||||
|
|
||||||
|
_current = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDetaching()
|
||||||
|
{
|
||||||
|
base.OnDetaching();
|
||||||
|
|
||||||
|
var navigationService = App.GetService<INavigationService>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace GZStudio.Behaviors;
|
||||||
|
|
||||||
|
public enum NavigationViewHeaderMode
|
||||||
|
{
|
||||||
|
Always,
|
||||||
|
Never,
|
||||||
|
Minimal
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace GZStudio.Contracts.Services;
|
||||||
|
|
||||||
|
public interface IActivationService
|
||||||
|
{
|
||||||
|
Task ActivateAsync(object activationArgs);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System.Collections.Specialized;
|
||||||
|
|
||||||
|
namespace GZStudio.Contracts.Services;
|
||||||
|
|
||||||
|
public interface IAppNotificationService
|
||||||
|
{
|
||||||
|
void Initialize();
|
||||||
|
|
||||||
|
bool Show(string payload);
|
||||||
|
|
||||||
|
NameValueCollection ParseArguments(string arguments);
|
||||||
|
|
||||||
|
void Unregister();
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace GZStudio.Contracts.Services;
|
||||||
|
|
||||||
|
public interface ILocalSettingsService
|
||||||
|
{
|
||||||
|
Task<T?> ReadSettingAsync<T>(string key);
|
||||||
|
|
||||||
|
Task SaveSettingAsync<T>(string key, T value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
|
|
||||||
|
namespace GZStudio.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();
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace GZStudio.Contracts.Services;
|
||||||
|
|
||||||
|
public interface INavigationViewService
|
||||||
|
{
|
||||||
|
IList<object>? MenuItems
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
object? SettingsItem
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize(NavigationView navigationView);
|
||||||
|
|
||||||
|
void UnregisterEvents();
|
||||||
|
|
||||||
|
NavigationViewItem? GetSelectedItem(Type pageType);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace GZStudio.Contracts.Services;
|
||||||
|
|
||||||
|
public interface IPageService
|
||||||
|
{
|
||||||
|
Type GetPageType(string key);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
|
||||||
|
namespace GZStudio.Contracts.Services;
|
||||||
|
|
||||||
|
public interface IThemeSelectorService
|
||||||
|
{
|
||||||
|
ElementTheme Theme
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
Task InitializeAsync();
|
||||||
|
|
||||||
|
Task SetThemeAsync(ElementTheme theme);
|
||||||
|
|
||||||
|
Task SetRequestedThemeAsync();
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace GZStudio.Contracts.ViewModels;
|
||||||
|
|
||||||
|
public interface INavigationAware
|
||||||
|
{
|
||||||
|
void OnNavigatedTo(object parameter);
|
||||||
|
|
||||||
|
void OnNavigatedFrom();
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
|
||||||
|
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||||
|
<RootNamespace>GZStudio</RootNamespace>
|
||||||
|
<ApplicationIcon>Assets/WindowIcon.ico</ApplicationIcon>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
<Platforms>x86;x64;arm64</Platforms>
|
||||||
|
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
|
||||||
|
<PublishProfile>Properties\PublishProfiles\win10-$(Platform).pubxml</PublishProfile>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<UseWinUI>true</UseWinUI>
|
||||||
|
<EnableMsixTooling>true</EnableMsixTooling>
|
||||||
|
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Manifest Include="$(ApplicationManifest)" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.231008000" />
|
||||||
|
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
|
||||||
|
<PackageReference Include="WinUIEx" Version="2.3.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\GZStudio.Core\GZStudio.Core.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
||||||
|
<ProjectCapability Include="Msix" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
||||||
|
<HasPackageAndPublishMenu>true</HasPackageAndPublishMenu>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
|
||||||
|
namespace GZStudio.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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace GZStudio.Helpers;
|
||||||
|
|
||||||
|
public static class FrameExtensions
|
||||||
|
{
|
||||||
|
public static object? GetPageViewModel(this Frame frame) => frame?.Content?.GetType().GetProperty("ViewModel")?.GetValue(frame.Content, null);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace GZStudio.Helpers;
|
||||||
|
|
||||||
|
// Helper class to set the navigation target for a NavigationViewItem.
|
||||||
|
//
|
||||||
|
// Usage in XAML:
|
||||||
|
// <NavigationViewItem x:Uid="Shell_Main" Icon="Document" helpers:NavigationHelper.NavigateTo="AppName.ViewModels.MainViewModel" />
|
||||||
|
//
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using Microsoft.Windows.ApplicationModel.Resources;
|
||||||
|
|
||||||
|
namespace GZStudio.Helpers;
|
||||||
|
|
||||||
|
public static class ResourceExtensions
|
||||||
|
{
|
||||||
|
private static readonly ResourceLoader _resourceLoader = new();
|
||||||
|
|
||||||
|
public static string GetLocalized(this string resourceKey) => _resourceLoader.GetString(resourceKey);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace GZStudio.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
using GZStudio.Core.Helpers;
|
||||||
|
|
||||||
|
using Windows.Storage;
|
||||||
|
using Windows.Storage.Streams;
|
||||||
|
|
||||||
|
namespace GZStudio.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<T>(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<T?> ReadAsync<T>(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<T>(fileContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task SaveAsync<T>(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<T?> ReadAsync<T>(this ApplicationDataContainer settings, string key)
|
||||||
|
{
|
||||||
|
object? obj;
|
||||||
|
|
||||||
|
if (settings.Values.TryGetValue(key, out obj))
|
||||||
|
{
|
||||||
|
return await Json.ToObjectAsync<T>((string)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<StorageFile> 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<byte[]?> 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<byte[]?> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 GZStudio.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<windowex:WindowEx
|
||||||
|
x:Class="GZStudio.MainWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="using:GZStudio"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:windowex="using:WinUIEx"
|
||||||
|
MinWidth="500"
|
||||||
|
MinHeight="500"
|
||||||
|
PersistenceId="MainWindow"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<Window.SystemBackdrop>
|
||||||
|
<MicaBackdrop/>
|
||||||
|
</Window.SystemBackdrop>
|
||||||
|
</windowex:WindowEx>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using GZStudio.Helpers;
|
||||||
|
|
||||||
|
using Windows.UI.ViewManagement;
|
||||||
|
|
||||||
|
namespace GZStudio;
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace GZStudio.Models;
|
||||||
|
|
||||||
|
public class LocalSettingsOptions
|
||||||
|
{
|
||||||
|
public string? ApplicationDataFolder
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? LocalSettingsFile
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AppInstaller Uri="{AppInstallerUri}"
|
||||||
|
Version="{Version}"
|
||||||
|
xmlns="http://schemas.microsoft.com/appx/appinstaller/2017/2">
|
||||||
|
|
||||||
|
<!-- Read more about updating settings with Package.appinstaller at https://docs.microsoft.com/windows/msix/app-installer/update-settings -->
|
||||||
|
|
||||||
|
<MainBundle Name="{Name}"
|
||||||
|
Version="{Version}"
|
||||||
|
Publisher="{Publisher}"
|
||||||
|
Uri="{MainPackageUri}"/>
|
||||||
|
|
||||||
|
<UpdateSettings>
|
||||||
|
<OnLaunch HoursBetweenUpdateChecks="0" />
|
||||||
|
</UpdateSettings>
|
||||||
|
|
||||||
|
</AppInstaller>
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<Package
|
||||||
|
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||||
|
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||||
|
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||||
|
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||||
|
xmlns:genTemplate="http://schemas.microsoft.com/appx/developer/templatestudio"
|
||||||
|
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
|
||||||
|
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||||
|
IgnorableNamespaces="uap rescap genTemplate">
|
||||||
|
|
||||||
|
<Identity
|
||||||
|
Name="7dea0040-809c-4f0e-9d37-1be796ce7494"
|
||||||
|
Publisher="CN=wn"
|
||||||
|
Version="1.0.0.0" />
|
||||||
|
|
||||||
|
<mp:PhoneIdentity PhoneProductId="7dea0040-809c-4f0e-9d37-1be796ce7494" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||||
|
|
||||||
|
<Properties>
|
||||||
|
<DisplayName>GZStudio</DisplayName>
|
||||||
|
<PublisherDisplayName>wn</PublisherDisplayName>
|
||||||
|
<Logo>Assets\StoreLogo.png</Logo>
|
||||||
|
</Properties>
|
||||||
|
|
||||||
|
<Dependencies>
|
||||||
|
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||||
|
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||||
|
</Dependencies>
|
||||||
|
|
||||||
|
<Resources>
|
||||||
|
<Resource Language="x-generate"/>
|
||||||
|
</Resources>
|
||||||
|
|
||||||
|
<Applications>
|
||||||
|
<Application Id="App"
|
||||||
|
Executable="$targetnametoken$.exe"
|
||||||
|
EntryPoint="$targetentrypoint$">
|
||||||
|
<uap:VisualElements
|
||||||
|
DisplayName="ms-resource:AppDisplayName"
|
||||||
|
Description="ms-resource:AppDescription"
|
||||||
|
BackgroundColor="transparent"
|
||||||
|
Square150x150Logo="Assets\Square150x150Logo.png"
|
||||||
|
Square44x44Logo="Assets\Square44x44Logo.png">
|
||||||
|
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
|
||||||
|
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||||
|
</uap:VisualElements>
|
||||||
|
<Extensions>
|
||||||
|
<desktop:Extension Category="windows.toastNotificationActivation">
|
||||||
|
<desktop:ToastNotificationActivation ToastActivatorCLSID="ed5ebccc-2499-4f74-9d42-f72efc41baa6" />
|
||||||
|
</desktop:Extension>
|
||||||
|
|
||||||
|
<com:Extension Category="windows.comServer">
|
||||||
|
<com:ComServer>
|
||||||
|
<com:ExeServer Executable="GZStudio.exe" Arguments="----AppNotificationActivated:" DisplayName="Toast activator">
|
||||||
|
<com:Class Id="ed5ebccc-2499-4f74-9d42-f72efc41baa6" DisplayName="Toast activator"/>
|
||||||
|
</com:ExeServer>
|
||||||
|
</com:ComServer>
|
||||||
|
</com:Extension>
|
||||||
|
</Extensions>
|
||||||
|
</Application>
|
||||||
|
</Applications>
|
||||||
|
|
||||||
|
<Capabilities>
|
||||||
|
<rescap:Capability Name="runFullTrust" />
|
||||||
|
</Capabilities>
|
||||||
|
|
||||||
|
<genTemplate:Metadata>
|
||||||
|
<genTemplate:Item Name="generator" Value="Template Studio"/>
|
||||||
|
<genTemplate:Item Name="wizardVersion" Version="v5.5" />
|
||||||
|
<genTemplate:Item Name="projectType" Value="NavView" />
|
||||||
|
<genTemplate:Item Name="framework" Value="MVVMToolkit" />
|
||||||
|
<genTemplate:Item Name="platform" Value="WinUI" />
|
||||||
|
<genTemplate:Item Name="appmodel" Value="Desktop" />
|
||||||
|
</genTemplate:Metadata>
|
||||||
|
</Package>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"GZStudio (Package)": {
|
||||||
|
"commandName": "MsixPackage"
|
||||||
|
},
|
||||||
|
"GZStudio (Unpackaged)": {
|
||||||
|
"commandName": "Project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
using GZStudio.Activation;
|
||||||
|
using GZStudio.Contracts.Services;
|
||||||
|
using GZStudio.Views;
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace GZStudio.Services;
|
||||||
|
|
||||||
|
public class ActivationService : IActivationService
|
||||||
|
{
|
||||||
|
private readonly ActivationHandler<LaunchActivatedEventArgs> _defaultHandler;
|
||||||
|
private readonly IEnumerable<IActivationHandler> _activationHandlers;
|
||||||
|
private readonly IThemeSelectorService _themeSelectorService;
|
||||||
|
private UIElement? _shell = null;
|
||||||
|
|
||||||
|
public ActivationService(ActivationHandler<LaunchActivatedEventArgs> defaultHandler, IEnumerable<IActivationHandler> activationHandlers, IThemeSelectorService themeSelectorService)
|
||||||
|
{
|
||||||
|
_defaultHandler = defaultHandler;
|
||||||
|
_activationHandlers = activationHandlers;
|
||||||
|
_themeSelectorService = themeSelectorService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ActivateAsync(object activationArgs)
|
||||||
|
{
|
||||||
|
// Execute tasks before activation.
|
||||||
|
await InitializeAsync();
|
||||||
|
|
||||||
|
// Set the MainWindow Content.
|
||||||
|
if (App.MainWindow.Content == null)
|
||||||
|
{
|
||||||
|
_shell = App.GetService<ShellPage>();
|
||||||
|
App.MainWindow.Content = _shell ?? new Frame();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle activation via ActivationHandlers.
|
||||||
|
await HandleActivationAsync(activationArgs);
|
||||||
|
|
||||||
|
// Activate the MainWindow.
|
||||||
|
App.MainWindow.Activate();
|
||||||
|
|
||||||
|
// Execute tasks after activation.
|
||||||
|
await StartupAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleActivationAsync(object activationArgs)
|
||||||
|
{
|
||||||
|
var activationHandler = _activationHandlers.FirstOrDefault(h => h.CanHandle(activationArgs));
|
||||||
|
|
||||||
|
if (activationHandler != null)
|
||||||
|
{
|
||||||
|
await activationHandler.HandleAsync(activationArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_defaultHandler.CanHandle(activationArgs))
|
||||||
|
{
|
||||||
|
await _defaultHandler.HandleAsync(activationArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
await _themeSelectorService.InitializeAsync().ConfigureAwait(false);
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StartupAsync()
|
||||||
|
{
|
||||||
|
await _themeSelectorService.SetRequestedThemeAsync();
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Web;
|
||||||
|
|
||||||
|
using GZStudio.Contracts.Services;
|
||||||
|
using GZStudio.ViewModels;
|
||||||
|
|
||||||
|
using Microsoft.Windows.AppNotifications;
|
||||||
|
|
||||||
|
namespace GZStudio.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
using GZStudio.Contracts.Services;
|
||||||
|
using GZStudio.Core.Contracts.Services;
|
||||||
|
using GZStudio.Core.Helpers;
|
||||||
|
using GZStudio.Helpers;
|
||||||
|
using GZStudio.Models;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
using Windows.ApplicationModel;
|
||||||
|
using Windows.Storage;
|
||||||
|
|
||||||
|
namespace GZStudio.Services;
|
||||||
|
|
||||||
|
public class LocalSettingsService : ILocalSettingsService
|
||||||
|
{
|
||||||
|
private const string _defaultApplicationDataFolder = "GZStudio/ApplicationData";
|
||||||
|
private const string _defaultLocalSettingsFile = "LocalSettings.json";
|
||||||
|
|
||||||
|
private readonly IFileService _fileService;
|
||||||
|
private readonly LocalSettingsOptions _options;
|
||||||
|
|
||||||
|
private readonly string _localApplicationData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||||
|
private readonly string _applicationDataFolder;
|
||||||
|
private readonly string _localsettingsFile;
|
||||||
|
|
||||||
|
private IDictionary<string, object> _settings;
|
||||||
|
|
||||||
|
private bool _isInitialized;
|
||||||
|
|
||||||
|
public LocalSettingsService(IFileService fileService, IOptions<LocalSettingsOptions> options)
|
||||||
|
{
|
||||||
|
_fileService = fileService;
|
||||||
|
_options = options.Value;
|
||||||
|
|
||||||
|
_applicationDataFolder = Path.Combine(_localApplicationData, _options.ApplicationDataFolder ?? _defaultApplicationDataFolder);
|
||||||
|
_localsettingsFile = _options.LocalSettingsFile ?? _defaultLocalSettingsFile;
|
||||||
|
|
||||||
|
_settings = new Dictionary<string, object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
if (!_isInitialized)
|
||||||
|
{
|
||||||
|
_settings = await Task.Run(() => _fileService.Read<IDictionary<string, object>>(_applicationDataFolder, _localsettingsFile)) ?? new Dictionary<string, object>();
|
||||||
|
|
||||||
|
_isInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T?> ReadSettingAsync<T>(string key)
|
||||||
|
{
|
||||||
|
if (RuntimeHelper.IsMSIX)
|
||||||
|
{
|
||||||
|
if (ApplicationData.Current.LocalSettings.Values.TryGetValue(key, out var obj))
|
||||||
|
{
|
||||||
|
return await Json.ToObjectAsync<T>((string)obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await InitializeAsync();
|
||||||
|
|
||||||
|
if (_settings != null && _settings.TryGetValue(key, out var obj))
|
||||||
|
{
|
||||||
|
return await Json.ToObjectAsync<T>((string)obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveSettingAsync<T>(string key, T value)
|
||||||
|
{
|
||||||
|
if (RuntimeHelper.IsMSIX)
|
||||||
|
{
|
||||||
|
ApplicationData.Current.LocalSettings.Values[key] = await Json.StringifyAsync(value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await InitializeAsync();
|
||||||
|
|
||||||
|
_settings[key] = await Json.StringifyAsync(value);
|
||||||
|
|
||||||
|
await Task.Run(() => _fileService.Save(_applicationDataFolder, _localsettingsFile, _settings));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
using GZStudio.Contracts.Services;
|
||||||
|
using GZStudio.Contracts.ViewModels;
|
||||||
|
using GZStudio.Helpers;
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
|
|
||||||
|
namespace GZStudio.Services;
|
||||||
|
|
||||||
|
// For more information on navigation between pages see
|
||||||
|
// https://github.com/microsoft/TemplateStudio/blob/main/docs/WinUI/navigation.md
|
||||||
|
public class NavigationService : INavigationService
|
||||||
|
{
|
||||||
|
private readonly IPageService _pageService;
|
||||||
|
private object? _lastParameterUsed;
|
||||||
|
private Frame? _frame;
|
||||||
|
|
||||||
|
public event NavigatedEventHandler? Navigated;
|
||||||
|
|
||||||
|
public Frame? Frame
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_frame == null)
|
||||||
|
{
|
||||||
|
_frame = App.MainWindow.Content as Frame;
|
||||||
|
RegisterFrameEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
UnregisterFrameEvents();
|
||||||
|
_frame = value;
|
||||||
|
RegisterFrameEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MemberNotNullWhen(true, nameof(Frame), nameof(_frame))]
|
||||||
|
public bool CanGoBack => Frame != null && Frame.CanGoBack;
|
||||||
|
|
||||||
|
public NavigationService(IPageService pageService)
|
||||||
|
{
|
||||||
|
_pageService = pageService;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterFrameEvents()
|
||||||
|
{
|
||||||
|
if (_frame != null)
|
||||||
|
{
|
||||||
|
_frame.Navigated += OnNavigated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnregisterFrameEvents()
|
||||||
|
{
|
||||||
|
if (_frame != null)
|
||||||
|
{
|
||||||
|
_frame.Navigated -= OnNavigated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GoBack()
|
||||||
|
{
|
||||||
|
if (CanGoBack)
|
||||||
|
{
|
||||||
|
var vmBeforeNavigation = _frame.GetPageViewModel();
|
||||||
|
_frame.GoBack();
|
||||||
|
if (vmBeforeNavigation is INavigationAware navigationAware)
|
||||||
|
{
|
||||||
|
navigationAware.OnNavigatedFrom();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool NavigateTo(string pageKey, object? parameter = null, bool clearNavigation = false)
|
||||||
|
{
|
||||||
|
var pageType = _pageService.GetPageType(pageKey);
|
||||||
|
|
||||||
|
if (_frame != null && (_frame.Content?.GetType() != pageType || (parameter != null && !parameter.Equals(_lastParameterUsed))))
|
||||||
|
{
|
||||||
|
_frame.Tag = clearNavigation;
|
||||||
|
var vmBeforeNavigation = _frame.GetPageViewModel();
|
||||||
|
var navigated = _frame.Navigate(pageType, parameter);
|
||||||
|
if (navigated)
|
||||||
|
{
|
||||||
|
_lastParameterUsed = parameter;
|
||||||
|
if (vmBeforeNavigation is INavigationAware navigationAware)
|
||||||
|
{
|
||||||
|
navigationAware.OnNavigatedFrom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return navigated;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnNavigated(object sender, NavigationEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is Frame frame)
|
||||||
|
{
|
||||||
|
var clearNavigation = (bool)frame.Tag;
|
||||||
|
if (clearNavigation)
|
||||||
|
{
|
||||||
|
frame.BackStack.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame.GetPageViewModel() is INavigationAware navigationAware)
|
||||||
|
{
|
||||||
|
navigationAware.OnNavigatedTo(e.Parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Navigated?.Invoke(sender, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
using GZStudio.Contracts.Services;
|
||||||
|
using GZStudio.Helpers;
|
||||||
|
using GZStudio.ViewModels;
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace GZStudio.Services;
|
||||||
|
|
||||||
|
public class NavigationViewService : INavigationViewService
|
||||||
|
{
|
||||||
|
private readonly INavigationService _navigationService;
|
||||||
|
|
||||||
|
private readonly IPageService _pageService;
|
||||||
|
|
||||||
|
private NavigationView? _navigationView;
|
||||||
|
|
||||||
|
public IList<object>? MenuItems => _navigationView?.MenuItems;
|
||||||
|
|
||||||
|
public object? SettingsItem => _navigationView?.SettingsItem;
|
||||||
|
|
||||||
|
public NavigationViewService(INavigationService navigationService, IPageService pageService)
|
||||||
|
{
|
||||||
|
_navigationService = navigationService;
|
||||||
|
_pageService = pageService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_navigationView))]
|
||||||
|
public void Initialize(NavigationView navigationView)
|
||||||
|
{
|
||||||
|
_navigationView = navigationView;
|
||||||
|
_navigationView.BackRequested += OnBackRequested;
|
||||||
|
_navigationView.ItemInvoked += OnItemInvoked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnregisterEvents()
|
||||||
|
{
|
||||||
|
if (_navigationView != null)
|
||||||
|
{
|
||||||
|
_navigationView.BackRequested -= OnBackRequested;
|
||||||
|
_navigationView.ItemInvoked -= OnItemInvoked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public NavigationViewItem? GetSelectedItem(Type pageType)
|
||||||
|
{
|
||||||
|
if (_navigationView != null)
|
||||||
|
{
|
||||||
|
return GetSelectedItem(_navigationView.MenuItems, pageType) ?? GetSelectedItem(_navigationView.FooterMenuItems, pageType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args) => _navigationService.GoBack();
|
||||||
|
|
||||||
|
private void OnItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
|
||||||
|
{
|
||||||
|
if (args.IsSettingsInvoked)
|
||||||
|
{
|
||||||
|
_navigationService.NavigateTo(typeof(SettingsViewModel).FullName!);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var selectedItem = args.InvokedItemContainer as NavigationViewItem;
|
||||||
|
|
||||||
|
if (selectedItem?.GetValue(NavigationHelper.NavigateToProperty) is string pageKey)
|
||||||
|
{
|
||||||
|
_navigationService.NavigateTo(pageKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private NavigationViewItem? GetSelectedItem(IEnumerable<object> menuItems, Type pageType)
|
||||||
|
{
|
||||||
|
foreach (var item in menuItems.OfType<NavigationViewItem>())
|
||||||
|
{
|
||||||
|
if (IsMenuItemForPageType(item, pageType))
|
||||||
|
{
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedChild = GetSelectedItem(item.MenuItems, pageType);
|
||||||
|
if (selectedChild != null)
|
||||||
|
{
|
||||||
|
return selectedChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsMenuItemForPageType(NavigationViewItem menuItem, Type sourcePageType)
|
||||||
|
{
|
||||||
|
if (menuItem.GetValue(NavigationHelper.NavigateToProperty) is string pageKey)
|
||||||
|
{
|
||||||
|
return _pageService.GetPageType(pageKey) == sourcePageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
using GZStudio.Contracts.Services;
|
||||||
|
using GZStudio.ViewModels;
|
||||||
|
using GZStudio.Views;
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace GZStudio.Services;
|
||||||
|
|
||||||
|
public class PageService : IPageService
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, Type> _pages = new();
|
||||||
|
|
||||||
|
public PageService()
|
||||||
|
{
|
||||||
|
Configure<AppListViewModel, AppListPage>();
|
||||||
|
Configure<SettingsViewModel, SettingsPage>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type GetPageType(string key)
|
||||||
|
{
|
||||||
|
Type? pageType;
|
||||||
|
lock (_pages)
|
||||||
|
{
|
||||||
|
if (!_pages.TryGetValue(key, out pageType))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Page not found: {key}. Did you forget to call PageService.Configure?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Configure<VM, V>()
|
||||||
|
where VM : ObservableObject
|
||||||
|
where V : Page
|
||||||
|
{
|
||||||
|
lock (_pages)
|
||||||
|
{
|
||||||
|
var key = typeof(VM).FullName!;
|
||||||
|
if (_pages.ContainsKey(key))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"The key {key} is already configured in PageService");
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = typeof(V);
|
||||||
|
if (_pages.ContainsValue(type))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"This type is already configured with key {_pages.First(p => p.Value == type).Key}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_pages.Add(key, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
using GZStudio.Contracts.Services;
|
||||||
|
using GZStudio.Helpers;
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
|
||||||
|
namespace GZStudio.Services;
|
||||||
|
|
||||||
|
public class ThemeSelectorService : IThemeSelectorService
|
||||||
|
{
|
||||||
|
private const string SettingsKey = "AppBackgroundRequestedTheme";
|
||||||
|
|
||||||
|
public ElementTheme Theme { get; set; } = ElementTheme.Default;
|
||||||
|
|
||||||
|
private readonly ILocalSettingsService _localSettingsService;
|
||||||
|
|
||||||
|
public ThemeSelectorService(ILocalSettingsService localSettingsService)
|
||||||
|
{
|
||||||
|
_localSettingsService = localSettingsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
Theme = await LoadThemeFromSettingsAsync();
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetThemeAsync(ElementTheme theme)
|
||||||
|
{
|
||||||
|
Theme = theme;
|
||||||
|
|
||||||
|
await SetRequestedThemeAsync();
|
||||||
|
await SaveThemeInSettingsAsync(Theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetRequestedThemeAsync()
|
||||||
|
{
|
||||||
|
if (App.MainWindow.Content is FrameworkElement rootElement)
|
||||||
|
{
|
||||||
|
rootElement.RequestedTheme = Theme;
|
||||||
|
|
||||||
|
TitleBarHelper.UpdateTitleBar(Theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ElementTheme> LoadThemeFromSettingsAsync()
|
||||||
|
{
|
||||||
|
var themeName = await _localSettingsService.ReadSettingAsync<string>(SettingsKey);
|
||||||
|
|
||||||
|
if (Enum.TryParse(themeName, out ElementTheme cacheTheme))
|
||||||
|
{
|
||||||
|
return cacheTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ElementTheme.Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveThemeInSettingsAsync(ElementTheme theme)
|
||||||
|
{
|
||||||
|
await _localSettingsService.SaveSettingAsync(SettingsKey, theme.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="AppDisplayName" xml:space="preserve">
|
||||||
|
<value>GZStudio</value>
|
||||||
|
</data>
|
||||||
|
<data name="AppDescription" xml:space="preserve">
|
||||||
|
<value>GZStudio</value>
|
||||||
|
</data>
|
||||||
|
<data name="Shell_AppList.Content" xml:space="preserve">
|
||||||
|
<value>AppList</value>
|
||||||
|
</data>
|
||||||
|
<data name="Settings_Personalization.Text" xml:space="preserve">
|
||||||
|
<value>Personalization</value>
|
||||||
|
</data>
|
||||||
|
<data name="Settings_Theme.Text" xml:space="preserve">
|
||||||
|
<value>Theme</value>
|
||||||
|
</data>
|
||||||
|
<data name="Settings_Theme_Light.Content" xml:space="preserve">
|
||||||
|
<value>Light</value>
|
||||||
|
</data>
|
||||||
|
<data name="Settings_Theme_Dark.Content" xml:space="preserve">
|
||||||
|
<value>Dark</value>
|
||||||
|
</data>
|
||||||
|
<data name="Settings_Theme_Default.Content" xml:space="preserve">
|
||||||
|
<value>Default</value>
|
||||||
|
</data>
|
||||||
|
<data name="Settings_About.Text" xml:space="preserve">
|
||||||
|
<value>About this application</value>
|
||||||
|
</data>
|
||||||
|
<data name="Settings_AboutDescription.Text" xml:space="preserve">
|
||||||
|
<value>TODO: Replace with your app description.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPage_PrivacyTermsLink.Content" xml:space="preserve">
|
||||||
|
<value>Privacy Statement</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPage_PrivacyTermsLink.NavigateUri" xml:space="preserve">
|
||||||
|
<value>https://YourPrivacyUrlGoesHere/</value>
|
||||||
|
</data>
|
||||||
|
<data name="AppNotificationSamplePayload" xml:space="preserve">
|
||||||
|
<value><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></value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<ResourceDictionary
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
|
||||||
|
<x:Double x:Key="LargeFontSize">24</x:Double>
|
||||||
|
|
||||||
|
<x:Double x:Key="MediumFontSize">16</x:Double>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<ResourceDictionary
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
|
||||||
|
<Style x:Key="PageTitleStyle" TargetType="TextBlock">
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
<Setter Property="FontWeight" Value="SemiLight" />
|
||||||
|
<Setter Property="FontSize" Value="{StaticResource LargeFontSize}" />
|
||||||
|
<Setter Property="TextTrimming" Value="CharacterEllipsis" />
|
||||||
|
<Setter Property="TextWrapping" Value="NoWrap" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style x:Key="BodyTextStyle" TargetType="TextBlock">
|
||||||
|
<Setter Property="FontWeight" Value="Normal" />
|
||||||
|
<Setter Property="FontSize" Value="{StaticResource MediumFontSize}" />
|
||||||
|
<Setter Property="TextTrimming" Value="CharacterEllipsis" />
|
||||||
|
<Setter Property="TextWrapping" Value="Wrap" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<ResourceDictionary
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
|
||||||
|
<Thickness x:Key="LargeTopMargin">0,36,0,0</Thickness>
|
||||||
|
<Thickness x:Key="LargeTopBottomMargin">0,36,0,36</Thickness>
|
||||||
|
|
||||||
|
<Thickness x:Key="MediumTopMargin">0,24,0,0</Thickness>
|
||||||
|
<Thickness x:Key="MediumTopBottomMargin">0,24,0,24</Thickness>
|
||||||
|
<Thickness x:Key="MediumLeftRightMargin">24,0,24,0</Thickness>
|
||||||
|
<Thickness x:Key="MediumBottomMargin">0,0,0,24</Thickness>
|
||||||
|
|
||||||
|
<Thickness x:Key="SmallLeftMargin">12,0,0,0</Thickness>
|
||||||
|
<Thickness x:Key="SmallLeftRightMargin">12,0,12,0</Thickness>
|
||||||
|
<Thickness x:Key="SmallTopMargin">0,12,0,0</Thickness>
|
||||||
|
<Thickness x:Key="SmallRightMargin">0,0,12,0</Thickness>
|
||||||
|
<Thickness x:Key="SmallTopBottomMargin">0,12,0,12</Thickness>
|
||||||
|
|
||||||
|
<Thickness x:Key="XSmallLeftMargin">8,0,0,0</Thickness>
|
||||||
|
<Thickness x:Key="XSmallTopMargin">0,8,0,0</Thickness>
|
||||||
|
<Thickness x:Key="XSmallLeftTopRightBottomMargin">8,8,8,8</Thickness>
|
||||||
|
|
||||||
|
<Thickness x:Key="XXSmallTopMargin">0,4,0,0</Thickness>
|
||||||
|
<Thickness x:Key="XXSmallLeftTopRightBottomMargin">4,4,4,4</Thickness>
|
||||||
|
|
||||||
|
<Thickness x:Key="NavigationViewContentGridBorderThickness">1,1,0,0</Thickness>
|
||||||
|
<CornerRadius x:Key="NavigationViewContentGridCornerRadius">8,0,0,0</CornerRadius>
|
||||||
|
<Thickness x:Key="NavigationViewContentMargin">0,48,0,0</Thickness>
|
||||||
|
<Thickness x:Key="NavigationViewHeaderMargin">56,34,0,0</Thickness>
|
||||||
|
<Thickness x:Key="NavigationViewPageContentMargin">56,24,56,0</Thickness>
|
||||||
|
|
||||||
|
<Thickness x:Key="MenuBarContentMargin">36,24,36,0</Thickness>
|
||||||
|
|
||||||
|
<Thickness x:Key="SettingsPageHyperlinkButtonMargin">-12,4,0,0</Thickness>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<xml
|
||||||
|
xmlns:genTemplate="http://schemas.microsoft.com/appx/developer/templatestudio">
|
||||||
|
<genTemplate:Metadata>
|
||||||
|
<genTemplate:Item Name="generator" Value="Template Studio"/>
|
||||||
|
<genTemplate:Item Name="wizardVersion" Version="v5.5" />
|
||||||
|
<genTemplate:Item Name="projectType" Value="NavView" />
|
||||||
|
<genTemplate:Item Name="framework" Value="MVVMToolkit" />
|
||||||
|
<genTemplate:Item Name="platform" Value="WinUI" />
|
||||||
|
<genTemplate:Item Name="appmodel" Value="Desktop" />
|
||||||
|
</genTemplate:Metadata>
|
||||||
|
</xml>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
global using WinUIEx;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace GZStudio.ViewModels;
|
||||||
|
|
||||||
|
public partial class AppListViewModel : ObservableRecipient
|
||||||
|
{
|
||||||
|
public AppListViewModel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
|
||||||
|
using GZStudio.Contracts.Services;
|
||||||
|
using GZStudio.Helpers;
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
|
||||||
|
using Windows.ApplicationModel;
|
||||||
|
|
||||||
|
namespace GZStudio.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<ElementTheme>(
|
||||||
|
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}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
using GZStudio.Contracts.Services;
|
||||||
|
using GZStudio.Views;
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
|
|
||||||
|
namespace GZStudio.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<Page
|
||||||
|
x:Class="GZStudio.Views.AppListPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<Grid x:Name="ContentArea">
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using GZStudio.ViewModels;
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace GZStudio.Views;
|
||||||
|
|
||||||
|
public sealed partial class AppListPage : Page
|
||||||
|
{
|
||||||
|
public AppListViewModel ViewModel
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppListPage()
|
||||||
|
{
|
||||||
|
ViewModel = App.GetService<AppListViewModel>();
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<Page
|
||||||
|
x:Class="GZStudio.Views.SettingsPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:helpers="using:GZStudio.Helpers"
|
||||||
|
xmlns:xaml="using:Microsoft.UI.Xaml"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<Page.Resources>
|
||||||
|
<helpers:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
|
||||||
|
</Page.Resources>
|
||||||
|
<Grid>
|
||||||
|
<StackPanel
|
||||||
|
x:Name="ContentArea">
|
||||||
|
<TextBlock x:Uid="Settings_Personalization" Style="{ThemeResource SubtitleTextBlockStyle}" />
|
||||||
|
<StackPanel Margin="{StaticResource SmallTopBottomMargin}">
|
||||||
|
<TextBlock x:Uid="Settings_Theme" />
|
||||||
|
|
||||||
|
<StackPanel Margin="{StaticResource XSmallTopMargin}">
|
||||||
|
<RadioButton
|
||||||
|
x:Uid="Settings_Theme_Light"
|
||||||
|
Command="{x:Bind ViewModel.SwitchThemeCommand}"
|
||||||
|
IsChecked="{x:Bind ViewModel.ElementTheme, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=Light, Mode=OneWay}"
|
||||||
|
FontSize="15"
|
||||||
|
GroupName="AppTheme">
|
||||||
|
<RadioButton.CommandParameter>
|
||||||
|
<xaml:ElementTheme>Light</xaml:ElementTheme>
|
||||||
|
</RadioButton.CommandParameter>
|
||||||
|
</RadioButton>
|
||||||
|
<RadioButton
|
||||||
|
x:Uid="Settings_Theme_Dark"
|
||||||
|
Command="{x:Bind ViewModel.SwitchThemeCommand}"
|
||||||
|
IsChecked="{x:Bind ViewModel.ElementTheme, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=Dark, Mode=OneWay}"
|
||||||
|
FontSize="15"
|
||||||
|
GroupName="AppTheme">
|
||||||
|
<RadioButton.CommandParameter>
|
||||||
|
<xaml:ElementTheme>Dark</xaml:ElementTheme>
|
||||||
|
</RadioButton.CommandParameter>
|
||||||
|
</RadioButton>
|
||||||
|
<RadioButton
|
||||||
|
x:Uid="Settings_Theme_Default"
|
||||||
|
Command="{x:Bind ViewModel.SwitchThemeCommand}"
|
||||||
|
IsChecked="{x:Bind ViewModel.ElementTheme, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=Default, Mode=OneWay}"
|
||||||
|
FontSize="15"
|
||||||
|
GroupName="AppTheme">
|
||||||
|
<RadioButton.CommandParameter>
|
||||||
|
<xaml:ElementTheme>Default</xaml:ElementTheme>
|
||||||
|
</RadioButton.CommandParameter>
|
||||||
|
</RadioButton>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<TextBlock x:Uid="Settings_About" Style="{ThemeResource SubtitleTextBlockStyle}" />
|
||||||
|
|
||||||
|
<StackPanel Margin="{StaticResource XSmallTopMargin}">
|
||||||
|
<TextBlock Text="{x:Bind ViewModel.VersionDescription, Mode=OneWay}" Style="{ThemeResource BodyTextBlockStyle}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
x:Uid="Settings_AboutDescription"
|
||||||
|
Margin="{StaticResource XSmallTopMargin}"
|
||||||
|
Style="{ThemeResource BodyTextBlockStyle}" />
|
||||||
|
<HyperlinkButton x:Uid="SettingsPage_PrivacyTermsLink" Margin="{StaticResource SettingsPageHyperlinkButtonMargin}" />
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using GZStudio.ViewModels;
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace GZStudio.Views;
|
||||||
|
|
||||||
|
// TODO: Set the URL for your privacy policy by updating SettingsPage_PrivacyTermsLink.NavigateUri in Resources.resw.
|
||||||
|
public sealed partial class SettingsPage : Page
|
||||||
|
{
|
||||||
|
public SettingsViewModel ViewModel
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SettingsPage()
|
||||||
|
{
|
||||||
|
ViewModel = App.GetService<SettingsViewModel>();
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
<Page
|
||||||
|
x:Class="GZStudio.Views.ShellPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:helpers="using:GZStudio.Helpers"
|
||||||
|
xmlns:behaviors="using:GZStudio.Behaviors"
|
||||||
|
xmlns:i="using:Microsoft.Xaml.Interactivity"
|
||||||
|
Loaded="OnLoaded">
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid x:Name="AppTitleBar"
|
||||||
|
Canvas.ZIndex="1"
|
||||||
|
Height="{Binding ElementName=NavigationViewControl, Path=CompactPaneLength}"
|
||||||
|
IsHitTestVisible="True"
|
||||||
|
VerticalAlignment="Top">
|
||||||
|
<Image Source="/Assets/WindowIcon.ico"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Width="16"
|
||||||
|
Height="16" />
|
||||||
|
<TextBlock x:Name="AppTitleBarText"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
Margin="28,0,0,0"/>
|
||||||
|
</Grid>
|
||||||
|
<NavigationView
|
||||||
|
x:Name="NavigationViewControl"
|
||||||
|
Canvas.ZIndex="0"
|
||||||
|
IsBackButtonVisible="Visible"
|
||||||
|
IsBackEnabled="{x:Bind ViewModel.IsBackEnabled, Mode=OneWay}"
|
||||||
|
SelectedItem="{x:Bind ViewModel.Selected, Mode=OneWay}"
|
||||||
|
IsSettingsVisible="True"
|
||||||
|
ExpandedModeThresholdWidth="1280"
|
||||||
|
DisplayModeChanged="NavigationViewControl_DisplayModeChanged"
|
||||||
|
Header="{x:Bind ((ContentControl)ViewModel.Selected).Content, Mode=OneWay}">
|
||||||
|
<NavigationView.MenuItems>
|
||||||
|
<!--
|
||||||
|
TODO: Update item titles by updating <x:Uid>.Content entries in Strings/en-us/Resources.resw.
|
||||||
|
https://docs.microsoft.com/windows/uwp/app-resources/localize-strings-ui-manifest#refer-to-a-string-resource-identifier-from-xaml
|
||||||
|
|
||||||
|
TODO: Update item icons by updating FontIcon.Glyph properties.
|
||||||
|
https://docs.microsoft.com/windows/apps/design/style/segoe-fluent-icons-font#icon-list
|
||||||
|
-->
|
||||||
|
<NavigationViewItem x:Uid="Shell_AppList" helpers:NavigationHelper.NavigateTo="GZStudio.ViewModels.AppListViewModel">
|
||||||
|
<NavigationViewItem.Icon>
|
||||||
|
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph=""/>
|
||||||
|
</NavigationViewItem.Icon>
|
||||||
|
</NavigationViewItem>
|
||||||
|
</NavigationView.MenuItems>
|
||||||
|
<NavigationView.HeaderTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Grid>
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding}"
|
||||||
|
Style="{ThemeResource TitleTextBlockStyle}" />
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</NavigationView.HeaderTemplate>
|
||||||
|
<i:Interaction.Behaviors>
|
||||||
|
<behaviors:NavigationViewHeaderBehavior
|
||||||
|
DefaultHeader="{x:Bind ((ContentControl)ViewModel.Selected).Content, Mode=OneWay}">
|
||||||
|
<behaviors:NavigationViewHeaderBehavior.DefaultHeaderTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Grid>
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding}"
|
||||||
|
Style="{ThemeResource TitleTextBlockStyle}" />
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</behaviors:NavigationViewHeaderBehavior.DefaultHeaderTemplate>
|
||||||
|
</behaviors:NavigationViewHeaderBehavior>
|
||||||
|
</i:Interaction.Behaviors>
|
||||||
|
<Grid Margin="{StaticResource NavigationViewPageContentMargin}">
|
||||||
|
<Frame x:Name="NavigationFrame" />
|
||||||
|
</Grid>
|
||||||
|
</NavigationView>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
using GZStudio.Contracts.Services;
|
||||||
|
using GZStudio.Helpers;
|
||||||
|
using GZStudio.ViewModels;
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Input;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
|
||||||
|
using Windows.System;
|
||||||
|
|
||||||
|
namespace GZStudio.Views;
|
||||||
|
|
||||||
|
// TODO: Update NavigationViewItem titles and icons in ShellPage.xaml.
|
||||||
|
public sealed partial class ShellPage : Page
|
||||||
|
{
|
||||||
|
public ShellViewModel ViewModel
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShellPage(ShellViewModel viewModel)
|
||||||
|
{
|
||||||
|
ViewModel = viewModel;
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
ViewModel.NavigationService.Frame = NavigationFrame;
|
||||||
|
ViewModel.NavigationViewService.Initialize(NavigationViewControl);
|
||||||
|
|
||||||
|
// TODO: Set the title bar icon by updating /Assets/WindowIcon.ico.
|
||||||
|
// A custom title bar is required for full window theme and Mica support.
|
||||||
|
// https://docs.microsoft.com/windows/apps/develop/title-bar?tabs=winui3#full-customization
|
||||||
|
App.MainWindow.ExtendsContentIntoTitleBar = true;
|
||||||
|
App.MainWindow.SetTitleBar(AppTitleBar);
|
||||||
|
App.MainWindow.Activated += MainWindow_Activated;
|
||||||
|
AppTitleBarText.Text = "AppDisplayName".GetLocalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLoaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
TitleBarHelper.UpdateTitleBar(RequestedTheme);
|
||||||
|
|
||||||
|
KeyboardAccelerators.Add(BuildKeyboardAccelerator(VirtualKey.Left, VirtualKeyModifiers.Menu));
|
||||||
|
KeyboardAccelerators.Add(BuildKeyboardAccelerator(VirtualKey.GoBack));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
|
||||||
|
{
|
||||||
|
App.AppTitlebar = AppTitleBarText as UIElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NavigationViewControl_DisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)
|
||||||
|
{
|
||||||
|
AppTitleBar.Margin = new Thickness()
|
||||||
|
{
|
||||||
|
Left = sender.CompactPaneLength * (sender.DisplayMode == NavigationViewDisplayMode.Minimal ? 2 : 1),
|
||||||
|
Top = AppTitleBar.Margin.Top,
|
||||||
|
Right = AppTitleBar.Margin.Right,
|
||||||
|
Bottom = AppTitleBar.Margin.Bottom
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static KeyboardAccelerator BuildKeyboardAccelerator(VirtualKey key, VirtualKeyModifiers? modifiers = null)
|
||||||
|
{
|
||||||
|
var keyboardAccelerator = new KeyboardAccelerator() { Key = key };
|
||||||
|
|
||||||
|
if (modifiers.HasValue)
|
||||||
|
{
|
||||||
|
keyboardAccelerator.Modifiers = modifiers.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
keyboardAccelerator.Invoked += OnKeyboardAcceleratorInvoked;
|
||||||
|
|
||||||
|
return keyboardAccelerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnKeyboardAcceleratorInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
|
||||||
|
{
|
||||||
|
var navigationService = App.GetService<INavigationService>();
|
||||||
|
|
||||||
|
var result = navigationService.GoBack();
|
||||||
|
|
||||||
|
args.Handled = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="GZStudio.app"/>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings>
|
||||||
|
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
|
||||||
|
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
</assembly>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"LocalSettingsOptions": {
|
||||||
|
"ApplicationDataFolder": "GZStudio/ApplicationData",
|
||||||
|
"LocalSettingsFile": "LocalSettings.json"
|
||||||
|
}
|
||||||
|
}
|
||||||