create a setup builder impl from MicaSetup

This commit is contained in:
ema 2023-12-04 01:36:02 +08:00
parent edc597a19a
commit a6891e29dc
465 changed files with 34946 additions and 75 deletions

View File

@ -17,44 +17,100 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fischless.HotkeyCapture", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fischless.KeyboardCapture", "Fischless.KeyboardCapture\Fischless.KeyboardCapture.csproj", "{10A48327-7E58-4B51-B1FC-55506C703C8F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Deploy", "Deploy", "{458E1106-43A4-47E6-B11B-D243035D4C76}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicaSetup", "Build\MicaSetup\MicaSetup.csproj", "{AB85DA23-EB8F-4FBF-A7FA-35CE05B23C15}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicaSetup.Uninst", "Build\MicaSetup\MicaSetup.Uninst.csproj", "{673344BC-B860-44AE-AD88-D33465BDE25B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Debug|Any CPU.ActiveCfg = Debug|x64
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Debug|Any CPU.Build.0 = Debug|x64
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Debug|x64.ActiveCfg = Debug|x64
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Debug|x64.Build.0 = Debug|x64
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Release|Any CPU.ActiveCfg = Release|x64
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Release|Any CPU.Build.0 = Release|x64
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Release|x64.ActiveCfg = Release|x64
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Release|x64.Build.0 = Release|x64
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Debug|Any CPU.ActiveCfg = Debug|x64
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Debug|Any CPU.Build.0 = Debug|x64
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Debug|x64.ActiveCfg = Debug|x64
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Debug|x64.Build.0 = Debug|x64
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Release|Any CPU.ActiveCfg = Release|x64
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Release|Any CPU.Build.0 = Release|x64
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Release|x64.ActiveCfg = Release|x64
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Release|x64.Build.0 = Release|x64
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Debug|Any CPU.ActiveCfg = Debug|x64
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Debug|Any CPU.Build.0 = Debug|x64
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Debug|x64.ActiveCfg = Debug|x64
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Debug|x64.Build.0 = Debug|x64
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Release|Any CPU.ActiveCfg = Release|x64
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Release|Any CPU.Build.0 = Release|x64
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Release|x64.ActiveCfg = Release|x64
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Release|x64.Build.0 = Release|x64
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Debug|Any CPU.ActiveCfg = Debug|x64
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Debug|Any CPU.Build.0 = Debug|x64
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Debug|x64.ActiveCfg = Debug|x64
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Debug|x64.Build.0 = Debug|x64
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Release|Any CPU.ActiveCfg = Release|x64
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Release|Any CPU.Build.0 = Release|x64
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Release|x64.ActiveCfg = Release|x64
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Release|x64.Build.0 = Release|x64
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Debug|Any CPU.ActiveCfg = Debug|x64
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Debug|Any CPU.Build.0 = Debug|x64
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Debug|x64.ActiveCfg = Debug|x64
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Debug|x64.Build.0 = Debug|x64
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Release|Any CPU.ActiveCfg = Release|x64
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Release|Any CPU.Build.0 = Release|x64
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Release|x64.ActiveCfg = Release|x64
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Release|x64.Build.0 = Release|x64
{08152E44-2564-46C5-B5B2-54DD43C01A79}.Debug|Any CPU.ActiveCfg = Debug|x64
{08152E44-2564-46C5-B5B2-54DD43C01A79}.Debug|Any CPU.Build.0 = Debug|x64
{08152E44-2564-46C5-B5B2-54DD43C01A79}.Debug|x64.ActiveCfg = Debug|x64
{08152E44-2564-46C5-B5B2-54DD43C01A79}.Debug|x64.Build.0 = Debug|x64
{08152E44-2564-46C5-B5B2-54DD43C01A79}.Release|Any CPU.ActiveCfg = Release|x64
{08152E44-2564-46C5-B5B2-54DD43C01A79}.Release|Any CPU.Build.0 = Release|x64
{08152E44-2564-46C5-B5B2-54DD43C01A79}.Release|x64.ActiveCfg = Release|x64
{08152E44-2564-46C5-B5B2-54DD43C01A79}.Release|x64.Build.0 = Release|x64
{10A48327-7E58-4B51-B1FC-55506C703C8F}.Debug|Any CPU.ActiveCfg = Debug|x64
{10A48327-7E58-4B51-B1FC-55506C703C8F}.Debug|Any CPU.Build.0 = Debug|x64
{10A48327-7E58-4B51-B1FC-55506C703C8F}.Debug|x64.ActiveCfg = Debug|x64
{10A48327-7E58-4B51-B1FC-55506C703C8F}.Debug|x64.Build.0 = Debug|x64
{10A48327-7E58-4B51-B1FC-55506C703C8F}.Release|Any CPU.ActiveCfg = Release|x64
{10A48327-7E58-4B51-B1FC-55506C703C8F}.Release|Any CPU.Build.0 = Release|x64
{10A48327-7E58-4B51-B1FC-55506C703C8F}.Release|x64.ActiveCfg = Release|x64
{10A48327-7E58-4B51-B1FC-55506C703C8F}.Release|x64.Build.0 = Release|x64
{AB85DA23-EB8F-4FBF-A7FA-35CE05B23C15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AB85DA23-EB8F-4FBF-A7FA-35CE05B23C15}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AB85DA23-EB8F-4FBF-A7FA-35CE05B23C15}.Debug|x64.ActiveCfg = Debug|x64
{AB85DA23-EB8F-4FBF-A7FA-35CE05B23C15}.Debug|x64.Build.0 = Debug|x64
{AB85DA23-EB8F-4FBF-A7FA-35CE05B23C15}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AB85DA23-EB8F-4FBF-A7FA-35CE05B23C15}.Release|Any CPU.Build.0 = Release|Any CPU
{AB85DA23-EB8F-4FBF-A7FA-35CE05B23C15}.Release|x64.ActiveCfg = Release|x64
{AB85DA23-EB8F-4FBF-A7FA-35CE05B23C15}.Release|x64.Build.0 = Release|x64
{673344BC-B860-44AE-AD88-D33465BDE25B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{673344BC-B860-44AE-AD88-D33465BDE25B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{673344BC-B860-44AE-AD88-D33465BDE25B}.Debug|x64.ActiveCfg = Debug|x64
{673344BC-B860-44AE-AD88-D33465BDE25B}.Debug|x64.Build.0 = Debug|x64
{673344BC-B860-44AE-AD88-D33465BDE25B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{673344BC-B860-44AE-AD88-D33465BDE25B}.Release|Any CPU.Build.0 = Release|Any CPU
{673344BC-B860-44AE-AD88-D33465BDE25B}.Release|x64.ActiveCfg = Release|x64
{673344BC-B860-44AE-AD88-D33465BDE25B}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{AB85DA23-EB8F-4FBF-A7FA-35CE05B23C15} = {458E1106-43A4-47E6-B11B-D243035D4C76}
{673344BC-B860-44AE-AD88-D33465BDE25B} = {458E1106-43A4-47E6-B11B-D243035D4C76}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {352D8B78-9DE3-4E58-985F-FADD22594DB4}
EndGlobalSection

View File

@ -1,75 +0,0 @@
<?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="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC 清单选项
如果想要更改 Windows 用户帐户控制级别,请使用
以下节点之一替换 requestedExecutionLevel 节点。n
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
如果你的应用程序需要此虚拟化来实现向后兼容性,则删除此
元素。
-->
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
Windows 版本的列表。取消评论适当的元素,
Windows 将自动选择最兼容的环境。 -->
<!-- Windows Vista -->
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
<!-- Windows 7 -->
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
<!-- Windows 8 -->
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
<!-- Windows 8.1 -->
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
<!-- Windows 10 -->
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
</application>
</compatibility>
<!-- 指示该应用程序可以感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI无需
选择加入。选择加入此设置的 Windows 窗体应用程序(目标设定为 .NET Framework 4.6 )还应
在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。-->
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
<!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->
</assembly>

5
Build/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.vs/
dist/
/*.exe
/*.7z
/*.zip

21
Build/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Lemutec Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Binary file not shown.

Binary file not shown.

4
Build/MicaSetup/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
bin/
obj/
*.user

21
Build/MicaSetup/App.xaml Normal file
View File

@ -0,0 +1,21 @@
<Application
x:Class="MicaSetup.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:control="clr-namespace:MicaSetup.Design.Controls"
xmlns:design="clr-namespace:MicaSetup.Design"
xmlns:local="clr-namespace:MicaSetup"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Resources/Languages/zh.xaml" />
<ResourceDictionary Source="/Resources/Languages/ja.xaml" />
<ResourceDictionary Source="/Resources/Languages/en.xaml" />
<design:ResourceResourceDictionary />
<design:GenericResourceDictionary />
<design:ThemeResourceDictionary />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@ -0,0 +1,17 @@
using MicaSetup.Helper;
using System.Windows;
namespace MicaSetup;
public partial class App : Application, IApp
{
public App()
{
InitializeComponent();
}
}
public interface IApp
{
public int Run();
}

View File

@ -0,0 +1,16 @@
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Media;
[assembly: DisableDpiAwareness]
[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
[assembly: ObfuscateAssembly(true)]
[assembly: ComVisible(false)]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: XmlnsPrefix("urn:MicaSetup", "mica")]
[assembly: XmlnsDefinition("urn:MicaSetup", "MicaSetup.Design.Controls")]
[assembly: XmlnsDefinition("urn:MicaSetup", "MicaSetup.Design.Converters")]

View File

@ -0,0 +1,11 @@
using System.ComponentModel;
namespace System.Runtime.CompilerServices;
/// <summary>
/// Workaround class for records and "init" keyword
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
internal class IsExternalInit
{
}

View File

@ -0,0 +1,104 @@
using MicaSetup.Helper;
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using DebugOut = System.Diagnostics.Debug;
namespace MicaSetup.Core;
public static class Logger
{
private static readonly string ApplicationLogPath = SpecialPathHelper.GetFolder();
private static readonly TextWriterTraceListener TraceListener = null!;
static Logger()
{
if (!Directory.Exists(ApplicationLogPath))
{
_ = Directory.CreateDirectory(ApplicationLogPath);
}
string logFilePath = Path.Combine(ApplicationLogPath, DateTime.Now.ToString(@"yyyyMMdd", CultureInfo.InvariantCulture) + ".log");
TraceListener = new TextWriterTraceListener(logFilePath);
#if LEGACY
Trace.AutoFlush = true;
Trace.Listeners.Clear();
Trace.Listeners.Add(TraceListener);
#endif
}
[SuppressMessage("Style", "IDE0060:")]
public static void Ignore(params object[] values)
{
}
[Conditional("DEBUG")]
public static void Debug(params object[] values)
{
Log("DEBUG", string.Join(" ", values));
}
public static void Info(params object[] values)
{
Log("INFO", string.Join(" ", values));
}
public static void Warn(params object[] values)
{
Log("ERROR", string.Join(" ", values));
}
public static void Error(params object[] values)
{
Log("ERROR", string.Join(" ", values));
}
public static void Fatal(params object[] values)
{
Log("FATAL", string.Join(" ", values));
}
public static void Exception(Exception e, string message = null!)
{
Log(
(message ?? string.Empty) + Environment.NewLine +
e?.Message + Environment.NewLine +
"Inner exception: " + Environment.NewLine +
e?.InnerException?.Message + Environment.NewLine +
"Stack trace: " + Environment.NewLine +
e?.StackTrace,
"ERROR");
#if DEBUG
Debugger.Break();
#endif
}
private static void Log(string type, string message)
{
StringBuilder sb = new();
sb.Append(type + "|" + DateTime.Now.ToString(@"yyyy-MM-dd|HH:mm:ss.fff", CultureInfo.InvariantCulture))
.Append("|" + GetCallerInfo())
.Append("|" + message);
DebugOut.WriteLine(sb.ToString());
if (Option.Current.Logging)
{
TraceListener.WriteLine(sb.ToString());
TraceListener.Flush();
}
}
private static string GetCallerInfo()
{
StackTrace stackTrace = new();
MethodBase methodName = stackTrace.GetFrame(3)?.GetMethod()!;
string? className = methodName?.DeclaringType?.Name;
return className + "|" + methodName?.Name;
}
}

View File

@ -0,0 +1,183 @@
using MicaSetup.Helper;
using MicaSetup.Services;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Baml2006;
using System.Xaml;
namespace MicaSetup.Core;
public static class MuiLanguage
{
/// <summary>
/// https://learn.microsoft.com/en-us/typography/fonts/windows_11_font_list
/// https://learn.microsoft.com/en-us/typography/fonts/windows_10_font_list
/// https://learn.microsoft.com/en-us/typography/fonts/windows_81_font_list
/// https://learn.microsoft.com/en-us/typography/fonts/windows_8_font_list
/// https://learn.microsoft.com/en-us/typography/fonts/windows_7_font_list
/// </summary>
public static List<MuiLanguageFont> FontSelector { get; } = new();
public static void SetupLanguage()
{
_ = SetLanguage();
}
public static bool SetLanguage() => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName switch
{
"zh" => SetLanguage("zh"),
"ja" => SetLanguage("ja"),
"en" or _ => SetLanguage("en"),
};
public static bool SetLanguage(string name = "en")
{
if (Application.Current == null)
{
return false;
}
try
{
foreach (ResourceDictionary dictionary in Application.Current.Resources.MergedDictionaries)
{
if (dictionary.Source != null && dictionary.Source.OriginalString.Equals($"/Resources/Languages/{name}.xaml", StringComparison.Ordinal))
{
Application.Current.Resources.MergedDictionaries.Remove(dictionary);
Application.Current.Resources.MergedDictionaries.Add(dictionary);
return true;
}
}
}
catch (Exception e)
{
_ = e;
}
return false;
}
public static string Mui(string key)
{
try
{
if (Application.Current == null)
{
return MuiBaml(key);
}
if (Application.Current!.FindResource(key) is string value)
{
return value;
}
}
catch (Exception e)
{
_ = e;
}
return null!;
}
public static string Mui(string key, params object[] args)
{
return string.Format(Mui(key)?.ToString(), args);
}
private static string MuiBaml(string key)
{
try
{
using Stream resourceXaml = ResourceHelper.GetStream(new MuiLanguageService().GetXamlUriString());
if (BamlHelper.LoadBaml(resourceXaml) is ResourceDictionary resourceDictionary)
{
return (resourceDictionary[key] as string)!;
}
}
catch (Exception e)
{
_ = e;
}
return null!;
}
}
file static class BamlHelper
{
public static object LoadBaml(Stream stream)
{
using Baml2006Reader reader = new(stream);
using XamlObjectWriter writer = new(reader.SchemaContext);
while (reader.Read())
{
writer.WriteNode(reader);
}
return writer.Result;
}
}
public class MuiLanguageFont
{
public string? Name { get; set; }
public string? TwoName { get; set; }
public string? ThreeName { get; set; }
public string? ResourceFontFileName { get; set; }
public string? ResourceFontFamilyName { get; set; }
public string? ResourceFamilyName => !string.IsNullOrWhiteSpace(ResourceFontFileName) && !string.IsNullOrWhiteSpace(ResourceFontFamilyName) ? $"./{ResourceFontFileName}#{ResourceFontFamilyName}" : null!;
public string? SystemFamilyName { get; set; }
public string? SystemFamilyNameBackup { get; set; }
}
public static class MuiLanguageFontExtension
{
public static MuiLanguageFont OnNameOf(this MuiLanguageFont self, string name)
{
self.Name = name ?? throw new ArgumentNullException(nameof(name));
self.TwoName = null!;
self.ThreeName = null!;
return self;
}
public static MuiLanguageFont OnTwoNameOf(this MuiLanguageFont self, string twoName)
{
self.Name = null!;
self.TwoName = twoName ?? throw new ArgumentNullException(nameof(twoName));
self.ThreeName = null!;
return self;
}
public static MuiLanguageFont OnThreeNameOf(this MuiLanguageFont self, string threeName)
{
self.Name = null!;
self.TwoName = null!;
self.ThreeName = threeName ?? throw new ArgumentNullException(nameof(threeName));
return self;
}
public static MuiLanguageFont OnAnyName(this MuiLanguageFont self)
{
self.Name = null!;
self.TwoName = null!;
self.ThreeName = null!;
return self;
}
public static MuiLanguageFont ForResourceFont(this MuiLanguageFont self, string fontFileName, string familyName)
{
self.ResourceFontFileName = fontFileName ?? throw new ArgumentNullException(nameof(fontFileName));
self.ResourceFontFamilyName = familyName ?? throw new ArgumentNullException(nameof(familyName));
return self;
}
public static MuiLanguageFont ForSystemFont(this MuiLanguageFont self, string familyName, string familyNameBackup = null!)
{
self.SystemFamilyName = familyName ?? throw new ArgumentNullException(nameof(familyName));
self.SystemFamilyNameBackup = familyNameBackup;
_ = !new Regex("^[a-zA-Z ]+$").IsMatch(familyName) ? throw new ArgumentException(nameof(familyName)) : default(object);
return self;
}
}

View File

@ -0,0 +1,15 @@
using Microsoft.Extensions.DependencyInjection;
namespace MicaSetup.Services;
#pragma warning disable CS8618
public class ServiceManager
{
public static ServiceProvider Services { get; set; }
public static T GetService<T>()
{
return Services.GetService<T>()!;
}
}

View File

@ -0,0 +1,372 @@
using System;
namespace MicaSetup.Controls.Animations;
public delegate double DoubleEasingAnimation(double t, double b, double c, double d);
public static class DoubleEasingAnimations
{
public static double EaseInOutQuad(double t, double b, double c, double d)
{
c -= b;
if ((t /= d / 2d) < 1d)
{
return c / 2d * t * t + b;
}
return -c / 2d * ((t -= 1d) * (t - 2d) - 1d) + b;
}
public static double EaseInQuad(double t, double b, double c, double d)
{
c -= b;
return c * (t /= d) * t + b;
}
public static double EaseOutQuad(double t, double b, double c, double d)
{
c -= b;
return -c * (t /= d) * (t - 2d) + b;
}
public static double EaseInCubic(double t, double b, double c, double d)
{
c -= b;
return c * (t /= d) * t * t + b;
}
public static double EaseOutCubic(double t, double b, double c, double d)
{
c -= b;
return c * ((t = t / d - 1d) * t * t + 1d) + b;
}
public static double EaseInOutCubic(double t, double b, double c, double d)
{
c -= b;
if ((t /= d / 2d) < 1d)
{
return c / 2d * t * t * t + b;
}
return c / 2d * ((t -= 2d) * t * t + 2d) + b;
}
public static double EaseInQuart(double t, double b, double c, double d)
{
c -= b;
return c * (t /= d) * t * t * t + b;
}
public static double EaseOutQuart(double t, double b, double c, double d)
{
c -= b;
return -c * ((t = t / d - 1d) * t * t * t - 1d) + b;
}
public static double EaseInOutQuart(double t, double b, double c, double d)
{
c -= b;
if ((t /= d / 2d) < 1d)
{
return c / 2d * t * t * t * t + b;
}
return -c / 2d * ((t -= 2d) * t * t * t - 2d) + b;
}
public static double EaseInQuint(double t, double b, double c, double d)
{
c -= b;
return c * (t /= d) * t * t * t * t + b;
}
public static double EaseOutQuint(double t, double b, double c, double d)
{
c -= b;
return c * ((t = t / d - 1d) * t * t * t * t + 1d) + b;
}
public static double EaseInOutQuint(double t, double b, double c, double d)
{
c -= b;
if ((t /= d / 2d) < 1d)
{
return c / 2d * t * t * t * t * t + b;
}
return c / 2d * ((t -= 2d) * t * t * t * t + 2d) + b;
}
public static double EaseInSine(double t, double b, double c, double d)
{
c -= b;
return -c * Math.Cos(t / d * 1.5707963267948966d) + c + b;
}
public static double EaseOutSine(double t, double b, double c, double d)
{
c -= b;
return c * Math.Sin(t / d * 1.5707963267948966d) + b;
}
public static double EaseInOutSine(double t, double b, double c, double d)
{
c -= b;
return -c / 2d * (Math.Cos(3.141592653589793d * t / d) - 1d) + b;
}
public static double EaseInExpo(double t, double b, double c, double d)
{
c -= b;
if (t != 0d)
{
return c * Math.Pow(2d, 10d * (t / d - 1d)) + b;
}
return b;
}
public static double EaseOutExpo(double t, double b, double c, double d)
{
c -= b;
if (t != d)
{
return c * (-Math.Pow(2d, -10d * t / d) + 1d) + b;
}
return b + c;
}
public static double EaseInOutExpo(double t, double b, double c, double d)
{
c -= b;
if (t == 0d)
{
return b;
}
if (t == d)
{
return b + c;
}
if ((t /= d / 2d) < 1d)
{
return c / 2d * Math.Pow(2d, 10d * (t - 1d)) + b;
}
return c / 2d * (-Math.Pow(2d, -10d * (t -= 1d)) + 2d) + b;
}
public static double EaseInCirc(double t, double b, double c, double d)
{
c -= b;
return -c * (Math.Sqrt(1d - (t /= d) * t) - 1d) + b;
}
public static double EaseOutCirc(double t, double b, double c, double d)
{
c -= b;
return c * Math.Sqrt(1d - (t = t / d - 1d) * t) + b;
}
public static double EaseInOutCirc(double t, double b, double c, double d)
{
c -= b;
if ((t /= d / 2d) < 1d)
{
return -c / 2d * (Math.Sqrt(1d - t * t) - 1d) + b;
}
return c / 2d * (Math.Sqrt(1d - (t -= 2d) * t) + 1d) + b;
}
public static double EaseInElastic(double t, double b, double c, double d)
{
c -= b;
double num = 0d;
double num2 = c;
if (t == 0d)
{
return b;
}
if ((t /= d) == 1d)
{
return b + c;
}
if (num == 0d)
{
num = d * 0.3d;
}
double num3;
if (num2 < Math.Abs(c))
{
num2 = c;
num3 = num / 4d;
}
else
{
num3 = num / 6.283185307179586d * Math.Asin(c / num2);
}
return -(num2 * Math.Pow(2d, 10d * (t -= 1d)) * Math.Sin((t * d - num3) * 6.283185307179586d / num)) + b;
}
public static double EaseOutElastic(double t, double b, double c, double d)
{
c -= b;
double num = 0d;
double num2 = c;
if (t == 0d)
{
return b;
}
if ((t /= d) == 1d)
{
return b + c;
}
if (num == 0d)
{
num = d * 0.3d;
}
double num3;
if (num2 < Math.Abs(c))
{
num2 = c;
num3 = num / 4d;
}
else
{
num3 = num / 6.283185307179586d * Math.Asin(c / num2);
}
return num2 * Math.Pow(2d, -10d * t) * Math.Sin((t * d - num3) * 6.283185307179586d / num) + c + b;
}
public static double EaseInOutElastic(double t, double b, double c, double d)
{
c -= b;
double num = 0d;
double num2 = c;
if (t == 0d)
{
return b;
}
if ((t /= d / 2d) == 2d)
{
return b + c;
}
if (num == 0d)
{
num = d * 0.44999999999999996d;
}
double num3;
if (num2 < Math.Abs(c))
{
num2 = c;
num3 = num / 4d;
}
else
{
num3 = num / 6.283185307179586d * Math.Asin(c / num2);
}
if (t < 1d)
{
return -0.5d * (num2 * Math.Pow(2d, 10d * (t -= 1d)) * Math.Sin((t * d - num3) * 6.283185307179586d / num)) + b;
}
return num2 * Math.Pow(2d, -10d * (t -= 1d)) * Math.Sin((t * d - num3) * 6.283185307179586d / num) * 0.5d + c + b;
}
public static double EaseInBounce(double t, double b, double c, double d)
{
c -= b;
return c - EaseOutBounce(d - t, 0d, c, d) + b;
}
public static double EaseOutBounce(double t, double b, double c, double d)
{
c -= b;
if ((t /= d) < 0.36363636363636365d)
{
return c * (7.5625d * t * t) + b;
}
if (t < 0.7272727272727273d)
{
return c * (7.5625d * (t -= 0.5454545454545454d) * t + 0.75d) + b;
}
if (t < 0.9090909090909091d)
{
return c * (7.5625d * (t -= 0.8181818181818182d) * t + 0.9375d) + b;
}
return c * (7.5625d * (t -= 0.9545454545454546d) * t + 0.984375d) + b;
}
public static double EaseInOutBounce(double t, double b, double c, double d)
{
c -= b;
if (t < d / 2d)
{
return EaseInBounce(t * 2d, 0d, c, d) * 0.5d + b;
}
return EaseOutBounce(t * 2d - d, 0d, c, d) * 0.5d + c * 0.5d + b;
}
public static double Linear(double t, double b, double c, double d)
{
c -= b;
return t / d * c + b;
}
public static DoubleEasingAnimation[] Function { get; } = new DoubleEasingAnimation[]
{
EaseInOutQuad,
EaseInQuad,
EaseOutQuad,
EaseInCubic,
EaseOutCubic,
EaseInOutCubic,
EaseInQuart,
EaseOutQuart,
EaseInOutQuart,
EaseInQuint,
EaseOutQuint,
EaseInOutQuint,
EaseInSine,
EaseOutSine,
EaseInOutSine,
EaseInExpo,
EaseOutExpo,
EaseInOutExpo,
EaseInCirc,
EaseOutCirc,
EaseInOutCirc,
EaseInElastic,
EaseOutElastic,
EaseInOutElastic,
EaseInBounce,
EaseOutBounce,
EaseInOutBounce,
Linear,
};
}
public enum DoubleEasingAnimationType
{
InOutQuad,
InQuad,
OutQuad,
InCubic,
OutCubic,
InOutCubic,
InQuart,
OutQuart,
InOutQuart,
InQuint,
OutQuint,
InOutQuint,
InSine,
OutSine,
InOutSine,
InExpo,
OutExpo,
InOutExpo,
InCirc,
OutCirc,
InOutCirc,
InElastic,
OutElastic,
InOutElastic,
InBounce,
OutBounce,
InOutBounce,
Linear,
}

View File

@ -0,0 +1,88 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MicaSetup.Controls.Animations;
public partial class ProgressAccumulator : ObservableObject, IDisposable
{
public Action<double>? Handler;
public DoubleEasingAnimation? Animation;
[ObservableProperty]
private double duration = 3000d;
[ObservableProperty]
private double current = 0d;
[ObservableProperty]
private double from = 0d;
[ObservableProperty]
private double to = 100d;
private Task task = null!;
private bool isRunning = false;
private DateTime startTime = default;
private double durationTime = default;
public ProgressAccumulator(double from = 0d, double to = 100d, double duration = 3000d, Action<double> handler = null!, DoubleEasingAnimation anime = null!)
{
Reset(from, to, duration, handler);
Animation = anime;
}
public void Dispose()
{
Reset();
}
public ProgressAccumulator Start()
{
isRunning = true;
startTime = DateTime.Now;
durationTime = default;
task = Task.Run(Handle);
return this;
}
public ProgressAccumulator Stop()
{
isRunning = false;
return this;
}
public ProgressAccumulator Reset(double from = 0d, double to = 100d, double duration = 3000d, Action<double> handler = null!)
{
Stop();
Current = From = from;
To = to;
Duration = duration;
Handler = handler;
return this;
}
private void Handle()
{
while (isRunning)
{
if (!SpinWait.SpinUntil(() => !isRunning, 50))
{
Calc();
Handler?.Invoke(Current);
}
}
}
private double Calc()
{
if (durationTime <= Duration)
{
Current = (Animation ?? DoubleEasingAnimations.EaseOutCirc).Invoke(durationTime, From, To, Duration);
durationTime = DateTime.Now.Subtract(startTime).TotalMilliseconds;
}
return Current;
}
}

View File

@ -0,0 +1,151 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace MicaSetup.Design.Controls;
#pragma warning disable CS8618
public class BitmapIcon : IconElement
{
static BitmapIcon()
{
ForegroundProperty.OverrideMetadata(typeof(BitmapIcon), new FrameworkPropertyMetadata(OnForegroundChanged));
}
public BitmapIcon()
{
}
public static readonly DependencyProperty UriSourceProperty =
BitmapImage.UriSourceProperty.AddOwner(
typeof(BitmapIcon),
new FrameworkPropertyMetadata(OnUriSourceChanged));
public Uri UriSource
{
get => (Uri)GetValue(UriSourceProperty);
set => SetValue(UriSourceProperty, value);
}
private static void OnUriSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((BitmapIcon)d).ApplyUriSource();
}
public static readonly DependencyProperty ShowAsMonochromeProperty =
DependencyProperty.Register(
nameof(ShowAsMonochrome),
typeof(bool),
typeof(BitmapIcon),
new PropertyMetadata(true, OnShowAsMonochromeChanged));
public bool ShowAsMonochrome
{
get => (bool)GetValue(ShowAsMonochromeProperty);
set => SetValue(ShowAsMonochromeProperty, value);
}
private static void OnShowAsMonochromeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((BitmapIcon)d).ApplyShowAsMonochrome();
}
private protected override void InitializeChildren()
{
_image = new Image
{
Visibility = Visibility.Hidden
};
_opacityMask = new ImageBrush();
_foreground = new Rectangle
{
OpacityMask = _opacityMask
};
ApplyForeground();
ApplyUriSource();
Children.Add(_image);
ApplyShowAsMonochrome();
}
private protected override void OnShouldInheritForegroundFromVisualParentChanged()
{
ApplyForeground();
}
private protected override void OnVisualParentForegroundPropertyChanged(DependencyPropertyChangedEventArgs args)
{
if (ShouldInheritForegroundFromVisualParent)
{
ApplyForeground();
}
}
private static void OnForegroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((BitmapIcon)d).ApplyForeground();
}
private void ApplyForeground()
{
if (_foreground != null)
{
_foreground.Fill = ShouldInheritForegroundFromVisualParent ? VisualParentForeground : Foreground;
}
}
private void ApplyUriSource()
{
if (_image != null && _opacityMask != null)
{
var uriSource = UriSource;
if (uriSource != null)
{
var imageSource = new BitmapImage(uriSource);
_image.Source = imageSource;
_opacityMask.ImageSource = imageSource;
}
else
{
_image.ClearValue(Image.SourceProperty);
_opacityMask.ClearValue(ImageBrush.ImageSourceProperty);
}
}
}
private void ApplyShowAsMonochrome()
{
bool showAsMonochrome = ShowAsMonochrome;
if (_image != null)
{
_image.Visibility = showAsMonochrome ? Visibility.Hidden : Visibility.Visible;
}
if (_foreground != null)
{
if (showAsMonochrome)
{
if (!Children.Contains(_foreground))
{
Children.Add(_foreground);
}
}
else
{
Children.Remove(_foreground);
}
}
}
private Image _image;
private Rectangle _foreground;
private ImageBrush _opacityMask;
}

View File

@ -0,0 +1,172 @@
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
namespace MicaSetup.Design.Controls;
#pragma warning disable CS8618
public abstract class IconElement : FrameworkElement
{
private protected IconElement()
{
}
public static readonly DependencyProperty ForegroundProperty =
TextElement.ForegroundProperty.AddOwner(
typeof(IconElement),
new FrameworkPropertyMetadata(SystemColors.ControlTextBrush,
FrameworkPropertyMetadataOptions.Inherits,
OnForegroundPropertyChanged));
private static void OnForegroundPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
((IconElement)sender).OnForegroundPropertyChanged(args);
}
private void OnForegroundPropertyChanged(DependencyPropertyChangedEventArgs args)
{
var baseValueSource = DependencyPropertyHelper.GetValueSource(this, args.Property).BaseValueSource;
_isForegroundDefaultOrInherited = baseValueSource <= BaseValueSource.Inherited;
UpdateShouldInheritForegroundFromVisualParent();
}
[Bindable(true), Category("Appearance")]
public Brush Foreground
{
get { return (Brush)GetValue(ForegroundProperty); }
set { SetValue(ForegroundProperty, value); }
}
private static readonly DependencyProperty VisualParentForegroundProperty =
DependencyProperty.Register(
nameof(VisualParentForeground),
typeof(Brush),
typeof(IconElement),
new PropertyMetadata(null, OnVisualParentForegroundPropertyChanged));
protected Brush VisualParentForeground
{
get => (Brush)GetValue(VisualParentForegroundProperty);
set => SetValue(VisualParentForegroundProperty, value);
}
private static void OnVisualParentForegroundPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
((IconElement)sender).OnVisualParentForegroundPropertyChanged(args);
}
private protected virtual void OnVisualParentForegroundPropertyChanged(DependencyPropertyChangedEventArgs args)
{
}
protected bool ShouldInheritForegroundFromVisualParent
{
get => _shouldInheritForegroundFromVisualParent;
private set
{
if (_shouldInheritForegroundFromVisualParent != value)
{
_shouldInheritForegroundFromVisualParent = value;
if (_shouldInheritForegroundFromVisualParent)
{
SetBinding(VisualParentForegroundProperty,
new Binding
{
Path = new PropertyPath(TextElement.ForegroundProperty),
Source = VisualParent
});
}
else
{
ClearValue(VisualParentForegroundProperty);
}
OnShouldInheritForegroundFromVisualParentChanged();
}
}
}
private protected virtual void OnShouldInheritForegroundFromVisualParentChanged()
{
}
private void UpdateShouldInheritForegroundFromVisualParent()
{
ShouldInheritForegroundFromVisualParent =
_isForegroundDefaultOrInherited &&
Parent != null &&
VisualParent != null &&
Parent != VisualParent;
}
protected UIElementCollection Children
{
get
{
EnsureLayoutRoot();
return _layoutRoot.Children;
}
}
private protected abstract void InitializeChildren();
protected override int VisualChildrenCount => 1;
protected override Visual GetVisualChild(int index)
{
if (index == 0)
{
EnsureLayoutRoot();
return _layoutRoot;
}
else
{
throw new ArgumentOutOfRangeException(nameof(index));
}
}
protected override Size MeasureOverride(Size availableSize)
{
EnsureLayoutRoot();
_layoutRoot.Measure(availableSize);
return _layoutRoot.DesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
EnsureLayoutRoot();
_layoutRoot.Arrange(new Rect(new Point(), finalSize));
return finalSize;
}
protected override void OnVisualParentChanged(DependencyObject oldParent)
{
base.OnVisualParentChanged(oldParent);
UpdateShouldInheritForegroundFromVisualParent();
}
private void EnsureLayoutRoot()
{
if (_layoutRoot != null)
return;
_layoutRoot = new Grid
{
Background = Brushes.Transparent,
SnapsToDevicePixels = true,
};
InitializeChildren();
AddVisualChild(_layoutRoot);
}
private Grid _layoutRoot;
private bool _isForegroundDefaultOrInherited = true;
private bool _shouldInheritForegroundFromVisualParent;
}

View File

@ -0,0 +1,129 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MicaSetup.Design.Controls">
<Thickness x:Key="ButtonPadding">11,5,11,6</Thickness>
<Thickness x:Key="ButtonBorderThemeThickness">1</Thickness>
<Thickness x:Key="ButtonIconMargin">0,0,8,0</Thickness>
<Style x:Key="DefaultButtonStyle" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Background" Value="{DynamicResource ButtonBackground}" />
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource ControlElevationBorderBrush}" />
<Setter Property="BorderThickness" Value="{StaticResource ButtonBorderThemeThickness}" />
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="Border.CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="ContentBorder"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
MinWidth="{TemplateBinding MinWidth}"
MinHeight="{TemplateBinding MinHeight}"
Padding="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding Border.CornerRadius}">
<ContentPresenter x:Name="ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
TextElement.Foreground="{TemplateBinding Foreground}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ControlElevationBorderBrush}" />
<Setter TargetName="ContentPresenter" Property="TextElement.Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
<Setter Property="Opacity" Value="0.6" />
<Setter TargetName="ContentPresenter" Property="TextElement.Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPressed}" />
<Setter TargetName="ContentPresenter" Property="TextElement.Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="PrimaryButtonStyle" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Background" Value="{DynamicResource AccentButtonBackground}" />
<Setter Property="Foreground" Value="{DynamicResource AccentButtonForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource AccentControlElevationBorderBrush}" />
<Setter Property="BorderThickness" Value="{StaticResource ButtonBorderThemeThickness}" />
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="Border.CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="ContentBorder"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
MinWidth="{TemplateBinding MinWidth}"
MinHeight="{TemplateBinding MinHeight}"
Padding="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding Border.CornerRadius}">
<ContentPresenter x:Name="ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
TextElement.Foreground="{TemplateBinding Foreground}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource AccentControlElevationBorderBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.3" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Foreground" Value="{DynamicResource PressedForeground}" />
<Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPressed}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style BasedOn="{StaticResource DefaultButtonStyle}" TargetType="{x:Type Button}" />
</ResourceDictionary>

View File

@ -0,0 +1,75 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:design="clr-namespace:MicaSetup.Design"
xmlns:local="clr-namespace:MicaSetup.Design.Controls">
<ResourceDictionary.MergedDictionaries>
<design:ResourceResourceDictionary />
</ResourceDictionary.MergedDictionaries>
<Style x:Key="DefaultCheckBoxStyle"
BasedOn="{StaticResource {x:Type CheckBox}}"
TargetType="{x:Type CheckBox}">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Padding" Value="4,0,0,0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<StackPanel Orientation="Horizontal">
<Grid Width="20"
Height="20"
Background="Transparent">
<Border x:Name="border"
Width="20"
Height="20"
Margin="0,0,0,0"
BorderBrush="#8E8E93"
BorderThickness="1"
CornerRadius="2" />
<TextBlock x:Name="checkIcon"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="{StaticResource IcoMoon}"
FontSize="18"
Foreground="{DynamicResource HighGreenBrush}"
Opacity="0"
Text="{x:Static local:Selection.PublicOk}" />
</Grid>
<ContentPresenter Margin="5,0,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<BeginStoryboard.Storyboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="checkIcon"
Storyboard.TargetProperty="(UIElement.Opacity)"
To="1"
Duration="0:0:0.05" />
</Storyboard>
</BeginStoryboard.Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<BeginStoryboard.Storyboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="checkIcon"
Storyboard.TargetProperty="(UIElement.Opacity)"
To="0"
Duration="0:0:0.05" />
</Storyboard>
</BeginStoryboard.Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style BasedOn="{StaticResource DefaultCheckBoxStyle}" TargetType="{x:Type CheckBox}" />
</ResourceDictionary>

View File

@ -0,0 +1,144 @@
<Window x:Class="MicaSetup.Design.Controls.MessageBoxDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:conv="clr-namespace:MicaSetup.Design.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:design="clr-namespace:MicaSetup.Design"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:MicaSetup.Design.Controls"
xmlns:markups="clr-namespace:MicaSetup.Design.Markups"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="Self"
Title="{DynamicResource TitleTips}"
Width="400"
Height="250"
d:Height="250"
d:Opacity="1"
d:Width="300"
AllowsTransparency="True"
Background="Transparent"
FontFamily="{markups:LocalizedFontFamily}"
Opacity="0"
RenderOptions.BitmapScalingMode="Fant"
ResizeMode="NoResize"
WindowStartupLocation="CenterOwner"
WindowStyle="None"
mc:Ignorable="d">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<design:GenericResourceDictionary />
<design:ResourceResourceDictionary />
</ResourceDictionary.MergedDictionaries>
<conv:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
</ResourceDictionary>
</Window.Resources>
<Window.Style>
<Style TargetType="{x:Type Window}">
<Style.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation BeginTime="0:0:0.0"
Storyboard.TargetProperty="Opacity"
From="0"
To="1"
Duration="0:0:0.15">
<DoubleAnimation.EasingFunction>
<SineEase EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Window.Style>
<Grid>
<i:Interaction.Behaviors>
<local:WindowDragMoveBehavior />
</i:Interaction.Behaviors>
<Border Margin="5" CornerRadius="3">
<Border.Background>
<SolidColorBrush Color="{DynamicResource ApplicationBackgroundColor}" />
</Border.Background>
<Border.Effect>
<DropShadowEffect BlurRadius="5"
ShadowDepth="0"
Color="LightGray" />
</Border.Effect>
</Border>
<Border Margin="5" CornerRadius="3">
<Grid Margin="5">
<TextBlock Margin="15,5"
FontSize="15"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
Text="{DynamicResource TitleTips}" />
<StackPanel Margin="15,0,0,0" Orientation="Horizontal">
<Grid>
<TextBlock VerticalAlignment="Center"
FontFamily="{StaticResource IcoMoon}"
FontSize="30"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
Text="{x:Static local:Selection.PublicFail}" />
</Grid>
<StackPanel Margin="5,5,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock Width="300"
FontSize="16"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
LineHeight="25"
Text="{Binding Message}"
TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
<StackPanel Margin="0,0,15,8"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button Height="30"
MinWidth="65"
Padding="10,0,10,0"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Command="{Binding OkayCommand}"
FontSize="15"
Style="{StaticResource PrimaryButtonStyle}"
Visibility="{Binding OkayVisiable, Converter={StaticResource BoolToVisibilityConverter}}">
<Button.Content>
<TextBlock Text="{DynamicResource ButtonOK}" />
</Button.Content>
</Button>
<Button Height="30"
MinWidth="65"
Margin="8,0,0,0"
Padding="10,0,10,0"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Command="{Binding YesCommand}"
FontSize="15"
Style="{StaticResource PrimaryButtonStyle}"
Visibility="{Binding YesVisiable, Converter={StaticResource BoolToVisibilityConverter}}">
<Button.Content>
<TextBlock Text="{DynamicResource ButtonYes}" />
</Button.Content>
</Button>
<Button Height="30"
MinWidth="65"
Margin="8,0,0,0"
Padding="10,0,10,0"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Command="{Binding NoCommand}"
FontSize="15"
Style="{StaticResource PrimaryButtonStyle}"
Visibility="{Binding NoVisiable, Converter={StaticResource BoolToVisibilityConverter}}">
<Button.Content>
<TextBlock Text="{DynamicResource ButtonNo}" />
</Button.Content>
</Button>
</StackPanel>
</Grid>
</Border>
</Grid>
</Window>

View File

@ -0,0 +1,109 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Windows;
namespace MicaSetup.Design.Controls;
[INotifyPropertyChanged]
public partial class MessageBoxDialog : Window
{
[ObservableProperty]
private string message = null!;
[ObservableProperty]
protected bool okayVisiable = true;
[ObservableProperty]
protected bool yesVisiable = false;
[ObservableProperty]
protected bool noVisiable = false;
[ObservableProperty]
protected WindowDialogResult result = WindowDialogResult.None;
partial void OnResultChanged(WindowDialogResult value)
{
_ = value;
Close();
}
[ObservableProperty]
private string iconString = "\xe915";
[ObservableProperty]
private MessageBoxType type = MessageBoxType.Info;
partial void OnTypeChanged(MessageBoxType value)
{
IconString = value switch
{
MessageBoxType.Question => "\xe918",
MessageBoxType.Info or _ => "\xe915",
};
OkayVisiable = value switch
{
MessageBoxType.Question => false,
MessageBoxType.Info or _ => true,
};
YesVisiable = value switch
{
MessageBoxType.Question => true,
MessageBoxType.Info or _ => false,
};
NoVisiable = value switch
{
MessageBoxType.Question => true,
MessageBoxType.Info or _ => false,
};
}
public MessageBoxDialog()
{
DataContext = this;
InitializeComponent();
}
[RelayCommand]
private void Okay()
{
Result = WindowDialogResult.OK;
}
[RelayCommand]
private void Yes()
{
Result = WindowDialogResult.Yes;
}
[RelayCommand]
private void No()
{
Result = WindowDialogResult.No;
}
public WindowDialogResult ShowDialog(Window owner)
{
Owner = owner;
ShowDialog();
return Result;
}
}
public enum MessageBoxType
{
Info,
Question,
}
public enum WindowDialogResult
{
None = 0,
OK = 1,
Cancel = 2,
Yes = 6,
No = 7
}

View File

@ -0,0 +1,29 @@
using MicaSetup.Helper;
using System.Windows;
namespace MicaSetup.Design.Controls;
public static class MessageBoxX
{
public static WindowDialogResult Info(DependencyObject dependencyObject, string message)
{
Window owner = (dependencyObject is Window win ? win : dependencyObject == null ? UIDispatcherHelper.MainWindow : Window.GetWindow(dependencyObject)) ?? UIDispatcherHelper.MainWindow;
return new MessageBoxDialog()
{
Type = MessageBoxType.Info,
Message = message,
}.ShowDialog(owner);
}
public static WindowDialogResult Question(DependencyObject dependencyObject, string message)
{
Window owner = (dependencyObject is Window win ? win : dependencyObject == null ? UIDispatcherHelper.MainWindow : Window.GetWindow(dependencyObject)) ?? UIDispatcherHelper.MainWindow;
return new MessageBoxDialog()
{
Type = MessageBoxType.Question,
Message = message,
}.ShowDialog(owner);
}
}

View File

@ -0,0 +1,102 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:design="clr-namespace:MicaSetup.Design"
xmlns:local="clr-namespace:MicaSetup.Design.Controls">
<ResourceDictionary.MergedDictionaries>
<design:ResourceResourceDictionary />
</ResourceDictionary.MergedDictionaries>
<Color x:Key="ProgressBarForeground1">#73EBF3</Color>
<Color x:Key="ProgressBarForeground2">#238EFA</Color>
<Style
x:Key="DefaultProgressBarStyle"
BasedOn="{StaticResource {x:Type ProgressBar}}"
TargetType="{x:Type ProgressBar}">
<Setter Property="Foreground" Value="#2196F3" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Height" Value="4" />
<Setter Property="Minimum" Value="0" />
<Setter Property="Maximum" Value="100" />
<Setter Property="local:ProgressBarHelper.Foreground1" Value="{StaticResource ProgressBarForeground1}" />
<Setter Property="local:ProgressBarHelper.Foreground2" Value="{StaticResource ProgressBarForeground2}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="1">
<Grid x:Name="PART_Track">
<Rectangle
x:Name="PART_Indicator"
Height="{TemplateBinding Height}"
HorizontalAlignment="Left"
RadiusX="1"
RadiusY="1">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStopCollection>
<GradientStop Offset="0" Color="{Binding Path=(local:ProgressBarHelper.Foreground1), RelativeSource={RelativeSource AncestorType=ProgressBar}, Mode=OneWay, FallbackValue={StaticResource ProgressBarForeground1}}" />
<GradientStop Offset="1" Color="{Binding Path=(local:ProgressBarHelper.Foreground2), RelativeSource={RelativeSource AncestorType=ProgressBar}, Mode=OneWay, FallbackValue={StaticResource ProgressBarForeground2}}" />
</GradientStopCollection>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle
x:Name="PART_IndeterminateIndicator"
Width="100"
Height="{TemplateBinding Height}"
HorizontalAlignment="Left"
RadiusX="1"
RadiusY="1">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStopCollection>
<GradientStop Offset="0" Color="{Binding Path=(local:ProgressBarHelper.Foreground1), RelativeSource={RelativeSource AncestorType=ProgressBar}, Mode=OneWay, FallbackValue={StaticResource ProgressBarForeground1}}" />
<GradientStop Offset="1" Color="{Binding Path=(local:ProgressBarHelper.Foreground2), RelativeSource={RelativeSource AncestorType=ProgressBar}, Mode=OneWay, FallbackValue={StaticResource ProgressBarForeground2}}" />
</GradientStopCollection>
</LinearGradientBrush>
</Rectangle.Fill>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<ThicknessAnimation
BeginTime="0:0:0.5"
Storyboard.TargetName="PART_IndeterminateIndicator"
Storyboard.TargetProperty="Margin"
From="0,0,0,0"
To="450,0,0,0"
Duration="0:0:2" />
<DoubleAnimation
Storyboard.TargetName="PART_IndeterminateIndicator"
Storyboard.TargetProperty="Width"
From="0"
To="100"
Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsIndeterminate" Value="True">
<Setter TargetName="PART_Indicator" Property="Visibility" Value="Collapsed" />
<Setter TargetName="PART_IndeterminateIndicator" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="IsIndeterminate" Value="False">
<Setter TargetName="PART_Indicator" Property="Visibility" Value="Visible" />
<Setter TargetName="PART_IndeterminateIndicator" Property="Visibility" Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style BasedOn="{StaticResource DefaultProgressBarStyle}" TargetType="{x:Type ProgressBar}" />
</ResourceDictionary>

View File

@ -0,0 +1,31 @@
using System.Windows;
using System.Windows.Media;
namespace MicaSetup.Design.Controls;
public class ProgressBarHelper
{
public static Color GetForeground1(DependencyObject obj)
{
return (Color)obj.GetValue(Foreground1Property);
}
public static void SetForeground1(DependencyObject obj, Color value)
{
obj.SetValue(Foreground1Property, value);
}
public static readonly DependencyProperty Foreground1Property = DependencyProperty.RegisterAttached("Foreground1", typeof(Color), typeof(ProgressBarHelper), new((Color)ColorConverter.ConvertFromString("#73EBF3")));
public static Color GetForeground2(DependencyObject obj)
{
return (Color)obj.GetValue(Foreground2Property);
}
public static void SetForeground2(DependencyObject obj, Color value)
{
obj.SetValue(Foreground2Property, value);
}
public static readonly DependencyProperty Foreground2Property = DependencyProperty.RegisterAttached("Foreground2", typeof(Color), typeof(ProgressBarHelper), new((Color)ColorConverter.ConvertFromString("#238EFA")));
}

View File

@ -0,0 +1,51 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shell;
namespace MicaSetup.Design.Controls;
public class SetupProgressBar : ProgressBar
{
public bool SyncToWindowTaskbar
{
get => (bool)GetValue(LinkToWindowTaskbarProperty);
set => SetValue(LinkToWindowTaskbarProperty, value);
}
public static readonly DependencyProperty LinkToWindowTaskbarProperty = DependencyProperty.Register("SyncToWindowTaskbar", typeof(bool), typeof(SetupProgressBar), new PropertyMetadata(true));
public SetupProgressBar()
{
ValueChanged += OnValueChanged;
}
private void OnValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!SyncToWindowTaskbar)
{
return;
}
if (this.FindTaskbar() is TaskbarItemInfo taskbar)
{
if (Value >= 0d)
{
taskbar.ProgressValue = Value / 100d;
if (Value >= 100d)
{
taskbar.ProgressState = TaskbarItemProgressState.None;
}
else
{
taskbar.ProgressState = TaskbarItemProgressState.Normal;
}
}
else
{
taskbar.ProgressValue = 0d;
taskbar.ProgressState = TaskbarItemProgressState.Indeterminate;
}
}
}
}

View File

@ -0,0 +1,27 @@
using System.Windows;
using System.Windows.Shell;
namespace MicaSetup.Design.Controls;
public static class TaskbarExtension
{
public static TaskbarItemInfo FindTaskbar(this FrameworkElement owner)
{
if ((owner is Window ? owner as Window : Window.GetWindow(owner)) is Window win)
{
return win.TaskbarItemInfo ??= new();
}
return null!;
}
public static TaskbarItemInfo FindTaskbar(this Window owner)
{
return owner.TaskbarItemInfo ??= new();
}
public static void SetProgress(this TaskbarItemInfo taskbarItem, double value, TaskbarItemProgressState state = TaskbarItemProgressState.Normal)
{
taskbarItem.ProgressState = state;
taskbarItem.ProgressValue = value;
}
}

View File

@ -0,0 +1,206 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:design="clr-namespace:MicaSetup.Design"
xmlns:local="clr-namespace:MicaSetup.Design.Controls">
<ResourceDictionary.MergedDictionaries>
<design:ResourceResourceDictionary />
</ResourceDictionary.MergedDictionaries>
<Style x:Key="DefaultScrollBarStyle" TargetType="{x:Type ScrollBar}">
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false" />
<Setter Property="Stylus.IsFlicksEnabled" Value="false" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="MinWidth" Value="0" />
<Setter Property="Height" Value="Auto" />
<Setter Property="Cursor" Value="Arrow" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollBar}">
<Border Background="{Binding Path=(local:ScrollViewerHelper.TrackBrush), RelativeSource={RelativeSource AncestorType=ScrollViewer}}" CornerRadius="{Binding Path=(local:ScrollViewerHelper.ScrollBarCornerRadius), RelativeSource={RelativeSource AncestorType=ScrollViewer}}">
<Track
x:Name="PART_Track"
IsDirectionReversed="true"
IsEnabled="{TemplateBinding IsMouseOver}">
<Track.Thumb>
<Thumb>
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<Border Background="{Binding Path=(local:ScrollViewerHelper.ThumbBrush), RelativeSource={RelativeSource AncestorType=ScrollViewer}}" CornerRadius="{Binding Path=(local:ScrollViewerHelper.ScrollBarCornerRadius), RelativeSource={RelativeSource AncestorType=ScrollViewer}}" />
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Track.Thumb>
</Track>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Orientation" Value="Horizontal">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Margin" Value="1,0,1,1" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="Width" Value="Auto" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollBar}">
<Border Background="{Binding Path=(local:ScrollViewerHelper.TrackBrush), RelativeSource={RelativeSource AncestorType=ScrollViewer}}" CornerRadius="{Binding Path=(local:ScrollViewerHelper.ScrollBarCornerRadius), RelativeSource={RelativeSource AncestorType=ScrollViewer}}">
<Track x:Name="PART_Track" IsEnabled="{TemplateBinding IsMouseOver}">
<Track.Thumb>
<Thumb>
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<Border Background="{Binding Path=(local:ScrollViewerHelper.ThumbBrush), RelativeSource={RelativeSource AncestorType=ScrollViewer}}" CornerRadius="{Binding Path=(local:ScrollViewerHelper.ScrollBarCornerRadius), RelativeSource={RelativeSource AncestorType=ScrollViewer}}" />
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Track.Thumb>
</Track>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="DefaultScrollViewerStyle" TargetType="{x:Type ScrollViewer}">
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="local:ScrollViewerHelper.TrackBrush" Value="Transparent" />
<Setter Property="local:ScrollViewerHelper.ThumbBrush" Value="#CCCCCC" />
<Setter Property="local:ScrollViewerHelper.ScrollBarCornerRadius" Value="4" />
<Setter Property="local:ScrollViewerHelper.ScrollBarThickness" Value="8" />
<Setter Property="local:ScrollViewerHelper.ScrollViewerHook" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ScrollContentPresenter
Margin="{TemplateBinding Padding}"
CanContentScroll="{TemplateBinding CanContentScroll}"
VirtualizingStackPanel.IsVirtualizing="{Binding Path=(VirtualizingStackPanel.IsVirtualizing), RelativeSource={RelativeSource AncestorType=ScrollViewer}}"
VirtualizingStackPanel.VirtualizationMode="{Binding Path=(VirtualizingStackPanel.VirtualizationMode), RelativeSource={RelativeSource AncestorType=ScrollViewer}}" />
<ScrollBar
x:Name="PART_VerticalScrollBar"
Grid.Column="1"
Width="{Binding Path=(local:ScrollViewerHelper.ScrollBarThickness), RelativeSource={RelativeSource AncestorType=ScrollViewer}}"
Maximum="{TemplateBinding ScrollableHeight}"
Opacity="0.5"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
Value="{TemplateBinding VerticalOffset}" />
<ScrollBar
x:Name="PART_HorizontalScrollBar"
Grid.Row="1"
Height="{Binding Path=(local:ScrollViewerHelper.ScrollBarThickness), RelativeSource={RelativeSource AncestorType=ScrollViewer}}"
Maximum="{TemplateBinding ScrollableWidth}"
Opacity="0.5"
Orientation="Horizontal"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
Value="{TemplateBinding HorizontalOffset}" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="ScrollChanged">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="PART_VerticalScrollBar"
Storyboard.TargetProperty="Opacity"
To="0.9"
Duration="0:0:0.15" />
<DoubleAnimation
Storyboard.TargetName="PART_HorizontalScrollBar"
Storyboard.TargetProperty="Opacity"
To="0.9"
Duration="0:0:0.15" />
<DoubleAnimation
BeginTime="0:0:1.5"
Storyboard.TargetName="PART_VerticalScrollBar"
Storyboard.TargetProperty="Opacity"
To="0.5"
Duration="0:0:0.15" />
<DoubleAnimation
BeginTime="0:0:1.5"
Storyboard.TargetName="PART_HorizontalScrollBar"
Storyboard.TargetProperty="Opacity"
To="0.5"
Duration="0:0:0.15" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseEnter" SourceName="PART_VerticalScrollBar">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="PART_VerticalScrollBar"
Storyboard.TargetProperty="Opacity"
To="0.9"
Duration="0:0:0.15" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave" SourceName="PART_VerticalScrollBar">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="PART_VerticalScrollBar"
Storyboard.TargetProperty="Opacity"
To="0.5"
Duration="0:0:0.15" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseEnter" SourceName="PART_HorizontalScrollBar">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="PART_HorizontalScrollBar"
Storyboard.TargetProperty="Opacity"
To="0.9"
Duration="0:0:0.15" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave" SourceName="PART_HorizontalScrollBar">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="PART_HorizontalScrollBar"
Storyboard.TargetProperty="Opacity"
To="0.5"
Duration="0:0:0.15" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style BasedOn="{StaticResource DefaultScrollBarStyle}" TargetType="{x:Type ScrollBar}" />
<Style BasedOn="{StaticResource DefaultScrollViewerStyle}" TargetType="{x:Type ScrollViewer}" />
<Style BasedOn="{StaticResource DefaultScrollViewerStyle}" TargetType="{x:Type local:SmoothScrollViewer}" />
</ResourceDictionary>

View File

@ -0,0 +1,110 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace MicaSetup.Design.Controls;
public class ScrollViewerHelper
{
public static Brush GetTrackBrush(DependencyObject obj)
{
return (Brush)obj.GetValue(TrackBrushProperty);
}
public static void SetTrackBrush(DependencyObject obj, Brush value)
{
obj.SetValue(TrackBrushProperty, value);
}
public static readonly DependencyProperty TrackBrushProperty = DependencyProperty.RegisterAttached("TrackBrush", typeof(Brush), typeof(ScrollViewerHelper));
public static Brush GetThumbBrush(DependencyObject obj)
{
return (Brush)obj.GetValue(ThumbBrushProperty);
}
public static void SetThumbBrush(DependencyObject obj, Brush value)
{
obj.SetValue(ThumbBrushProperty, value);
}
public static readonly DependencyProperty ThumbBrushProperty = DependencyProperty.RegisterAttached("ThumbBrush", typeof(Brush), typeof(ScrollViewerHelper));
public static CornerRadius GetScrollBarCornerRadius(DependencyObject obj)
{
return (CornerRadius)obj.GetValue(ScrollBarCornerRadiusProperty);
}
public static void SetScrollBarCornerRadius(DependencyObject obj, CornerRadius value)
{
obj.SetValue(ScrollBarCornerRadiusProperty, value);
}
public static readonly DependencyProperty ScrollBarCornerRadiusProperty = DependencyProperty.RegisterAttached("ScrollBarCornerRadius", typeof(CornerRadius), typeof(ScrollViewerHelper));
public static double GetScrollBarThickness(DependencyObject obj)
{
return (double)obj.GetValue(ScrollBarThicknessProperty);
}
public static void SetScrollBarThickness(DependencyObject obj, double value)
{
obj.SetValue(ScrollBarThicknessProperty, value);
}
public static readonly DependencyProperty ScrollBarThicknessProperty = DependencyProperty.RegisterAttached("ScrollBarThickness", typeof(double), typeof(ScrollViewerHelper));
internal static bool GetScrollViewerHook(DependencyObject obj)
{
return (bool)obj.GetValue(ScrollViewerHookProperty);
}
internal static void SetScrollViewerHook(DependencyObject obj, bool value)
{
obj.SetValue(ScrollViewerHookProperty, value);
}
internal static readonly DependencyProperty ScrollViewerHookProperty = DependencyProperty.RegisterAttached("ScrollViewerHook", typeof(bool), typeof(ScrollViewerHelper), new PropertyMetadata(OnScrollViewerHookChanged));
private static void OnScrollViewerHookChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var scrollViewer = d as ScrollViewer;
scrollViewer!.PreviewMouseWheel += ScrollViewer_PreviewMouseWheel;
}
private static void ScrollViewer_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
{
var scrollViewer = sender as ScrollViewer;
var handle = true;
if (e.Delta > 0)
{
if (scrollViewer!.ComputedVerticalScrollBarVisibility == Visibility.Visible)
{ handle = false; }
else if (scrollViewer.ComputedHorizontalScrollBarVisibility == Visibility.Visible)
scrollViewer.LineLeft();
else if (scrollViewer.VerticalScrollBarVisibility != ScrollBarVisibility.Disabled)
{ handle = false; }
else if (scrollViewer.HorizontalScrollBarVisibility != ScrollBarVisibility.Disabled)
scrollViewer.LineLeft();
else
return;
}
else
{
if (scrollViewer!.ComputedVerticalScrollBarVisibility == Visibility.Visible)
{ handle = false; }
else if (scrollViewer.ComputedHorizontalScrollBarVisibility == Visibility.Visible)
scrollViewer.LineRight();
else if (scrollViewer.VerticalScrollBarVisibility != ScrollBarVisibility.Disabled)
{ handle = false; }
else if (scrollViewer.HorizontalScrollBarVisibility != ScrollBarVisibility.Disabled)
scrollViewer.LineRight();
else
return;
}
if (handle)
e.Handled = true;
}
}

View File

@ -0,0 +1,222 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace MicaSetup.Design.Controls;
public class SmoothScrollViewer : ScrollViewer
{
private double _totalVerticalOffset;
private double _totalHorizontalOffset;
private bool _isRunning;
public Orientation Orientation
{
get => (Orientation)GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(SmoothScrollViewer), new PropertyMetadata(Orientation.Vertical));
public bool CanMouseWheel
{
get => (bool)GetValue(CanMouseWheelProperty);
set => SetValue(CanMouseWheelProperty, value);
}
public static readonly DependencyProperty CanMouseWheelProperty = DependencyProperty.Register(nameof(CanMouseWheel), typeof(bool), typeof(SmoothScrollViewer), new PropertyMetadata(true));
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
if (ViewportHeight + VerticalOffset >= ExtentHeight && e.Delta <= 0)
{
return;
}
if (VerticalOffset == 0 && e.Delta >= 0)
{
return;
}
if (!CanMouseWheel)
{
return;
}
if (!IsInertiaEnabled)
{
if (Orientation == Orientation.Vertical)
{
base.OnMouseWheel(e);
}
else
{
_totalHorizontalOffset = HorizontalOffset;
CurrentHorizontalOffset = HorizontalOffset;
_totalHorizontalOffset = Math.Min(Math.Max(0, _totalHorizontalOffset - e.Delta), ScrollableWidth);
CurrentHorizontalOffset = _totalHorizontalOffset;
}
return;
}
e.Handled = true;
if (Orientation == Orientation.Vertical)
{
if (!_isRunning)
{
_totalVerticalOffset = VerticalOffset;
CurrentVerticalOffset = VerticalOffset;
}
_totalVerticalOffset = Math.Min(Math.Max(0, _totalVerticalOffset - e.Delta), ScrollableHeight);
ScrollToVerticalOffsetWithAnimation(_totalVerticalOffset);
}
else
{
if (!_isRunning)
{
_totalHorizontalOffset = HorizontalOffset;
CurrentHorizontalOffset = HorizontalOffset;
}
_totalHorizontalOffset = Math.Min(Math.Max(0, _totalHorizontalOffset - e.Delta), ScrollableWidth);
ScrollToHorizontalOffsetWithAnimation(_totalHorizontalOffset);
}
}
internal void ScrollToTopInternal(double milliseconds = 500)
{
if (!_isRunning)
{
_totalVerticalOffset = VerticalOffset;
CurrentVerticalOffset = VerticalOffset;
}
ScrollToVerticalOffsetWithAnimation(0, milliseconds);
}
public void ScrollToVerticalOffsetWithAnimation(double offset, double milliseconds = 500)
{
DoubleAnimation animation = AnimationHelper.CreateAnimation(offset, milliseconds);
animation.EasingFunction = new CubicEase
{
EasingMode = EasingMode.EaseOut
};
animation.FillBehavior = FillBehavior.Stop;
animation.Completed += (s, e1) =>
{
CurrentVerticalOffset = offset;
_isRunning = false;
};
_isRunning = true;
BeginAnimation(CurrentVerticalOffsetProperty, animation, HandoffBehavior.Compose);
}
public void ScrollToHorizontalOffsetWithAnimation(double offset, double milliseconds = 500)
{
DoubleAnimation animation = AnimationHelper.CreateAnimation(offset, milliseconds);
animation.EasingFunction = new CubicEase
{
EasingMode = EasingMode.EaseOut
};
animation.FillBehavior = FillBehavior.Stop;
animation.Completed += (s, e1) =>
{
CurrentHorizontalOffset = offset;
_isRunning = false;
};
_isRunning = true;
BeginAnimation(CurrentHorizontalOffsetProperty, animation, HandoffBehavior.Compose);
}
protected override HitTestResult? HitTestCore(PointHitTestParameters hitTestParameters)
{
return IsPenetrating ? null : base.HitTestCore(hitTestParameters);
}
public static void SetIsInertiaEnabled(DependencyObject element, bool value)
{
element.SetValue(IsInertiaEnabledProperty, value);
}
public static bool GetIsInertiaEnabled(DependencyObject element)
{
return (bool)element.GetValue(IsInertiaEnabledProperty);
}
public bool IsInertiaEnabled
{
get => (bool)GetValue(IsInertiaEnabledProperty);
set => SetValue(IsInertiaEnabledProperty, value);
}
public static readonly DependencyProperty IsInertiaEnabledProperty = DependencyProperty.RegisterAttached(nameof(IsInertiaEnabled), typeof(bool), typeof(SmoothScrollViewer), new PropertyMetadata(true));
public bool IsPenetrating
{
get => (bool)GetValue(IsPenetratingProperty);
set => SetValue(IsPenetratingProperty, value);
}
public static void SetIsPenetrating(DependencyObject element, bool value)
{
element.SetValue(IsPenetratingProperty, value);
}
public static bool GetIsPenetrating(DependencyObject element)
{
return (bool)element.GetValue(IsPenetratingProperty);
}
public static readonly DependencyProperty IsPenetratingProperty = DependencyProperty.RegisterAttached(nameof(IsPenetrating), typeof(bool), typeof(SmoothScrollViewer), new PropertyMetadata(false));
internal double CurrentVerticalOffset
{
get => (double)GetValue(CurrentVerticalOffsetProperty);
set => SetValue(CurrentVerticalOffsetProperty, value);
}
internal static readonly DependencyProperty CurrentVerticalOffsetProperty = DependencyProperty.Register(nameof(CurrentVerticalOffset), typeof(double), typeof(SmoothScrollViewer), new PropertyMetadata(0d, OnCurrentVerticalOffsetChanged));
private static void OnCurrentVerticalOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is SmoothScrollViewer ctl && e.NewValue is double v)
{
ctl.ScrollToVerticalOffset(v);
}
}
internal double CurrentHorizontalOffset
{
get => (double)GetValue(CurrentHorizontalOffsetProperty);
set => SetValue(CurrentHorizontalOffsetProperty, value);
}
internal static readonly DependencyProperty CurrentHorizontalOffsetProperty = DependencyProperty.Register(nameof(CurrentHorizontalOffset), typeof(double), typeof(SmoothScrollViewer), new PropertyMetadata(0d, OnCurrentHorizontalOffsetChanged));
private static void OnCurrentHorizontalOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is SmoothScrollViewer ctl && e.NewValue is double v)
{
ctl.ScrollToHorizontalOffset(v);
}
}
}
file sealed class AnimationHelper
{
public static DoubleAnimation CreateAnimation(double toValue, double milliseconds = 200)
{
return new(toValue, new Duration(TimeSpan.FromMilliseconds(milliseconds)))
{
EasingFunction = new PowerEase { EasingMode = EasingMode.EaseInOut }
};
}
}

View File

@ -0,0 +1,104 @@
namespace MicaSetup.Design.Controls;
public static class Selection
{
public const string CeliakeyboardEnassociate = "\xe900";
public const string CeliakeyboardMenuLanguage = "\xe901";
public const string ContactsDelete = "\xe902";
public const string DeviceMateBook = "\xe903";
public const string FilesNameDrive = "\xe904";
public const string GalleryCollage = "\xe905";
public const string GalleryCreate = "\xe906";
public const string GalleryMaterialSelectCheckbo = "\xe907";
public const string GalleryMoveIn10 = "\xe908";
public const string GalleryMoveOut = "\xe909";
public const string GalleryPhotoEditMore = "\xe90a";
public const string GalleryRectify = "\xe90b";
public const string GalleryRename = "\xe90c";
public const string GallerySet = "\xe90d";
public const string GallerySortOrder = "\xe90e";
public const string GallerySortReverse = "\xe90f";
public const string MessageForwarding = "\xe910";
public const string MessageTheme = "\xe911";
public const string PowerOff = "\xe912";
public const string PublicAdd = "\xe913";
public const string PublicAddFilled = "\xe914";
public const string PublicAddNorm = "\xe915";
public const string PublicApp = "\xe916";
public const string PublicArrowLeft = "\xe917";
public const string PublicArrowRight = "\xe918";
public const string PublicArrowUp0 = "\xe919";
public const string PublicArrowUp1 = "\xe91a";
public const string PublicBack = "\xe91b";
public const string PublicBackToTop = "\xe91c";
public const string PublicBrightness = "\xe91d";
public const string PublicCancel = "\xe91e";
public const string PublicCancelFilled = "\xe91f";
public const string PublicClose = "\xe920";
public const string PublicCloudDownload = "\xe921";
public const string PublicCloudUpload = "\xe922";
public const string PublicConnection = "\xe923";
public const string PublicContacts = "\xe924";
public const string PublicCopy = "\xe925";
public const string PublicDelete = "\xe926";
public const string PublicDoNotDisturb = "\xe927";
public const string PublicDownload = "\xe928";
public const string PublicEdit = "\xe929";
public const string PublicEmail = "\xe92a";
public const string PublicEmailSend = "\xe92b";
public const string PublicEnlarge = "\xe92c";
public const string PublicFail = "\xe92d";
public const string PublicFolder = "\xe92e";
public const string PublicForbid = "\xe92f";
public const string PublicHelp = "\xe930";
public const string PublicHighLights = "\xe931";
public const string PublicHome = "\xe932";
public const string PublicInputSearch = "\xe933";
public const string PublicKeyboard = "\xe934";
public const string PublicListCycle = "\xe935";
public const string PubliClock = "\xe936";
public const string PublicMessage = "\xe937";
public const string PublicMobileData = "\xe938";
public const string PublicMove = "\xe939";
public const string PublicNavigation = "\xe93a";
public const string PublicOk = "\xe93b";
public const string PublicOkFilled = "\xe93c";
public const string PublicPicture = "\xe93d";
public const string PublicPrinter = "\xe93e";
public const string PublicQuit = "\xe93f";
public const string PublicRandom = "\xe940";
public const string PublicReduce = "\xe941";
public const string PublicRefresh = "\xe942";
public const string PublicRemove = "\xe943";
public const string PublicReset = "\xe944";
public const string PublicRing = "\xe945";
public const string PublicRingOff = "\xe946";
public const string PublicRotate = "\xe947";
public const string PublicSave = "\xe948";
public const string PublicScan = "\xe949";
public const string PublicSearch = "\xe94a";
public const string PublicSecurity = "\xe94b";
public const string PublicSend = "\xe94c";
public const string PublicSettings = "\xe94d";
public const string PublicShare = "\xe94e";
public const string PublicSift = "\xe94f";
public const string PublicSound = "\xe950";
public const string PublicText = "\xe951";
public const string PublicThemes = "\xe952";
public const string PublicThumbsup = "\xe953";
public const string PublicTodo = "\xe954";
public const string PublicTopping = "\xe955";
public const string PublicUnlock = "\xe956";
public const string PublicUpgrade = "\xe957";
public const string PublicUpload = "\xe958";
public const string PublicVolumeDown = "\xe959";
public const string PublicWirelessProjection = "\xe95b";
public const string PublicWorldClock = "\xe95c";
public const string QuickReply = "\xe95d";
public const string Reboot = "\xe95e";
public const string SearchThings = "\xe95f";
public const string ContactSencryptionCalls = "\xe960";
public const string Circle = "\xe961";
public const string Squircle = "\xe962";
public const string IcoMoon = "\xe95a";
}

View File

@ -0,0 +1,112 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
namespace MicaSetup.Design.Controls;
public static class Routing
{
public static ServiceProvider Provider { get; internal set; } = null!;
public static WeakReference<ShellControl> Shell { get; internal set; } = null!;
public static void RegisterRoute()
{
ServiceCollection serviceCollection = new();
foreach (var pageItem in ShellPageSetting.PageDict)
{
serviceCollection.Register(pageItem.Key, pageItem.Value);
}
Provider = serviceCollection.BuildServiceProvider();
}
public static FrameworkElement ResolveRoute(string route)
{
if (string.IsNullOrEmpty(route))
{
return null!;
}
return Provider?.Resolve<FrameworkElement>(route)!;
}
public static void GoTo(string route)
{
if (Shell != null)
{
if (Shell.TryGetTarget(out ShellControl shell))
{
OnGoTo(route);
shell.Content = ResolveRoute(route);
shell.Route = route;
}
}
}
public static void GoToNext()
{
if (Shell != null)
{
if (Shell.TryGetTarget(out ShellControl shell))
{
if (ShellPageSetting.PageDict.ContainsKey(shell.Route))
{
bool found = false;
foreach (var item in ShellPageSetting.PageDict)
{
if (found)
{
OnGoTo(item.Key);
shell.Content = ResolveRoute(item.Key);
shell.Route = item.Key;
break;
}
if (item.Key == shell.Route)
{
found = true;
}
}
}
}
}
}
private static void OnGoTo(string route)
{
}
}
file class RoutingServiceInfo
{
public string Name { get; set; }
public Type Type { get; set; }
public RoutingServiceInfo(string name, Type type)
{
Name = name;
Type = type;
}
}
file static class RoutingExtension
{
public static IServiceCollection Register(this IServiceCollection services, string name, Type type)
{
services.AddSingleton(type);
services.AddSingleton(new RoutingServiceInfo(name, type));
return services;
}
public static T Resolve<T>(this IServiceProvider serviceProvider, string name)
{
var serviceInfo = serviceProvider.GetRequiredService<IEnumerable<RoutingServiceInfo>>()
.FirstOrDefault(x => x.Name == name);
if (serviceInfo == null)
{
throw new InvalidOperationException($"Service '{name}' not found");
}
return (T)serviceProvider.GetRequiredService(serviceInfo.Type);
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Windows;
using System.Windows.Controls;
namespace MicaSetup.Design.Controls;
public class ShellControl : ContentControl
{
public string Route
{
get => (string)GetValue(RouteProperty);
set => SetCurrentValue(RouteProperty, value);
}
public static readonly DependencyProperty RouteProperty = DependencyProperty.Register(nameof(Route), typeof(string), typeof(ShellControl), new(string.Empty));
public ShellControl()
{
Routing.Shell = new WeakReference<ShellControl>(this);
FocusVisualStyle = null!;
Loaded += (_, _) => Routing.GoTo(Route);
}
}

View File

@ -0,0 +1,9 @@
using System;
using System.Collections.Generic;
namespace MicaSetup.Design.Controls;
public class ShellPageSetting
{
public static Dictionary<string, Type> PageDict = new();
}

View File

@ -0,0 +1,102 @@
using CommunityToolkit.Mvvm.Input;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
namespace MicaSetup.Design.Controls;
public class TextBoxEx : TextBox
{
public static readonly DependencyProperty PlaceholderTextProperty = DependencyProperty.Register(
nameof(PlaceholderText),
typeof(string),
typeof(TextBoxEx),
new PropertyMetadata(string.Empty)
);
public static readonly DependencyProperty PlaceholderEnabledProperty = DependencyProperty.Register(
nameof(PlaceholderEnabled),
typeof(bool),
typeof(TextBoxEx),
new PropertyMetadata(true)
);
public static readonly DependencyProperty IsTextSelectionEnabledProperty = DependencyProperty.Register(
nameof(IsTextSelectionEnabled),
typeof(bool),
typeof(TextBoxEx),
new PropertyMetadata(false)
);
public static readonly DependencyProperty TemplateButtonCommandProperty = DependencyProperty.Register(
nameof(TemplateButtonCommand),
typeof(IRelayCommand),
typeof(TextBoxEx),
new PropertyMetadata(null)
);
public string PlaceholderText
{
get => (string)GetValue(PlaceholderTextProperty);
set => SetValue(PlaceholderTextProperty, value);
}
public bool PlaceholderEnabled
{
get => (bool)GetValue(PlaceholderEnabledProperty);
set => SetValue(PlaceholderEnabledProperty, value);
}
/// <summary>
/// TODO
/// </summary>
public bool IsTextSelectionEnabled
{
get => (bool)GetValue(IsTextSelectionEnabledProperty);
set => SetValue(IsTextSelectionEnabledProperty, value);
}
public IRelayCommand TemplateButtonCommand => (IRelayCommand)GetValue(TemplateButtonCommandProperty);
public TextBoxEx()
{
SetValue(TemplateButtonCommandProperty, new RelayCommand<string>(OnTemplateButtonClick));
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
if (PlaceholderEnabled && Text.Length > 0)
PlaceholderEnabled = false;
if (!PlaceholderEnabled && Text.Length < 1)
PlaceholderEnabled = true;
}
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
CaretIndex = Text.Length;
}
protected override void OnLostFocus(RoutedEventArgs e)
{
base.OnLostFocus(e);
}
protected virtual void OnClearButtonClick()
{
if (Text.Length > 0)
{
Text = string.Empty;
}
}
protected virtual void OnTemplateButtonClick(string? parameter)
{
Debug.WriteLine($"INFO: {typeof(TextBoxEx)} button clicked");
OnClearButtonClick();
}
}

View File

@ -0,0 +1,286 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:MicaSetup.Design.Controls"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MicaSetup;component/Resources/Variables.xaml" />
</ResourceDictionary.MergedDictionaries>
<Thickness x:Key="TextBoxBorderThemeThickness">1,1,1,0</Thickness>
<Thickness x:Key="TextBoxAccentBorderThemeThickness">0,0,0,1</Thickness>
<Thickness x:Key="TextBoxLeftIconMargin">10,0,0,0</Thickness>
<Thickness x:Key="TextBoxRightIconMargin">0,0,10,0</Thickness>
<Thickness x:Key="TextBoxClearButtonMargin">0,0,4,0</Thickness>
<Thickness x:Key="TextBoxClearButtonPadding">0,0,0,0</Thickness>
<sys:Double x:Key="TextBoxClearButtonHeight">24</sys:Double>
<Style x:Key="DefaultTextBoxScrollViewerStyle" TargetType="{x:Type ScrollViewer}">
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<ScrollContentPresenter Margin="0" CanContentScroll="{TemplateBinding CanContentScroll}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="DefaultTextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Foreground" Value="{DynamicResource TextControlForeground}" />
<Setter Property="CaretBrush" Value="{DynamicResource TextControlForeground}" />
<Setter Property="Background" Value="{DynamicResource TextControlBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource TextControlElevationBorderBrush}" />
<Setter Property="BorderThickness" Value="{StaticResource TextBoxBorderThemeThickness}" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="ScrollViewer.CanContentScroll" Value="False" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="MinHeight" Value="{DynamicResource TextControlThemeMinHeight}" />
<Setter Property="MinWidth" Value="{DynamicResource TextControlThemeMinWidth}" />
<Setter Property="Padding" Value="{DynamicResource TextControlThemePadding}" />
<Setter Property="Border.CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}">
<Border
x:Name="ContentBorder"
MinWidth="{TemplateBinding MinWidth}"
MinHeight="{TemplateBinding MinHeight}"
Padding="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding Border.CornerRadius}">
<Grid
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<ScrollViewer
x:Name="PART_ContentHost"
VerticalAlignment="Center"
CanContentScroll="{TemplateBinding ScrollViewer.CanContentScroll}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
IsTabStop="{TemplateBinding ScrollViewer.IsTabStop}"
Style="{StaticResource DefaultTextBoxScrollViewerStyle}"
TextElement.Foreground="{TemplateBinding Foreground}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" />
</Grid>
</Border>
<!-- The Accent Border is a separate element so that changes to the border thickness do not affect the position of the element -->
<Border
x:Name="AccentBorder"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
BorderThickness="{StaticResource TextBoxAccentBorderThemeThickness}"
CornerRadius="{TemplateBinding Border.CornerRadius}">
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource ControlStrongStrokeColorDefault}" />
</Border.BorderBrush>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter TargetName="AccentBorder" Property="BorderThickness" Value="0,0,0,2" />
<Setter TargetName="AccentBorder" Property="BorderBrush" Value="{DynamicResource TextControlFocusedBorderBrush}" />
<Setter TargetName="ContentBorder" Property="Background" Value="{DynamicResource TextControlBackgroundFocused}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsEnabled" Value="True" />
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsFocused" Value="False" />
</MultiTrigger.Conditions>
<Setter TargetName="ContentBorder" Property="Background" Value="{DynamicResource TextControlBackgroundPointerOver}" />
</MultiTrigger>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Cursor" Value="IBeam" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="ContentBorder" Property="Background" Value="{DynamicResource TextControlBackgroundDisabled}" />
<Setter TargetName="ContentBorder" Property="BorderBrush" Value="{DynamicResource TextControlBorderBrushDisabled}" />
<Setter TargetName="AccentBorder" Property="BorderBrush" Value="{DynamicResource TextControlBorderBrushDisabled}" />
<Setter Property="Foreground" Value="{DynamicResource TextControlForegroundDisabled}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="DefaultUiTextBoxControlTemplate" TargetType="{x:Type controls:TextBoxEx}">
<Grid HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}">
<Border
x:Name="ContentBorder"
MinWidth="{TemplateBinding MinWidth}"
MinHeight="{TemplateBinding MinHeight}"
Padding="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding Border.CornerRadius}">
<Grid HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="1" Margin="{TemplateBinding Padding}">
<ScrollViewer
x:Name="PART_ContentHost"
VerticalAlignment="Center"
CanContentScroll="{TemplateBinding ScrollViewer.CanContentScroll}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
IsTabStop="{TemplateBinding ScrollViewer.IsTabStop}"
Style="{StaticResource DefaultTextBoxScrollViewerStyle}"
TextElement.Foreground="{TemplateBinding Foreground}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" />
<TextBlock
x:Name="PlaceholderTextBox"
Margin="0"
Padding="1,0"
VerticalAlignment="Center"
Foreground="{DynamicResource TextControlPlaceholderForeground}"
Text="{TemplateBinding PlaceholderText}" />
</Grid>
</Grid>
</Border>
<!-- The Accent Border is a separate element so that changes to the border thickness do not affect the position of the element -->
<Border
x:Name="AccentBorder"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
BorderThickness="{StaticResource TextBoxAccentBorderThemeThickness}"
CornerRadius="{TemplateBinding Border.CornerRadius}">
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource ControlStrongStrokeColorDefault}" />
</Border.BorderBrush>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="PlaceholderEnabled" Value="False">
<Setter TargetName="PlaceholderTextBox" Property="Visibility" Value="Collapsed" />
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter TargetName="AccentBorder" Property="BorderThickness" Value="0,0,0,2" />
<Setter TargetName="AccentBorder" Property="BorderBrush" Value="{DynamicResource TextControlFocusedBorderBrush}" />
<Setter TargetName="ContentBorder" Property="Background" Value="{DynamicResource TextControlBackgroundFocused}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsEnabled" Value="True" />
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsFocused" Value="False" />
</MultiTrigger.Conditions>
<Setter TargetName="ContentBorder" Property="Background" Value="{DynamicResource TextControlBackgroundPointerOver}" />
</MultiTrigger>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Cursor" Value="IBeam" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="ContentBorder" Property="Background" Value="{DynamicResource TextControlBackgroundDisabled}" />
<Setter TargetName="ContentBorder" Property="BorderBrush" Value="{DynamicResource TextControlBorderBrushDisabled}" />
<Setter TargetName="AccentBorder" Property="BorderBrush" Value="{DynamicResource TextControlBorderBrushDisabled}" />
<Setter TargetName="PlaceholderTextBox" Property="Foreground" Value="{DynamicResource TextControlForegroundDisabled}" />
<Setter Property="Foreground" Value="{DynamicResource TextControlForegroundDisabled}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="DefaultUiTextBoxTextSelectionEnabledControlTemplate" TargetType="{x:Type controls:TextBoxEx}">
<Grid HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}">
<Border
x:Name="ContentBorder"
MinWidth="{TemplateBinding MinWidth}"
MinHeight="{TemplateBinding MinHeight}"
Padding="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding Border.CornerRadius}"
Focusable="False">
<Grid HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Grid Margin="{TemplateBinding Padding}">
<ScrollViewer
x:Name="PART_ContentHost"
VerticalAlignment="Center"
CanContentScroll="{TemplateBinding ScrollViewer.CanContentScroll}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
IsTabStop="{TemplateBinding ScrollViewer.IsTabStop}"
Style="{StaticResource DefaultTextBoxScrollViewerStyle}"
TextElement.Foreground="{TemplateBinding Foreground}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" />
</Grid>
</Grid>
</Border>
</Grid>
</ControlTemplate>
<Style x:Key="DefaultUiTextBoxStyle" TargetType="{x:Type controls:TextBoxEx}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Foreground" Value="{DynamicResource TextControlForeground}" />
<Setter Property="CaretBrush" Value="{DynamicResource TextControlForeground}" />
<Setter Property="Background" Value="{DynamicResource TextControlBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource TextControlElevationBorderBrush}" />
<Setter Property="BorderThickness" Value="{StaticResource TextBoxBorderThemeThickness}" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="ScrollViewer.CanContentScroll" Value="False" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="MinHeight" Value="{DynamicResource TextControlThemeMinHeight}" />
<Setter Property="MinWidth" Value="{DynamicResource TextControlThemeMinWidth}" />
<Setter Property="Padding" Value="{DynamicResource TextControlThemePadding}" />
<Setter Property="Border.CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Style.Triggers>
<Trigger Property="IsTextSelectionEnabled" Value="False">
<Setter Property="Template" Value="{StaticResource DefaultUiTextBoxControlTemplate}" />
</Trigger>
<Trigger Property="IsTextSelectionEnabled" Value="True">
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Padding" Value="0" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Template" Value="{StaticResource DefaultUiTextBoxTextSelectionEnabledControlTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
<Style BasedOn="{StaticResource DefaultTextBoxStyle}" TargetType="{x:Type TextBox}" />
<Style BasedOn="{StaticResource DefaultUiTextBoxStyle}" TargetType="{x:Type controls:TextBoxEx}" />
</ResourceDictionary>

View File

@ -0,0 +1,22 @@
using MicaSetup.Natives;
using Microsoft.Win32;
namespace MicaSetup.Design.Controls;
public class ApplicationThemeManager
{
public static ApplicationTheme GetAppTheme()
{
using RegistryKey? key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize");
object? registryValueObject = key?.GetValue("AppsUseLightTheme");
if (registryValueObject == null)
{
return ApplicationTheme.Light;
}
var registryValue = (int)registryValueObject;
return registryValue > 0 ? ApplicationTheme.Light : ApplicationTheme.Dark;
}
}

View File

@ -0,0 +1,44 @@
using Microsoft.Xaml.Behaviors;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
namespace MicaSetup.Design.Controls;
public sealed class WindowClearOwnerOnClosingBehavior : Behavior<Window>
{
protected override void OnAttached()
{
AssociatedObject.Closing += OnWindowClosing;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.Closing -= OnWindowClosing;
base.OnDetaching();
}
private void OnWindowClosing(object? sender, CancelEventArgs e)
{
if (!e.Cancel)
{
try
{
if (AssociatedObject.Owner != null)
{
AssociatedObject.Owner = null!;
}
}
catch (Exception ex)
{
Debug.WriteLine($"Don't attach this {nameof(WindowClearOwnerOnClosingBehavior)} for {nameof(Window)} called from {nameof(Window.ShowDialog)}.", ex);
if (Debugger.IsAttached)
{
Debugger.Break();
}
}
}
}
}

View File

@ -0,0 +1,28 @@
using Microsoft.Xaml.Behaviors;
using System;
using System.Windows;
namespace MicaSetup.Design.Controls;
public sealed class WindowDragMoveBehavior : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
AssociatedObject.MouseLeftButtonDown += MouseLeftButtonDown;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.MouseLeftButtonDown -= MouseLeftButtonDown;
base.OnDetaching();
}
private void MouseLeftButtonDown(object sender, EventArgs e)
{
if (sender is UIElement ui)
{
Window.GetWindow(ui)?.DragMove();
}
}
}

View File

@ -0,0 +1,27 @@
using MicaSetup.Natives;
using Microsoft.Xaml.Behaviors;
using System;
using System.Windows;
using System.Windows.Interop;
namespace MicaSetup.Design.Controls;
public sealed class WindowHideTitleButtonBehavior : Behavior<Window>
{
protected override void OnAttached()
{
AssociatedObject.SourceInitialized += OnSourceInitialized;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.Loaded -= OnSourceInitialized;
base.OnDetaching();
}
private void OnSourceInitialized(object? sender, EventArgs e)
{
NativeMethods.HideAllWindowButton(new WindowInteropHelper(AssociatedObject).Handle);
}
}

View File

@ -0,0 +1,49 @@
using Microsoft.Xaml.Behaviors;
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
namespace MicaSetup.Design.Controls;
public sealed class WindowRestorePathBehavior : Behavior<FrameworkElement>
{
private WindowState windowStatePrev = WindowState.Normal;
protected override void OnAttached()
{
AssociatedObject.Loaded += Loaded;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.Loaded -= Loaded;
base.OnDetaching();
}
private void Loaded(object sender, EventArgs e)
{
if (sender is UIElement maximizeButtonContent && Window.GetWindow(maximizeButtonContent) is Window window)
{
window.SizeChanged += (s, e) =>
{
if (windowStatePrev != window.WindowState)
{
if (maximizeButtonContent is Path path)
{
if (window.WindowState == WindowState.Maximized)
{
path.Data = Geometry.Parse("M0 0.2 L0.8 0.2 M0 01 L0.8 1 M0.8 1 L0.8 0.2 M0 0.2 L0 1 M0.3 0 L0.95 0 M01 0.05 L1 0.7");
}
else
{
path.Data = Geometry.Parse("M0.025 0 L0.975 0 M0.025 1 L0.975 1 M1 0.975 L1 0.025 M0 0.025 L0 0.975");
}
}
windowStatePrev = window.WindowState;
}
};
}
}
}

View File

@ -0,0 +1,83 @@
using MicaSetup.Helper;
using MicaSetup.Natives;
using Microsoft.Xaml.Behaviors;
using System;
using System.Windows;
using System.Windows.Input;
namespace MicaSetup.Design.Controls;
public sealed class WindowTitleHeaderBehavior : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
AssociatedObject.Loaded += Loaded;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.Loaded -= Loaded;
base.OnDetaching();
}
private void Loaded(object sender, EventArgs e)
{
AssociatedObject.RegisterAsTitleHeader();
}
}
file static class RegisterAsTitleHeaderBehaviorExtension
{
public static void RegisterAsTitleHeader(this UIElement self)
{
self.MouseLeftButtonDown += (s, e) =>
{
if (s is UIElement titleHeader && Window.GetWindow(titleHeader) is Window window)
{
if (e.ClickCount == 2)
{
if (window.ResizeMode == ResizeMode.CanResize || window.ResizeMode == ResizeMode.CanResizeWithGrip)
{
switch (window.WindowState)
{
case WindowState.Normal:
WindowSystemCommands.MaximizeWindow(window);
break;
case WindowState.Maximized:
WindowSystemCommands.RestoreWindow(window);
break;
}
}
}
else
{
if (e.LeftButton == MouseButtonState.Pressed)
{
WindowSystemCommands.FastRestoreWindow(window);
}
}
}
};
self.MouseRightButtonDown += (s, e) =>
{
if (s is UIElement titleHeader && Window.GetWindow(titleHeader) is Window window)
{
if (window.Cursor != null && window.Cursor.ToString() == Cursors.None.ToString())
{
return;
}
if (e.RightButton == MouseButtonState.Pressed)
{
if (User32.GetCursorPos(out POINT pt))
{
e.Handled = true;
SystemCommands.ShowSystemMenu(window, new Point(DpiHelper.CalcDPiX(pt.X), DpiHelper.CalcDPiY(pt.Y)));
}
}
}
};
}
}

View File

@ -0,0 +1,48 @@
using MicaSetup.Helper;
using MicaSetup.Natives;
using Microsoft.Xaml.Behaviors;
using System;
using System.Windows;
using System.Windows.Input;
namespace MicaSetup.Design.Controls;
public sealed class WindowTitleIconBehavior : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
AssociatedObject.Loaded += Loaded;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.Loaded -= Loaded;
base.OnDetaching();
}
private void Loaded(object sender, EventArgs e)
{
AssociatedObject.RegisterAsTitleIcon();
}
}
public static class RegisterAsTitleIconBehaviorExtension
{
public static void RegisterAsTitleIcon(this UIElement self)
{
self.MouseLeftButtonDown += (s, e) =>
{
if (s is UIElement titleHeader && Window.GetWindow(titleHeader) is Window window)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
if (User32.GetCursorPos(out POINT pt))
{
SystemCommands.ShowSystemMenu(window, new Point(DpiHelper.CalcDPiX(pt.X), DpiHelper.CalcDPiY(pt.Y)));
}
}
}
};
}
}

View File

@ -0,0 +1,174 @@
using MicaSetup.Helper;
using MicaSetup.Natives;
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Controls;
using System.Windows.Media;
using Point = System.Windows.Point;
using Size = System.Windows.Size;
namespace MicaSetup.Design.Controls;
/// <summary>
/// https://learn.microsoft.com/zh-cn/windows/apps/desktop/modernize/apply-snap-layout-menu
/// </summary>
public sealed class SnapLayout
{
public bool IsRegistered { get; private set; } = false;
public bool IsSupported => Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Build > 20000;
public static bool IsEnabled { get; } = IsSnapLayoutEnabled();
private Button? button;
private bool isButtonFocused;
private Window? window;
public void Register(Button button)
{
isButtonFocused = false;
this.button = button;
IsRegistered = true;
}
public nint WndProc(nint hWnd, int msg, nint wParam, nint lParam, ref bool handled)
{
if (!IsRegistered) return 0;
switch ((User32.WindowMessage)msg)
{
case User32.WindowMessage.WM_NCLBUTTONDOWN:
if (IsOverButton(lParam))
{
window ??= Window.GetWindow(button);
if (window != null)
{
WindowSystemCommands.MaximizeOrRestoreWindow(window);
}
else
{
if (new ButtonAutomationPeer(button).GetPattern(PatternInterface.Invoke) is IInvokeProvider invokeProv)
{
invokeProv?.Invoke();
}
}
handled = true;
}
break;
case User32.WindowMessage.WM_NCMOUSELEAVE:
DefocusButton();
break;
case User32.WindowMessage.WM_NCHITTEST:
if (IsEnabled)
{
if (IsOverButton(lParam))
{
FocusButton();
handled = true;
}
else
{
DefocusButton();
}
}
return (int)User32.HitTestValues.HTMAXBUTTON;
case User32.WindowMessage.WM_SETCURSOR:
if (isButtonFocused)
{
handled = true;
}
break;
default:
handled = false;
break;
}
return (int)User32.HitTestValues.HTCLIENT;
}
private void FocusButton()
{
if (isButtonFocused) return;
if (button != null)
{
button.Background = (Brush)Application.Current.FindResource("ButtonBackgroundPointerOver");
button.BorderBrush = (Brush)Application.Current.FindResource("ButtonBorderBrushPointerOver");
button.Foreground = (Brush)Application.Current.FindResource("ButtonForegroundPointerOver");
}
isButtonFocused = true;
}
private void DefocusButton()
{
if (!isButtonFocused) return;
button?.ClearValue(Control.BackgroundProperty);
button?.ClearValue(Control.BorderBrushProperty);
button?.ClearValue(Control.ForegroundProperty);
isButtonFocused = false;
}
private bool IsOverButton(nint lParam)
{
if (button == null)
{
return false;
}
try
{
int x = Macro.GET_X_LPARAM(lParam);
int y = Macro.GET_Y_LPARAM(lParam);
Rect rect = new(button.PointToScreen(new Point()), new Size(DpiHelper.CalcDPIX(button.ActualWidth), DpiHelper.CalcDPIY(button.ActualHeight)));
if (rect.Contains(new Point(x, y)))
{
return true;
}
}
catch (OverflowException)
{
return true;
}
return false;
}
private static bool IsSnapLayoutEnabled()
{
try
{
using RegistryKey? key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced", true);
object? registryValueObject = key?.GetValue("EnableSnapAssistFlyout");
if (registryValueObject == null)
{
return true;
}
int registryValue = (int)registryValueObject;
return registryValue > 0;
}
catch (Exception e)
{
Debug.WriteLine(e);
}
return true;
}
}
file static class Macro
{
public static ushort LOWORD(nint value) => (ushort)((long)value & 0xFFFF);
public static ushort HIWORD(nint value) => (ushort)((((long)value) >> 0x10) & 0xFFFF);
public static int GET_X_LPARAM(nint wParam) => LOWORD(wParam);
public static int GET_Y_LPARAM(nint wParam) => HIWORD(wParam);
}

View File

@ -0,0 +1,187 @@
using MicaSetup.Helper;
using MicaSetup.Natives;
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
namespace MicaSetup.Design.Controls;
public static class WindowBackdrop
{
private static bool IsSupported(WindowBackdropType backdropType)
{
return backdropType switch
{
WindowBackdropType.Auto => OsVersionHelper.IsWindows11_22523,
WindowBackdropType.Tabbed => OsVersionHelper.IsWindows11_22523,
WindowBackdropType.Mica => OsVersionHelper.IsWindows11_OrGreater,
WindowBackdropType.Acrylic => OsVersionHelper.IsWindows7_OrGreater,
WindowBackdropType.None => OsVersionHelper.IsWindows11_OrGreater,
_ => false
};
}
public static bool ApplyBackdrop(Window window, WindowBackdropType backdropType = WindowBackdropType.Mica, ApplicationTheme theme = ApplicationTheme.Unknown)
{
if (!IsSupported(backdropType))
{
return false;
}
if (window is null)
{
return false;
}
if (window.IsLoaded)
{
nint windowHandle = new WindowInteropHelper(window).Handle;
if (windowHandle == 0x00)
{
return false;
}
return ApplyBackdrop(windowHandle, backdropType, theme);
}
window.Loaded += (sender, _) =>
{
nint windowHandle =
new WindowInteropHelper(sender as Window ?? null)?.Handle
?? IntPtr.Zero;
if (windowHandle == 0x00)
return;
ApplyBackdrop(windowHandle, backdropType, theme);
};
return true;
}
public static bool ApplyBackdrop(nint hWnd, WindowBackdropType backdropType = WindowBackdropType.Mica, ApplicationTheme theme = ApplicationTheme.Unknown)
{
if (!IsSupported(backdropType))
{
return false;
}
if (hWnd == 0x00 || !User32.IsWindow(hWnd))
{
return false;
}
if (backdropType != WindowBackdropType.Auto)
{
WindowDarkMode.RemoveBackground(hWnd);
}
switch (theme)
{
case ApplicationTheme.Unknown:
if (ApplicationThemeManager.GetAppTheme() == ApplicationTheme.Dark)
{
WindowDarkMode.ApplyWindowDarkMode(hWnd);
}
break;
case ApplicationTheme.Dark:
WindowDarkMode.ApplyWindowDarkMode(hWnd);
break;
case ApplicationTheme.Light:
case ApplicationTheme.HighContrast:
WindowDarkMode.RemoveWindowDarkMode(hWnd);
break;
}
var wtaOptions = new UxTheme.WTA_OPTIONS()
{
Flags = UxTheme.WTNCA.WTNCA_NODRAWCAPTION,
Mask = (uint)UxTheme.ThemeDialogTextureFlags.ETDT_VALIDBITS,
};
UxTheme.SetWindowThemeAttribute(
hWnd,
UxTheme.WINDOWTHEMEATTRIBUTETYPE.WTA_NONCLIENT,
wtaOptions,
(uint)Marshal.SizeOf(typeof(UxTheme.WTA_OPTIONS))
);
var dwmApiResult = DwmApi.DwmSetWindowAttribute(
hWnd,
DWMWINDOWATTRIBUTE.DWMWA_SYSTEMBACKDROP_TYPE,
(int)(backdropType switch
{
WindowBackdropType.Auto => DwmApi.DWM_SYSTEMBACKDROP_TYPE.DWMSBT_AUTO,
WindowBackdropType.Mica => DwmApi.DWM_SYSTEMBACKDROP_TYPE.DWMSBT_MAINWINDOW,
WindowBackdropType.Acrylic => DwmApi.DWM_SYSTEMBACKDROP_TYPE.DWMSBT_TRANSIENTWINDOW,
WindowBackdropType.Tabbed => DwmApi.DWM_SYSTEMBACKDROP_TYPE.DWMSBT_TABBEDWINDOW,
_ => DwmApi.DWM_SYSTEMBACKDROP_TYPE.DWMSBT_NONE,
}),
Marshal.SizeOf(typeof(int))
);
return dwmApiResult == HRESULT.S_OK;
}
public static bool RemoveBackdrop(Window window)
{
if (window == null)
{
return false;
}
nint hWnd = new WindowInteropHelper(window).Handle;
return RemoveBackdrop(hWnd);
}
public static bool RemoveBackdrop(nint hWnd)
{
if (hWnd == 0x00 || !User32.IsWindow(hWnd))
{
return false;
}
var windowSource = HwndSource.FromHwnd(hWnd);
if (windowSource?.Handle.ToInt32() != 0x00 && windowSource?.CompositionTarget != null)
{
windowSource.CompositionTarget.BackgroundColor = SystemColors.WindowColor;
}
if (windowSource?.RootVisual is Window window)
{
var backgroundBrush = window.Resources["ApplicationBackgroundBrush"];
if (backgroundBrush is not SolidColorBrush)
{
backgroundBrush = ApplicationThemeManager.GetAppTheme() == ApplicationTheme.Dark
? new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20))
: new SolidColorBrush(Color.FromArgb(0xFF, 0xFA, 0xFA, 0xFA));
}
window.Background = (SolidColorBrush)backgroundBrush;
}
_ = DwmApi.DwmSetWindowAttribute(
hWnd,
DWMWINDOWATTRIBUTE.DWMWA_MICA_EFFECT,
0x0,
Marshal.SizeOf(typeof(int))
);
_ = DwmApi.DwmSetWindowAttribute(
hWnd,
DWMWINDOWATTRIBUTE.DWMWA_SYSTEMBACKDROP_TYPE,
(int)DwmApi.DWM_SYSTEMBACKDROP_TYPE.DWMSBT_NONE,
Marshal.SizeOf(typeof(int))
);
return true;
}
}

View File

@ -0,0 +1,100 @@
using MicaSetup.Helper;
using MicaSetup.Natives;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
namespace MicaSetup.Design.Controls;
public static class WindowDarkMode
{
public static bool ApplyWindowDarkMode(nint hWnd)
{
if (hWnd == 0x00 || !User32.IsWindow(hWnd))
{
return false;
}
var dwAttribute = DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE;
if (!OsVersionHelper.IsWindows11_22523_OrGreater)
{
dwAttribute = DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE_OLD;
}
_ = DwmApi.DwmSetWindowAttribute(
hWnd,
dwAttribute,
0x1,
Marshal.SizeOf(typeof(int))
);
return true;
}
public static bool RemoveWindowDarkMode(nint handle)
{
if (handle == 0x00 || !User32.IsWindow(handle))
{
return false;
}
var dwAttribute = DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE;
if (!OsVersionHelper.IsWindows11_22523_OrGreater)
{
dwAttribute = DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE_OLD;
}
_ = DwmApi.DwmSetWindowAttribute(
handle,
dwAttribute,
0x0,
Marshal.SizeOf(typeof(int))
);
return true;
}
public static bool RemoveBackground(nint hWnd)
{
if (hWnd == 0x00 || !User32.IsWindow(hWnd))
{
return false;
}
var windowSource = HwndSource.FromHwnd(hWnd);
if (windowSource?.RootVisual is Window window)
{
return RemoveBackground(window);
}
return false;
}
public static bool RemoveBackground(Window window)
{
if (window == null)
{
return false;
}
window.Background = Brushes.Transparent;
nint windowHandle = new WindowInteropHelper(window).Handle;
if (windowHandle == 0x00)
{
return false;
}
var windowSource = HwndSource.FromHwnd(windowHandle);
if (windowSource?.Handle.ToInt32() != 0x00 && windowSource?.CompositionTarget != null)
{
windowSource.CompositionTarget.BackgroundColor = Colors.Transparent;
}
return true;
}
}

View File

@ -0,0 +1,9 @@
namespace MicaSetup.Design.Controls;
public static class WindowExtension
{
//public static void EnableBackdrop(this Window window, BackdropType backdropType = BackdropType.Mica)
//{
// ThemeService.Current.EnableBackdrop(window, backdropType);
//}
}

View File

@ -0,0 +1,71 @@
using MicaSetup.Helper;
using MicaSetup.Natives;
using System.Security;
using System.Windows;
using System.Windows.Interop;
namespace MicaSetup.Design.Controls;
internal static class WindowSystemCommands
{
[SecuritySafeCritical]
public static void ShowSystemMenu(Window window, Point? screenLocation = null!)
{
if (screenLocation == null)
{
if (User32.GetCursorPos(out POINT pt))
{
SystemCommands.ShowSystemMenu(window, new Point(DpiHelper.CalcDPiX(pt.X), DpiHelper.CalcDPiY(pt.Y)));
}
}
else
{
SystemCommands.ShowSystemMenu(window, new Point(DpiHelper.CalcDPiX(screenLocation.Value.X), DpiHelper.CalcDPiY(screenLocation.Value.Y)));
}
}
[SecuritySafeCritical]
public static void CloseWindow(Window window)
{
SystemCommands.CloseWindow(window);
}
[SecuritySafeCritical]
public static void MaximizeWindow(Window window)
{
SystemCommands.MaximizeWindow(window);
}
[SecuritySafeCritical]
public static void MinimizeWindow(Window window)
{
SystemCommands.MinimizeWindow(window);
}
[SecuritySafeCritical]
public static void RestoreWindow(Window window)
{
SystemCommands.RestoreWindow(window);
window.WindowStyle = WindowStyle.SingleBorderWindow;
}
[SecuritySafeCritical]
public static void FastRestoreWindow(Window window, bool force = false)
{
_ = User32.PostMessage(new WindowInteropHelper(window).Handle, (int)User32.WindowMessage.WM_NCLBUTTONDOWN, (int)User32.HitTestValues.HTCAPTION, 0);
window.WindowStyle = WindowStyle.SingleBorderWindow;
}
[SecuritySafeCritical]
public static void MaximizeOrRestoreWindow(Window window)
{
if (window.WindowState == WindowState.Maximized)
{
RestoreWindow(window);
}
else
{
MaximizeWindow(window);
}
}
}

View File

@ -0,0 +1,149 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MicaSetup.Natives;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Shell;
namespace MicaSetup.Design.Controls;
[INotifyPropertyChanged]
public partial class WindowX : Window
{
public HwndSource? HwndSource { get; private set; }
public nint? Handle { get; private set; }
public SnapLayout SnapLayout { get; } = new();
public bool IsActivated
{
get => (bool)GetValue(IsActivatedProperty);
set => SetValue(IsActivatedProperty, value);
}
public static readonly DependencyProperty IsActivatedProperty = DependencyProperty.Register(nameof(IsActivated), typeof(bool), typeof(WindowX), new PropertyMetadata(false));
public WindowX()
{
Loaded += (s, e) =>
{
HwndSource = (PresentationSource.FromVisual(this) as HwndSource)!;
HwndSource.AddHook(new HwndSourceHook(WndProc));
Handle = new WindowInteropHelper(this).Handle;
if (GetTemplateChild("PART_BtnMaximize") is Button buttonMaximize)
{
if (SnapLayout.IsSupported)
{
SnapLayout.Register(buttonMaximize);
}
}
};
}
protected private virtual nint WndProc(nint hWnd, int msg, nint wParam, nint lParam, ref bool handled)
{
return SnapLayout.WndProc(hWnd, msg, wParam, lParam, ref handled);
}
protected override void OnActivated(EventArgs e)
{
IsActivated = true;
_ = WindowBackdrop.ApplyBackdrop(this, WindowBackdropType.Mica, ThemeService.Current.CurrentTheme switch
{
WindowsTheme.Dark => ApplicationTheme.Dark,
WindowsTheme.Light => ApplicationTheme.Light,
WindowsTheme.Auto or _ => ApplicationTheme.Unknown,
});
base.OnActivated(e);
}
protected override void OnDeactivated(EventArgs e)
{
IsActivated = false;
base.OnDeactivated(e);
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
WindowBackdrop.ApplyBackdrop(this, WindowBackdropType.None, ApplicationTheme.Unknown);
NativeMethods.HideAllWindowButton(new WindowInteropHelper(this).Handle);
}
public override void EndInit()
{
ApplyResizeBorderThickness(WindowState);
base.EndInit();
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if (e.Property.Name is nameof(WindowState))
{
ApplyResizeBorderThickness((WindowState)e.NewValue);
}
base.OnPropertyChanged(e);
}
private void ApplyResizeBorderThickness(WindowState windowsState)
{
if (windowsState == WindowState.Maximized || ResizeMode == ResizeMode.NoResize || ResizeMode == ResizeMode.CanMinimize)
{
WindowChrome.SetWindowChrome(this, new WindowChrome()
{
CaptionHeight = 0d,
CornerRadius = new CornerRadius(8d),
GlassFrameThickness = new Thickness(-1d),
ResizeBorderThickness = new Thickness(0d),
});
}
else
{
WindowChrome.SetWindowChrome(this, new WindowChrome()
{
CaptionHeight = 0d,
CornerRadius = new CornerRadius(8d),
GlassFrameThickness = new Thickness(-1d),
ResizeBorderThickness = new Thickness(3d),
});
}
}
[RelayCommand]
private void CloseWindow()
{
WindowSystemCommands.CloseWindow(this);
}
[RelayCommand]
private void MinimizeWindow()
{
WindowSystemCommands.MinimizeWindow(this);
}
[RelayCommand]
private void MaximizeWindow()
{
WindowSystemCommands.MaximizeWindow(this);
}
[RelayCommand]
private void RestoreWindow()
{
WindowSystemCommands.RestoreWindow(this);
}
[RelayCommand]
private void MaximizeOrRestoreWindow()
{
WindowSystemCommands.MaximizeOrRestoreWindow(this);
}
[RelayCommand]
private void ShowSystemMenu()
{
WindowSystemCommands.ShowSystemMenu(this);
}
}

View File

@ -0,0 +1,370 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:conv="clr-namespace:MicaSetup.Design.Converters"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:MicaSetup.Design.Controls"
xmlns:markups="clr-namespace:MicaSetup.Design.Markups">
<Color x:Key="SystemAltHighColor">#FF000000</Color>
<Color x:Key="SystemAltLowColor">#33000000</Color>
<Color x:Key="SystemAltMediumColor">#99000000</Color>
<Color x:Key="SystemAltMediumHighColor">#CC000000</Color>
<Color x:Key="SystemAltMediumLowColor">#66000000</Color>
<Color x:Key="SystemBaseHighColor">#FFFFFFFF</Color>
<Color x:Key="SystemBaseLowColor">#33FFFFFF</Color>
<Color x:Key="SystemBaseMediumColor">#99FFFFFF</Color>
<Color x:Key="SystemBaseMediumHighColor">#CCFFFFFF</Color>
<Color x:Key="SystemBaseMediumLowColor">#66FFFFFF</Color>
<Color x:Key="SystemChromeAltLowColor">#FFF2F2F2</Color>
<Color x:Key="SystemChromeBlackHighColor">#FF000000</Color>
<Color x:Key="SystemChromeBlackLowColor">#33000000</Color>
<Color x:Key="SystemChromeBlackMediumLowColor">#66000000</Color>
<Color x:Key="SystemChromeBlackMediumColor">#CC000000</Color>
<Color x:Key="SystemChromeDisabledHighColor">#FF333333</Color>
<Color x:Key="SystemChromeDisabledLowColor">#FF858585</Color>
<Color x:Key="SystemChromeHighColor">#FF767676</Color>
<Color x:Key="SystemChromeLowColor">#FF171717</Color>
<Color x:Key="SystemChromeMediumColor">#FF1F1F1F</Color>
<Color x:Key="SystemChromeMediumHighColor">#FF323232</Color>
<Color x:Key="SystemChromeMediumLowColor">#FF2B2B2B</Color>
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="SystemChromeGrayColor">#FF767676</Color>
<Color x:Key="SystemListLowColor">#19FFFFFF</Color>
<Color x:Key="SystemListMediumColor">#33FFFFFF</Color>
<Color x:Key="SystemErrorTextColor">#FFF000</Color>
<SolidColorBrush x:Key="ButtonBackground" Color="{StaticResource SystemBaseLowColor}" />
<SolidColorBrush x:Key="ButtonBackgroundPointerOver"
Opacity="0.1"
Color="{StaticResource SystemBaseHighColor}" />
<SolidColorBrush x:Key="ButtonBackgroundPressed" Color="{StaticResource SystemBaseMediumLowColor}" />
<SolidColorBrush x:Key="ButtonBackgroundDisabled" Color="{StaticResource SystemBaseLowColor}" />
<SolidColorBrush x:Key="ButtonForeground" Color="{StaticResource SystemBaseHighColor}" />
<SolidColorBrush x:Key="ButtonForegroundPointerOver" Color="{StaticResource SystemBaseHighColor}" />
<SolidColorBrush x:Key="ButtonForegroundPressed" Color="{StaticResource SystemBaseHighColor}" />
<SolidColorBrush x:Key="ButtonForegroundDisabled" Color="{StaticResource SystemBaseMediumLowColor}" />
<SolidColorBrush x:Key="ButtonBorderBrush" Color="Transparent" />
<SolidColorBrush x:Key="ButtonBorderBrushPointerOver" Color="Transparent" />
<SolidColorBrush x:Key="ButtonBorderBrushPressed" Color="Transparent" />
<SolidColorBrush x:Key="ButtonBorderBrushDisabled" Color="Transparent" />
<conv:AddConverter x:Key="AddConverter" />
<conv:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<conv:BoolNegationConverter x:Key="BoolNegationConverter" />
<conv:NullToBoolConverter x:Key="NullToBoolConverter" />
<conv:NullToBoolConverter x:Key="NullToBoolInvertedConverter" IsInverted="True" />
<Style x:Key="DefaultWindowXStyle" TargetType="{x:Type local:WindowX}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="#313A4C" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="White" />
<Setter Property="local:WindowXCaption.Background" Value="Transparent" />
<Setter Property="local:WindowXCaption.Foreground">
<Setter.Value>
<SolidColorBrush Color="{DynamicResource TextFillColorPrimary}" />
</Setter.Value>
</Setter>
<Setter Property="local:WindowXCaption.Height" Value="36" />
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome CaptionHeight="0"
CornerRadius="8"
GlassFrameThickness="-1"
NonClientFrameEdges="None"
ResizeBorderThickness="3"
UseAeroCaptionButtons="False" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:WindowX}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid x:Name="PART_GrdMain">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid x:Name="PART_GrdCaption"
Height="{Binding Path=(local:WindowXCaption.Height), RelativeSource={RelativeSource AncestorType=Window}}"
Background="{Binding Path=(local:WindowXCaption.Background), RelativeSource={RelativeSource AncestorType=Window}}">
<i:Interaction.Behaviors>
<local:WindowTitleHeaderBehavior />
</i:Interaction.Behaviors>
<Grid Margin="{Binding Path=(local:WindowXCaption.Padding), RelativeSource={RelativeSource AncestorType=Window}}">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel x:Name="PART_StkTitle"
VerticalAlignment="Stretch"
Orientation="Horizontal">
<Image x:Name="PART_ImgIcon"
Width="{TemplateBinding FontSize,
Converter={StaticResource AddConverter},
ConverterParameter={markups:Double Value=5}}"
Height="{TemplateBinding FontSize,
Converter={StaticResource AddConverter},
ConverterParameter={markups:Double Value=5}}"
Margin="8,0,0,0"
VerticalAlignment="Center"
RenderOptions.BitmapScalingMode="HighQuality"
Source="{Binding Path=Icon, RelativeSource={RelativeSource AncestorType=Window}}">
<i:Interaction.Behaviors>
<local:WindowTitleIconBehavior />
</i:Interaction.Behaviors>
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers />
</Style>
</Image.Style>
</Image>
<TextBlock x:Name="PART_TxtTitle"
Margin="5,0,0,0"
VerticalAlignment="Center"
Foreground="{Binding Path=(local:WindowXCaption.Foreground), RelativeSource={RelativeSource AncestorType=Window}}"
Text="{TemplateBinding Title}" />
</StackPanel>
<ContentControl x:Name="PART_CpHeader"
Content="{Binding Path=(local:WindowXCaption.Header), RelativeSource={RelativeSource AncestorType=Window}}"
TextBlock.Foreground="{Binding Path=(local:WindowXCaption.Foreground), RelativeSource={RelativeSource AncestorType=Window}}"
TextElement.Foreground="{Binding Path=(local:WindowXCaption.Foreground), RelativeSource={RelativeSource AncestorType=Window}}"
Visibility="Visible" />
<ContentControl x:Name="PART_CcExtend"
Grid.Column="1"
Content="{Binding Path=(local:WindowXCaption.ExtendControl), RelativeSource={RelativeSource AncestorType=Window}}" />
<Button x:Name="PART_BtnMinimize"
Grid.Column="3"
Command="{Binding MinimizeWindowCommand, RelativeSource={RelativeSource TemplatedParent}}"
CommandParameter="{Binding RelativeSource={x:Static RelativeSource.TemplatedParent}}"
Style="{Binding Path=(local:WindowXCaption.MinimizeButtonStyle), RelativeSource={RelativeSource AncestorType=Window}}" />
<Button x:Name="PART_BtnMaximize"
Grid.Column="4"
Command="{Binding MaximizeOrRestoreWindowCommand, RelativeSource={RelativeSource TemplatedParent}}"
CommandParameter="{Binding RelativeSource={x:Static RelativeSource.TemplatedParent}}"
Style="{Binding Path=(local:WindowXCaption.MaximizeButtonStyle), RelativeSource={RelativeSource AncestorType=Window}}" />
<Button x:Name="PART_BtnClose"
Grid.Column="5"
Command="{Binding CloseWindowCommand, RelativeSource={RelativeSource TemplatedParent}}"
CommandParameter="{Binding RelativeSource={x:Static RelativeSource.TemplatedParent}}"
IsEnabled="{Binding Path=(local:WindowXCaption.DisableCloseButton), RelativeSource={RelativeSource AncestorType=Window}, Converter={StaticResource BoolNegationConverter}}"
Style="{Binding Path=(local:WindowXCaption.CloseButtonStyle), RelativeSource={RelativeSource AncestorType=Window}}" />
</Grid>
</Grid>
<AdornerDecorator Grid.Row="1" Panel.ZIndex="-1">
<ContentPresenter />
</AdornerDecorator>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="ResizeMode" Value="NoResize">
<Setter TargetName="PART_BtnMinimize" Property="Visibility" Value="Collapsed" />
<Setter TargetName="PART_BtnMaximize" Property="Visibility" Value="Collapsed" />
</Trigger>
<Trigger Property="ResizeMode" Value="CanMinimize">
<Setter TargetName="PART_BtnMaximize" Property="Visibility" Value="Collapsed" />
</Trigger>
<Trigger Property="WindowState" Value="Maximized">
<Setter TargetName="PART_GrdMain" Property="Margin" Value="5" />
</Trigger>
<Trigger Property="IsActivated" Value="false">
<Setter TargetName="PART_TxtTitle" Property="Opacity" Value="0.9" />
</Trigger>
<DataTrigger Binding="{Binding Path=(local:WindowXCaption.Icon), RelativeSource={RelativeSource Self}, Mode=OneWay, Converter={StaticResource NullToBoolConverter}}" Value="True">
<Setter TargetName="PART_ImgIcon" Property="Source" Value="{Binding Path=Icon, RelativeSource={RelativeSource AncestorType=Window}}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=(local:WindowXCaption.Icon), RelativeSource={RelativeSource Self}, Mode=OneWay, Converter={StaticResource NullToBoolInvertedConverter}}" Value="True">
<Setter TargetName="PART_ImgIcon" Property="Source" Value="{Binding Path=(local:WindowXCaption.Icon), RelativeSource={RelativeSource AncestorType=Window}}" />
</DataTrigger>
<DataTrigger Binding="{Binding Icon, RelativeSource={RelativeSource Self}, Mode=OneWay, Converter={StaticResource NullToBoolConverter}}" Value="True">
<Setter TargetName="PART_ImgIcon" Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=(local:WindowXCaption.Header), RelativeSource={RelativeSource Self}, Mode=OneWay, Converter={StaticResource NullToBoolInvertedConverter}}" Value="True">
<Setter TargetName="PART_StkTitle" Property="Visibility" Value="Collapsed" />
<Setter TargetName="PART_CpHeader" Property="Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=(local:WindowXCaption.HideBasicButtons), RelativeSource={RelativeSource Self}, Mode=OneWay}" Value="True">
<Setter TargetName="PART_BtnMinimize" Property="Visibility" Value="Collapsed" />
<Setter TargetName="PART_BtnMaximize" Property="Visibility" Value="Collapsed" />
<Setter TargetName="PART_BtnClose" Property="Visibility" Value="Collapsed" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="local:WindowXCaption.MaximizeButtonStyle">
<Setter.Value>
<Style BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">
<Setter Property="Background" Value="{Binding Path=(local:WindowXCaption.Background), RelativeSource={RelativeSource AncestorType=Window}}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Height" Value="35" />
<Setter Property="Width" Value="{Binding ActualHeight, Converter={StaticResource AddConverter}, ConverterParameter={markups:Double Value=10}, RelativeSource={RelativeSource Self}}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="FontSize" Value="20" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="true">
<Path Width="10"
Height="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M0.025 0 L0.975 0 M0.025 1 L0.975 1 M1 0.975 L1 0.025 M0 0.025 L0 0.975"
Stretch="Uniform"
StrokeThickness="1">
<Path.Stroke>
<SolidColorBrush Color="{DynamicResource TextFillColorPrimary}" />
</Path.Stroke>
<i:Interaction.Behaviors>
<local:WindowRestorePathBehavior />
</i:Interaction.Behaviors>
</Path>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPointerOver}" />
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPressed}" />
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
<Setter Property="local:WindowXCaption.MinimizeButtonStyle">
<Setter.Value>
<Style BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="true">
<Path Width="10"
Height="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M1 0.5 L0 0.5"
Stretch="Uniform"
StrokeThickness="1">
<Path.Stroke>
<SolidColorBrush Color="{DynamicResource TextFillColorPrimary}" />
</Path.Stroke>
</Path>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="{DynamicResource WindowControlButtonColorDefault}" />
</Setter.Value>
</Setter>
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPointerOver}" />
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPressed}" />
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Height" Value="35" />
<Setter Property="Width" Value="{Binding ActualHeight, Converter={StaticResource AddConverter}, ConverterParameter={markups:Double Value=10}, RelativeSource={RelativeSource Self}}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="FontSize" Value="20" />
<Setter Property="Background" Value="{Binding Path=(local:WindowXCaption.Background), RelativeSource={RelativeSource AncestorType=Window}}" />
</Style>
</Setter.Value>
</Setter>
<Setter Property="local:WindowXCaption.CloseButtonStyle">
<Setter.Value>
<Style BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">
<Setter Property="Background" Value="{Binding Path=(local:WindowXCaption.Background), RelativeSource={RelativeSource AncestorType=Window}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="true">
<Path x:Name="borderPath"
Width="10"
Height="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M0,0 L1,1 M0,1 L1,0"
Stretch="Uniform"
StrokeThickness="1">
<Path.Stroke>
<SolidColorBrush Color="{DynamicResource TextFillColorPrimary}" />
</Path.Stroke>
</Path>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#CCC53434" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPointerOver}" />
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
<Setter TargetName="borderPath" Property="Stroke" Value="White" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#DE6D6C" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPressed}" />
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Height" Value="35" />
<Setter Property="Width" Value="{Binding ActualHeight, Converter={StaticResource AddConverter}, ConverterParameter={markups:Double Value=10}, RelativeSource={RelativeSource Self}}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="FontSize" Value="20" />
</Style>
</Setter.Value>
</Setter>
</Style>
<Style BasedOn="{StaticResource DefaultWindowXStyle}" TargetType="{x:Type local:WindowX}" />
</ResourceDictionary>

View File

@ -0,0 +1,189 @@
using System.Windows;
using System.Windows.Media;
namespace MicaSetup.Design.Controls;
public sealed class WindowXCaption
{
public static ImageSource GetIcon(DependencyObject obj)
{
return (ImageSource)obj.GetValue(IconProperty);
}
public static void SetIcon(DependencyObject obj, ImageSource value)
{
obj.SetValue(IconProperty, value);
}
public static readonly DependencyProperty IconProperty =
DependencyProperty.RegisterAttached("Icon", typeof(ImageSource), typeof(WindowXCaption));
public static Thickness GetPadding(DependencyObject obj)
{
return (Thickness)obj.GetValue(PaddingProperty);
}
public static void SetPadding(DependencyObject obj, Thickness value)
{
obj.SetValue(PaddingProperty, value);
}
public static readonly DependencyProperty PaddingProperty =
DependencyProperty.RegisterAttached("Padding", typeof(Thickness), typeof(WindowXCaption));
public static double GetHeight(DependencyObject obj)
{
return (double)obj.GetValue(HeightProperty);
}
public static void SetHeight(DependencyObject obj, double value)
{
obj.SetValue(HeightProperty, value);
}
public static readonly DependencyProperty HeightProperty =
DependencyProperty.RegisterAttached("Height", typeof(double), typeof(WindowXCaption));
public static Brush GetForeground(DependencyObject obj)
{
return (Brush)obj.GetValue(ForegroundProperty);
}
public static void SetForeground(DependencyObject obj, Brush value)
{
obj.SetValue(ForegroundProperty, value);
}
public static readonly DependencyProperty ForegroundProperty =
DependencyProperty.RegisterAttached("Foreground", typeof(Brush), typeof(WindowXCaption));
public static Brush GetBackground(DependencyObject obj)
{
return (Brush)obj.GetValue(BackgroundProperty);
}
public static void SetBackground(DependencyObject obj, Brush value)
{
obj.SetValue(BackgroundProperty, value);
}
public static readonly DependencyProperty BackgroundProperty =
DependencyProperty.RegisterAttached("Background", typeof(Brush), typeof(WindowXCaption));
public static Style GetMinimizeButtonStyle(DependencyObject obj)
{
return (Style)obj.GetValue(MinimizeButtonStyleProperty);
}
public static void SetMinimizeButtonStyle(DependencyObject obj, Style value)
{
obj.SetValue(MinimizeButtonStyleProperty, value);
}
public static readonly DependencyProperty MinimizeButtonStyleProperty =
DependencyProperty.RegisterAttached("MinimizeButtonStyle", typeof(Style), typeof(WindowXCaption));
public static Style GetMaximizeButtonStyle(DependencyObject obj)
{
return (Style)obj.GetValue(MaximizeButtonStyleProperty);
}
public static void SetMaximizeButtonStyle(DependencyObject obj, Style value)
{
obj.SetValue(MaximizeButtonStyleProperty, value);
}
public static readonly DependencyProperty MaximizeButtonStyleProperty =
DependencyProperty.RegisterAttached("MaximizeButtonStyle", typeof(Style), typeof(WindowXCaption));
public static Style GetCloseButtonStyle(DependencyObject obj)
{
return (Style)obj.GetValue(CloseButtonStyleProperty);
}
public static void SetCloseButtonStyle(DependencyObject obj, Style value)
{
obj.SetValue(CloseButtonStyleProperty, value);
}
public static readonly DependencyProperty CloseButtonStyleProperty =
DependencyProperty.RegisterAttached("CloseButtonStyle", typeof(Style), typeof(WindowXCaption));
public static Style GetFullScreenButtonStyle(DependencyObject obj)
{
return (Style)obj.GetValue(FullScreenButtonStyleProperty);
}
public static void SetFullScreenButtonStyle(DependencyObject obj, Style value)
{
obj.SetValue(FullScreenButtonStyleProperty, value);
}
public static readonly DependencyProperty FullScreenButtonStyleProperty =
DependencyProperty.RegisterAttached("FullScreenButtonStyle", typeof(Style), typeof(WindowXCaption));
public static object GetHeader(DependencyObject obj)
{
return obj.GetValue(HeaderProperty);
}
public static void SetHeader(DependencyObject obj, object value)
{
obj.SetValue(HeaderProperty, value);
}
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.RegisterAttached("Header", typeof(object), typeof(WindowXCaption));
public static UIElement GetExtendControl(DependencyObject obj)
{
return (UIElement)obj.GetValue(ExtendControlProperty);
}
public static void SetExtendControl(DependencyObject obj, UIElement value)
{
obj.SetValue(ExtendControlProperty, value);
}
public static readonly DependencyProperty ExtendControlProperty =
DependencyProperty.RegisterAttached("ExtendControl", typeof(UIElement), typeof(WindowXCaption));
public static bool GetDisableCloseButton(DependencyObject obj)
{
return (bool)obj.GetValue(DisableCloseButtonProperty);
}
public static void SetDisableCloseButton(DependencyObject obj, bool value)
{
obj.SetValue(DisableCloseButtonProperty, value);
}
public static readonly DependencyProperty DisableCloseButtonProperty =
DependencyProperty.RegisterAttached("DisableCloseButton", typeof(bool), typeof(WindowXCaption));
public static bool GetHideBasicButtons(DependencyObject obj)
{
return (bool)obj.GetValue(HideBasicButtonsProperty);
}
public static void SetHideBasicButtons(DependencyObject obj, bool value)
{
obj.SetValue(HideBasicButtonsProperty, value);
}
public static readonly DependencyProperty HideBasicButtonsProperty =
DependencyProperty.RegisterAttached("HideBasicButtons", typeof(bool), typeof(WindowXCaption));
public static bool GetShowFullScreenButton(DependencyObject obj)
{
return (bool)obj.GetValue(ShowFullScreenButtonProperty);
}
public static void SetShowFullScreenButton(DependencyObject obj, bool value)
{
obj.SetValue(ShowFullScreenButtonProperty, value);
}
public static readonly DependencyProperty ShowFullScreenButtonProperty =
DependencyProperty.RegisterAttached("ShowFullScreenButton", typeof(bool), typeof(WindowXCaption), new(false));
}

View File

@ -0,0 +1,51 @@
using System;
using System.Globalization;
namespace MicaSetup.Design.Converters;
public abstract class BoolToValueConverterBase<T, TConverter> : SingletonConverterBase<TConverter> where TConverter : new()
{
public abstract T TrueValue { get; set; }
public abstract T FalseValue { get; set; }
public abstract bool IsInverted { get; set; }
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var returnValue = this.FalseValue;
if (value is bool boolValue)
{
if (this.IsInverted)
{
returnValue = boolValue ? this.FalseValue : this.TrueValue;
}
else
{
returnValue = boolValue ? this.TrueValue : this.FalseValue;
}
}
return returnValue!;
}
protected override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool returnValue = false;
if (value != null)
{
if (this.IsInverted)
{
returnValue = value.Equals(this.FalseValue);
}
else
{
returnValue = value.Equals(this.TrueValue);
}
}
return returnValue;
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
public abstract class ConverterBase : DependencyObject, IValueConverter
{
public PreferredCulture PreferredCulture { get; set; } = ValueConvertersConfig.DefaultPreferredCulture;
protected abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture);
protected virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException($"Converter '{this.GetType().Name}' does not support backward conversion.");
}
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return this.Convert(value, targetType, parameter, this.SelectCulture(() => culture));
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return this.ConvertBack(value, targetType, parameter, this.SelectCulture(() => culture));
}
private CultureInfo SelectCulture(Func<CultureInfo> converterCulture)
{
return PreferredCulture switch
{
PreferredCulture.CurrentCulture => CultureInfo.CurrentCulture,
PreferredCulture.CurrentUICulture => CultureInfo.CurrentUICulture,
_ => converterCulture(),
};
}
public static readonly object UnsetValue = DependencyProperty.UnsetValue;
}

View File

@ -0,0 +1,21 @@
using System.Globalization;
namespace MicaSetup.Design.Converters;
public enum PreferredCulture
{
/// <summary>
/// Uses the default culture provided by <seealso cref="IValueConverter"/>.
/// </summary>
ConverterCulture,
/// <summary>
/// Overrides the default converter culture with <seealso cref="CultureInfo.CurrentCulture"/>.
/// </summary>
CurrentCulture,
/// <summary>
/// Overrides the default converter culture with <seealso cref="CultureInfo.CurrentUICulture"/>.
/// </summary>
CurrentUICulture,
}

View File

@ -0,0 +1,15 @@
using System.Windows;
using Property = System.Windows.DependencyProperty;
namespace MicaSetup.Design.Converters;
#pragma warning disable WPF0011
#pragma warning disable WPF0015
internal static class PropertyHelper
{
public static Property Create<T, TParent>(string name, T defaultValue) =>
Property.Register(name, typeof(T), typeof(TParent), new PropertyMetadata(defaultValue));
public static Property Create<T, TParent>(string name) => Create<T, TParent>(name, default!);
}

View File

@ -0,0 +1,35 @@
using System;
using System.Globalization;
using Property = System.Windows.DependencyProperty;
namespace MicaSetup.Design.Converters;
public abstract class ReversibleValueToBoolConverterBase<T, TConverter> : ValueToBoolConverterBase<T, TConverter>
where TConverter : new()
{
public abstract T FalseValue { get; set; }
public bool BaseOnFalseValue
{
get { return (bool)this.GetValue(BaseOnFalseValueProperty); }
set { this.SetValue(BaseOnFalseValueProperty, value); }
}
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!this.BaseOnFalseValue)
{
return base.Convert(value, targetType, parameter, culture);
}
var falseValue = this.FalseValue;
return !Equals(value, falseValue) ^ this.IsInverted;
}
protected override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return true.Equals(value) ^ this.IsInverted ? this.TrueValue! : this.FalseValue!;
}
public static readonly Property BaseOnFalseValueProperty = PropertyHelper.Create<bool, ValueToBoolConverterBase<T, TConverter>>(nameof(BaseOnFalseValueProperty));
}

View File

@ -0,0 +1,15 @@
using System;
using System.Threading;
namespace MicaSetup.Design.Converters;
public abstract class SingletonConverterBase<TConverter> : ConverterBase
where TConverter : new()
{
private static readonly Lazy<TConverter> InstanceConstructor = new(() =>
{
return new TConverter();
}, LazyThreadSafetyMode.PublicationOnly);
public static TConverter Instance => InstanceConstructor.Value;
}

View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows.Data;
using System.Windows.Markup;
namespace MicaSetup.Design.Converters;
[ContentProperty(nameof(Converters))]
[ValueConversion(typeof(object), typeof(object))]
public class ValueConverterGroup : SingletonConverterBase<ValueConverterGroup>
{
public List<IValueConverter> Converters { get; set; } = new List<IValueConverter>();
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (this.Converters is IEnumerable<IValueConverter> converters)
{
var language = culture;
return converters.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, language));
}
return UnsetValue;
}
protected override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (this.Converters is IEnumerable<IValueConverter> converters)
{
var language = culture;
return converters.Reverse<IValueConverter>().Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, language));
}
return UnsetValue;
}
}

View File

@ -0,0 +1,6 @@
namespace MicaSetup.Design.Converters;
public static class ValueConvertersConfig
{
public static PreferredCulture DefaultPreferredCulture { get; set; } = PreferredCulture.ConverterCulture;
}

View File

@ -0,0 +1,25 @@
using System;
using System.Globalization;
using Property = System.Windows.DependencyProperty;
namespace MicaSetup.Design.Converters;
public abstract class ValueToBoolConverterBase<T, TConverter> : ConverterBase
where TConverter : new()
{
public abstract T TrueValue { get; set; }
public bool IsInverted
{
get { return (bool)this.GetValue(IsInvertedProperty); }
set { this.SetValue(IsInvertedProperty, value); }
}
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var trueValue = this.TrueValue;
return Equals(value, trueValue) ^ this.IsInverted;
}
public static readonly Property IsInvertedProperty = PropertyHelper.Create<bool, ValueToBoolConverterBase<T, TConverter>>(nameof(IsInverted));
}

View File

@ -0,0 +1,20 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(double), typeof(double))]
public class AddConverter : SingletonConverterBase<AddConverter>
{
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (double.TryParse(value?.ToString(), NumberStyles.Any, culture, out var basis)
&& double.TryParse(parameter?.ToString(), NumberStyles.Any, culture, out var subtract))
{
return basis + subtract;
}
return UnsetValue;
}
}

View File

@ -0,0 +1,8 @@
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(bool), typeof(bool))]
public class BoolInverter : BoolNegationConverter
{
}

View File

@ -0,0 +1,14 @@
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(bool), typeof(bool))]
public class BoolNegationConverter : BoolToValueConverter<bool>
{
public BoolNegationConverter()
{
this.TrueValue = true;
this.FalseValue = false;
this.IsInverted = true;
}
}

View File

@ -0,0 +1,9 @@
using System.Windows.Data;
using System.Windows.Media;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(bool), typeof(Brush))]
public class BoolToBrushConverter : BoolToValueConverter<Brush>
{
}

View File

@ -0,0 +1,8 @@
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(bool), typeof(double))]
public class BoolToDoubleConverter : BoolToValueConverter<double>
{
}

View File

@ -0,0 +1,14 @@
using System.Windows;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(bool), typeof(FontWeight))]
public class BoolToFontWeightConverter : BoolToValueConverter<FontWeight>
{
public BoolToFontWeightConverter()
{
this.TrueValue = FontWeights.Bold;
this.FalseValue = FontWeights.Normal;
}
}

View File

@ -0,0 +1,8 @@
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(bool), typeof(object))]
public class BoolToObjectConverter : BoolToValueConverter<object>
{
}

View File

@ -0,0 +1,8 @@
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(bool), typeof(string))]
public class BoolToStringConverter : BoolToValueConverter<string>
{
}

View File

@ -0,0 +1,9 @@
using System.Windows;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(bool), typeof(Style))]
public class BoolToStyleConverter : BoolToValueConverter<Style>
{
}

View File

@ -0,0 +1,9 @@
using System.Windows;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(bool), typeof(Thickness))]
public class BoolToThicknessConverter : BoolToValueConverter<Thickness>
{
}

View File

@ -0,0 +1,44 @@
using System.Windows;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(bool), typeof(object))]
public class BoolToValueConverter<T> : BoolToValueConverterBase<T, BoolToValueConverter<T>>
{
public static readonly DependencyProperty TrueValueProperty = DependencyProperty.Register(
nameof(TrueValue),
typeof(T),
typeof(BoolToValueConverter<T>),
new PropertyMetadata(default(T)));
public static readonly DependencyProperty FalseValueProperty = DependencyProperty.Register(
nameof(FalseValue),
typeof(T),
typeof(BoolToValueConverter<T>),
new PropertyMetadata(default(T)));
public static readonly DependencyProperty IsInvertedProperty = DependencyProperty.Register(
nameof(IsInverted),
typeof(bool),
typeof(BoolToValueConverter<T>),
new PropertyMetadata(false));
public override T TrueValue
{
get => (T)this.GetValue(TrueValueProperty);
set => this.SetValue(TrueValueProperty, value);
}
public override T FalseValue
{
get => (T)this.GetValue(FalseValueProperty);
set => this.SetValue(FalseValueProperty, value);
}
public override bool IsInverted
{
get => (bool)this.GetValue(IsInvertedProperty);
set => this.SetValue(IsInvertedProperty, value);
}
}

View File

@ -0,0 +1,14 @@
using System.Windows;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(bool), typeof(Visibility))]
public class BoolToVisibilityConverter : BoolToValueConverter<Visibility>
{
public BoolToVisibilityConverter()
{
this.TrueValue = Visibility.Visible;
this.FalseValue = Visibility.Collapsed;
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(string), typeof(SolidColorBrush))]
public sealed class ColorToSolidBrushConverter : ConverterBase
{
public bool Freeze { get; set; } = true;
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Color color)
{
SolidColorBrush brush = new(color);
if (Freeze)
{
brush.Freeze();
}
return brush;
}
return value;
}
protected override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is SolidColorBrush brush)
{
return brush.Color;
}
return default(Color);
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Diagnostics;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(object), typeof(object))]
public class DebugConverter : SingletonConverterBase<DebugConverter>
{
protected override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Debug.WriteLine("DebugConverter.Convert(value={0}, targetType={1}, parameter={2}, culture={3}",
value ?? "null",
(object)targetType ?? "null",
parameter ?? "null",
(object)culture ?? "null");
return value!;
}
protected override object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Debug.WriteLine("DebugConverter.ConvertBack(value={0}, targetType={1}, parameter={2}, culture={3}",
value ?? "null",
(object)targetType ?? "null",
parameter ?? "null",
(object)culture ?? "null");
return value!;
}
}

View File

@ -0,0 +1,8 @@
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(double), typeof(bool))]
public class DoubleToBoolConverter : ValueToBoolConverter<double>
{
}

View File

@ -0,0 +1,36 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(object), typeof(bool))]
public class EnumToBoolConverter : SingletonConverterBase<EnumToBoolConverter>
{
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter is string parameterString)
{
if (Enum.IsDefined(value.GetType(), value) == false)
{
return UnsetValue;
}
var parameterValue = Enum.Parse(value.GetType(), parameterString);
return parameterValue.Equals(value);
}
return UnsetValue;
}
protected override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter is string parameterString)
{
return Enum.Parse(targetType, parameterString);
}
return UnsetValue;
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(object), typeof(object))]
public class EnumToObjectConverter : StringToObjectConverter
{
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return UnsetValue;
}
var type = value.GetType();
var key = Enum.GetName(type, value);
return base.Convert(key, targetType, parameter, culture);
}
}

View File

@ -0,0 +1,26 @@
using System;
using System.Collections;
using System.Globalization;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(IEnumerable), typeof(object))]
public class FirstOrDefaultConverter : SingletonConverterBase<FirstOrDefaultConverter>
{
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is IEnumerable enumerable)
{
var enumerator = enumerable.GetEnumerator();
{
if (enumerator.MoveNext())
{
return enumerator.Current;
}
}
}
return UnsetValue;
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Drawing;
using System.Globalization;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(Image), typeof(Bitmap))]
public sealed class ImageToBitmapConverter : SingletonConverterBase<ImageToBitmapConverter>
{
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Image image)
{
return new Bitmap(image);
}
else if (value is Bitmap)
{
return value;
}
return null!;
}
}

View File

@ -0,0 +1,8 @@
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(int), typeof(bool))]
public class IntegerToBoolConverter : ValueToBoolConverter<int>
{
}

View File

@ -0,0 +1,8 @@
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(bool), typeof(bool))]
public class InvertedBoolConverter : BoolNegationConverter
{
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(IEnumerable), typeof(bool))]
[ValueConversion(typeof(object), typeof(bool))]
public class IsEmptyConverter : SingletonConverterBase<IsEmptyConverter>
{
public static readonly DependencyProperty IsInvertedProperty = DependencyProperty.Register(
nameof(IsInverted),
typeof(bool),
typeof(IsEmptyConverter),
new PropertyMetadata(false));
public bool IsInverted
{
get => (bool)this.GetValue(IsInvertedProperty);
set => this.SetValue(IsInvertedProperty, value);
}
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is IEnumerable enumerable)
{
var hasAtLeastOne = enumerable.GetEnumerator().MoveNext();
return (hasAtLeastOne == false) ^ this.IsInverted;
}
return true ^ this.IsInverted;
}
}

View File

@ -0,0 +1,54 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
internal sealed class MathRoundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double? valueNullable = default;
if (value is float @float)
{
valueNullable = @float;
}
else if (value is double @double)
{
valueNullable = @double;
}
else if (value is int @int)
{
valueNullable = @int;
}
if (valueNullable != null)
{
int dec = default;
if (parameter is null)
{
dec = default;
}
else if (parameter is int decFromInt)
{
dec = decFromInt;
}
else if (parameter is string decString)
{
if (int.TryParse(decString, out int decFromString))
{
dec = decFromString;
}
}
return Math.Round(valueNullable.Value, dec).ToString();
}
return value?.ToString()!;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(object), typeof(bool))]
public class NullToBoolConverter : SingletonConverterBase<NullToBoolConverter>
{
public static readonly DependencyProperty IsInvertedProperty = DependencyProperty.Register(
nameof(IsInverted),
typeof(bool),
typeof(NullToBoolConverter),
new PropertyMetadata(false));
public bool IsInverted
{
get { return (bool)this.GetValue(IsInvertedProperty); }
set { this.SetValue(IsInvertedProperty, value); }
}
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ^ this.IsInverted;
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
internal class ProgressBarIndeterminateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double @double && @double < 0d)
{
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
internal class ProgressBarValueVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double @double && @double < 0d)
{
return Visibility.Collapsed;
}
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(string), typeof(bool))]
public sealed class StringEqualityConverter : SingletonConverterBase<StringEqualityConverter>
{
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string inputString)
{
return inputString == parameter as string;
}
return value;
}
protected override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool isChecked = (bool)value;
if (!isChecked)
{
return string.Empty;
}
return (parameter as string) ?? string.Empty;
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(string), typeof(bool))]
public class StringIsNotNullOrEmptyConverter : StringIsNullOrEmptyConverter
{
public StringIsNotNullOrEmptyConverter()
{
this.IsInverted = true;
}
}
[ValueConversion(typeof(string), typeof(bool))]
public class StringIsNullOrEmptyConverter : SingletonConverterBase<StringIsNotNullOrEmptyConverter>
{
public static readonly DependencyProperty IsInvertedProperty = DependencyProperty.Register(
nameof(IsInverted),
typeof(bool),
typeof(StringIsNullOrEmptyConverter),
new PropertyMetadata(false));
public bool IsInverted
{
get { return (bool)this.GetValue(IsInvertedProperty); }
set { this.SetValue(IsInvertedProperty, value); }
}
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType is null)
{
throw new ArgumentNullException(nameof(targetType));
}
if (this.IsInverted)
{
return !string.IsNullOrEmpty(value as string);
}
return string.IsNullOrEmpty(value as string);
}
}

View File

@ -0,0 +1,8 @@
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(string), typeof(bool))]
public class StringToBoolConverter : ValueToBoolConverter<string>
{
}

View File

@ -0,0 +1,28 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(string), typeof(Color))]
public sealed class StringToColorConverter : SingletonConverterBase<StringToColorConverter>
{
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string inputString)
{
return ColorConverter.ConvertFromString(inputString);
}
return value;
}
protected override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Color color)
{
return $"#{color.R:X2}{color.G:X2}{color.B:X2}";
}
return string.Empty;
}
}

View File

@ -0,0 +1,37 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(string), typeof(decimal))]
public class StringToDecimalConverter : SingletonConverterBase<StringToDecimalConverter>
{
private static readonly NumberStyles DefaultNumberStyles = NumberStyles.Any;
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var dec = value as decimal?;
if (dec != null)
{
return dec.Value.ToString("G", culture ?? CultureInfo.InvariantCulture);
}
if (value is string str)
{
if (decimal.TryParse(str, DefaultNumberStyles, culture ?? CultureInfo.InvariantCulture, out var result))
{
return result;
}
return result;
}
return UnsetValue;
}
protected override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return this.Convert(value, targetType, parameter, culture);
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(string), typeof(object))]
[ContentProperty(nameof(Items))]
public class StringToObjectConverter : SingletonConverterBase<StringToObjectConverter>
{
public ResourceDictionary Items { get; set; } = null!;
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string key)
{
if (this.Items != null && ContainsKey(this.Items, key))
{
return this.Items[key];
}
}
return UnsetValue;
}
private static bool ContainsKey(ResourceDictionary dict, string key)
{
return dict.Contains(key);
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(object), typeof(object))]
public class SubtractConverter : SingletonConverterBase<SubtractConverter>
{
protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (double.TryParse(value?.ToString(), NumberStyles.Any, culture, out var basis)
&& double.TryParse(parameter?.ToString(), NumberStyles.Any, culture, out var subtract))
{
return basis - subtract;
}
return UnsetValue;
}
}

View File

@ -0,0 +1,31 @@
using System.Windows.Data;
using Property = System.Windows.DependencyProperty;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(object), typeof(bool))]
public class ValueToBoolConverter<T> : ReversibleValueToBoolConverterBase<T, ValueToBoolConverter<T>>
{
public override T TrueValue
{
get => (T)this.GetValue(TrueValueProperty);
set => this.SetValue(TrueValueProperty, value);
}
public static readonly Property TrueValueProperty =
PropertyHelper.Create<T, ValueToBoolConverter<T>>(nameof(TrueValue));
public override T FalseValue
{
get => (T)this.GetValue(FalseValueProperty);
set => this.SetValue(FalseValueProperty, value);
}
public static readonly Property FalseValueProperty =
PropertyHelper.Create<T, ValueToBoolConverter<T>>(nameof(FalseValue));
}
[ValueConversion(typeof(object), typeof(bool))]
public class ValueToBoolConverter : ValueToBoolConverter<object>
{
}

View File

@ -0,0 +1,15 @@
using System.Windows;
using System.Windows.Data;
namespace MicaSetup.Design.Converters;
[ValueConversion(typeof(Visibility), typeof(string))]
public class VisibilityInverter : BoolToValueConverter<Visibility>
{
public VisibilityInverter()
{
this.TrueValue = Visibility.Visible;
this.FalseValue = Visibility.Collapsed;
this.IsInverted = true;
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Windows;
namespace MicaSetup.Design;
public sealed class GenericResourceDictionary : ResourceDictionary
{
public GenericResourceDictionary()
{
Source = new Uri($"pack://application:,,,/MicaSetup;component/Resources/Generic.xaml");
}
}

View File

@ -0,0 +1,22 @@
using Microsoft.Extensions.DependencyInjection;
namespace MicaSetup.Design.Controls;
#pragma warning disable CS8618
public class HostBuilder : IHostBuilder
{
public IApp App { get; set; }
public ServiceProvider ServiceProvider { get; set; }
public IHostBuilder CreateApp()
{
App = new App();
return this;
}
public void RunApp()
{
App?.Run();
}
}

View File

@ -0,0 +1,168 @@
using MicaSetup.Helper;
using MicaSetup.Natives;
using MicaSetup.Services;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
namespace MicaSetup.Design.Controls;
#pragma warning disable IDE0002
public static class HostBuilderExtension
{
public static IHostBuilder UseHostBuilder(this IHostBuilder builder, Action<IHostBuilder> custom)
{
custom?.Invoke(builder);
return builder;
}
public static IHostBuilder UseAsUninst(this IHostBuilder builder)
{
Option.Current.IsUninst = true;
return builder;
}
public static IHostBuilder UseLogger(this IHostBuilder builder, bool enabled = true)
{
Option.Current.Logging = enabled;
Logger.Info("Setup run started ...");
return builder;
}
public static IHostBuilder UseServices(this IHostBuilder builder, Action<IServiceCollection> service)
{
ServiceCollection serviceCollection = new();
service?.Invoke(serviceCollection);
ServiceManager.Services = builder.ServiceProvider = serviceCollection.BuildServiceProvider();
return builder;
}
public static IHostBuilder UseTempPathFork(this IHostBuilder builder)
{
if (RuntimeHelper.IsDebuggerAttached)
{
return builder;
}
TempPathForkHelper.Fork();
return builder;
}
public static IHostBuilder UseElevated(this IHostBuilder builder)
{
RuntimeHelper.EnsureElevated();
return builder;
}
public static IHostBuilder UseDpiAware(this IHostBuilder builder)
{
_ = DpiAwareHelper.SetProcessDpiAwareness();
return builder;
}
public static IHostBuilder UseSingleInstance(this IHostBuilder builder, string instanceName, Action<bool> callback = null!)
{
RuntimeHelper.CheckSingleInstance(instanceName, callback);
return builder;
}
public static IHostBuilder UseLanguage(this IHostBuilder builder, string name)
{
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = new CultureInfo(name);
return builder;
}
public static IHostBuilder UseMuiLanguage(this IHostBuilder builder)
{
MuiLanguage.SetupLanguage();
return builder;
}
public static IHostBuilder UseTheme(this IHostBuilder builder, WindowsTheme theme = WindowsTheme.Auto)
{
ThemeService.Current.SetTheme(theme);
return builder;
}
public static IHostBuilder UseFonts(this IHostBuilder builder, Action<List<MuiLanguageFont>> handler)
{
handler?.Invoke(MuiLanguage.FontSelector);
return builder;
}
public static IHostBuilder UseOptions(this IHostBuilder builder, Action<Option> handler)
{
handler?.Invoke(Option.Current);
return builder;
}
public static IHostBuilder UsePages(this IHostBuilder builder, Action<Dictionary<string, Type>> handler)
{
handler?.Invoke(ShellPageSetting.PageDict);
return builder;
}
public static IHostBuilder UseDispatcherUnhandledExceptionCatched(this IHostBuilder builder, DispatcherUnhandledExceptionEventHandler handler = null!)
{
if (builder?.App != null)
{
if (handler != null)
{
if (builder!.App is Application app)
{
app.DispatcherUnhandledException += handler;
}
}
else
{
if (builder!.App is Application app)
{
app.DispatcherUnhandledException += (object s, DispatcherUnhandledExceptionEventArgs e) =>
{
Logger.Fatal("Application.DispatcherUnhandledException", e?.Exception?.ToString()!);
e!.Handled = true;
};
}
}
}
return builder!;
}
public static IHostBuilder UseDomainUnhandledExceptionCatched(this IHostBuilder builder, UnhandledExceptionEventHandler handler = null!)
{
if (handler != null)
{
AppDomain.CurrentDomain.UnhandledException += handler;
}
else
{
AppDomain.CurrentDomain.UnhandledException += (object s, UnhandledExceptionEventArgs e) =>
{
Logger.Fatal("AppDomain.CurrentDomain.UnhandledException", e?.ExceptionObject?.ToString()!);
};
}
return builder;
}
public static IHostBuilder UseUnobservedTaskExceptionCatched(this IHostBuilder builder, EventHandler<UnobservedTaskExceptionEventArgs> handler = null!)
{
if (handler != null)
{
TaskScheduler.UnobservedTaskException += handler;
}
else
{
TaskScheduler.UnobservedTaskException += (s, e) =>
{
Logger.Fatal("TaskScheduler.UnobservedTaskException", e?.Exception?.ToString()!);
e?.SetObserved();
};
}
return builder;
}
}

View File

@ -0,0 +1,10 @@
namespace MicaSetup.Design.Controls;
public static class Hosting
{
public static IHostBuilder CreateBuilder()
{
HostBuilder builder = new();
return builder;
}
}

View File

@ -0,0 +1,13 @@
using Microsoft.Extensions.DependencyInjection;
namespace MicaSetup.Design.Controls;
public interface IHostBuilder
{
public IApp App { get; set; }
public ServiceProvider ServiceProvider { get; set; }
public IHostBuilder CreateApp();
public void RunApp();
}

View File

@ -0,0 +1,15 @@
using System;
using System.Windows.Markup;
namespace MicaSetup.Design.Markups;
[MarkupExtensionReturnType(typeof(double))]
public sealed class DoubleExtension : MarkupExtension
{
public double Value { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Value;
}
}

Some files were not shown because too many files have changed in this diff Show More