目标:
1、去掉Window自带的标题栏。
2、移动窗体。
3、实现最小化、最大/正常、退出应用、隐藏滚动条:
效果:
代码:
MauiProgram.cs代码如下:
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp()
.ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); })
.ConfigureLifecycleEvents(events =>
{
#if WINDOWS
events.AddWindows(windows => windows.OnWindowCreated(window =>
{
//window.SizeChanged += OnSizeChanged;
MauiWinUIWindow mauiwin = window as MauiWinUIWindow;
if (mauiwin == null) { return; }
//关闭扩展内容
mauiwin.ExtendsContentIntoTitleBar = false;
//mauiwin.Title = "Hello Maui";
//通过maui窗口句柄获取appwindow---
///这里有个操蛋的东西我用最新版新建的工程没法直接getappwindow所以用了文章里的方法
var wndId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(mauiwin.WindowHandle);
Microsoft.UI.Windowing.AppWindow appwin = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(wndId);
//对于OverlappedPresenter的解释文档在这个网址
//https://learn.microsoft.com/zh-tw/windows/windows-app-sdk/api/winrt/microsoft.ui.windowing.overlappedpresenter?view=windows-app-sdk-1.2
//大致就是OverlappedPresenter会设置这个窗口,这个窗口可以和其他窗口重叠,并对窗口标题栏 状态栏 工作栏进行设置,以及其他一些调整窗口的操作
//var customOverlappedPresenter = Microsoft.UI.Windowing.OverlappedPresenter.CreateForContextMenu();
//appwin.SetPresenter(customOverlappedPresenter);
}));
#endif
});
builder.Services.AddMauiBlazorWebView();
#if DEBUG
builder.Services.AddBlazorWebViewDeveloperTools();
#endif
builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
//builder.Services.AddSingleton();
// Add services to the container.
builder.Services.AddMasaBlazor();
builder.Services.AddMasaBlazor().AddI18nForMauiBlazor("wwwroot/i18n");
return builder.Build();
}
}
MainLayout.razor代码如下:
@using BlazorComponent.I18n
@using System.Globalization
@using BlazorComponent
@using BlazorComponent.Web
@using System.Diagnostics
@inherits LayoutComponentBase
@inject I18n I18n
@inject ErrorLogsDatabase errorLogsDatabase
@inject IPopupService PopupService
@inject IJSRuntime JsRuntime
YIMO
mdi-view-dashboard
@I18n.T("Home")
mdi-alert-circle-outline
@I18n.T("ErrorLog")
@I18n.T("NetWorkTool")
mdi-access-point
@I18n.T("TCPServer")
mdi-access-point
@I18n.T("TCPClient")
@*
mdi-view-dashboard
@I18n.T("Home")
mdi-alert-circle-outline
@I18n.T("ErrorLog")
mdi-access-point
@I18n.T("NetWorkTool")
*@
_drawer = !_drawer">
@*@I18n.T("ApplicationName")*@
@I18n.T("Home")
mdi-minus
@Maximize
quitDialog = true">
mdi-close
@I18n.T("Prompt")
@I18n.T("Do you want to quit the program?")
@I18n.T("Yes")
@I18n.T("No")
@Body
@code {
public static event EventHandler MouseMoveWindowEventHandler;
private CultureInfo? _culture;
private bool _showMobileMenuList;
private bool _hideAppBarNavIcon;
private bool _showSettings;
private string? _project;
bool _drawer = false;
bool _isChinee = true;
public bool IsChinese
{
get { return _isChinee; }
set
{
_isChinee = !_isChinee;
SetLanugae();
}
}
private void SetLanugae()
{
if (_isChinee)
{
I18n.SetCulture(new CultureInfo("zh-CN")); //将语言切换成zh-CN
}
else
{
I18n.SetCulture(new CultureInfo("en-US")); //将语言切换成zh-CN
}
}
private async void Callback(Exception obj)
{
await errorLogsDatabase.SaveErrorLogsAsync(new ErrorLogs() { ErrorInformation = obj.Message });
PopupService.EnqueueSnackbarAsync(obj.Message, AlertTypes.Error);
}
private bool isMouseMove = false;
//鼠标开始的位置
private double x = -1;
private double y = -1;
private void OnMouseMove(MouseEventArgs e)
{
if (e.Buttons.Equals(1) && isMouseMove) //按住鼠标移动
{
WindowsHelper.Move(Convert.ToInt32(WindowsHelper.GetPositionX - x + e.ScreenX), Convert.ToInt32(WindowsHelper.GetPositionY - y + e.ScreenY));
}
//Debug.WriteLine("当前鼠标状态:" + e.Buttons.ToString());
//Debug.WriteLine("窗体位置X:" + WindowsHelper.GetPositionX+ "Y:" + WindowsHelper.GetPositionY);
if (e.Buttons.Equals(1) && !isMouseMove)//获取鼠标点击鼠标左键的点
{
Debug.WriteLine("点击鼠标左键X:" + e.ScreenX + "Y:" + e.ScreenY);
isMouseMove = true;
//记录鼠标开始的位置
x = e.ScreenX;
y = e.ScreenY;
Debug.WriteLine("点击鼠标左键的屏幕当前位置X:" + WindowsHelper.GetPositionX + "Y:" + WindowsHelper.GetPositionY);
}
if (!e.Buttons.Equals(1) && isMouseMove)//获取鼠标左键松开的点
{
Debug.WriteLine("鼠标左键松开X:" + e.ScreenX + "Y:" + e.ScreenY);
isMouseMove = false;
x = -1;
y = -1;
Debug.WriteLine("鼠标左键松开的屏幕当前位置X:" + WindowsHelper.GetPositionX + "Y:" + WindowsHelper.GetPositionY);
}
}
private void MinimizeWindow()
{
WindowsHelper.MinimizeWindow();
}
///
/// 是否隐藏任务栏
///
private static bool isHideTaskBar = true;
///
/// 是否最大化状态
///
private static bool isMaximize = false;
private static string Maximize = "mdi-window-maximize";
///
/// 切换最大化状态
///
private static void FullOrExitFullScreen()
{
if (!isMaximize)
{
WindowsHelper.FullScreen(isHideTaskBar);
isMaximize = true;
Maximize = "mdi-window-restore";
}
else
{
WindowsHelper.ExitFullScreen();
isMaximize = false;
Maximize = "mdi-window-maximize";
}
}
///
/// 是否弹出退出应用程序对话框
///
private bool quitDialog = false;
///
/// 退出应用程序
///
private static void QuitApplication()
{
WindowsHelper.QuitApplication();
}
}
WindowsHelper.cs代码如下:
public static class WindowsHelper
{
#if WINDOWS
public static Window _Window;
public static Object? nativeWindow;
public static IntPtr windowHandle;
public static WindowId WindowId;
public static AppWindow appWindow;
public static OverlappedPresenter p;
public static OverlappedPresenter customOverlappedPresenter = Microsoft.UI.Windowing.OverlappedPresenter.CreateForContextMenu();
///
/// 判断属性是否已经初始化
///
public static bool isInit=false;
#endif
public static void InitWindowsHelper()
{
#if WINDOWS
_Window = App.Current.Windows.First();
nativeWindow = _Window.Handler.PlatformView;
windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(nativeWindow);
WindowId = Win32Interop.GetWindowIdFromWindow(windowHandle);
appWindow = AppWindow.GetFromWindowId(WindowId);
p = appWindow.Presenter as OverlappedPresenter;
isInit=true;
#endif
}
///
/// 最小化窗体
///
public static void MinimizeWindow()
{
#if WINDOWS
if (!isInit)
{
InitWindowsHelper();
}
PInvoke.User32.ShowWindow(windowHandle, PInvoke.User32.WindowShowStyle.SW_MINIMIZE);
#endif
}
///
/// 全屏显示
///
public static void FullScreen(bool isHideTaskBar)
{
#if WINDOWS
if (!isInit)
{
InitWindowsHelper();
}
if (isHideTaskBar)
{
appWindow.SetPresenter(AppWindowPresenterKind.FullScreen);
}
if (p != null)
{
p.Maximize();
}
#endif
}
///
/// 退出全屏
///
public static void ExitFullScreen()
{
#if WINDOWS
if (!isInit)
{
InitWindowsHelper();
}
switch (appWindow.Presenter)
{
case OverlappedPresenter p:
p.Restore();
break;
default:
appWindow.SetPresenter(AppWindowPresenterKind.Default);
break;
}
appWindow.SetPresenter(customOverlappedPresenter);
#endif
}
///
/// 退出应用程序
///
public static void QuitApplication()
{
Application.Current.Quit();
}
///
///
///
///
///
public static void Move(int x, int y)
{
#if WINDOWS
if (!isInit)
{
InitWindowsHelper();
}
appWindow.SetPresenter(customOverlappedPresenter);
appWindow.Move(new PointInt32(x, y));
#endif
}
///
/// 获取窗体的X
///
public static int GetPositionX
{
get
{
#if WINDOWS
if (!isInit)
{
InitWindowsHelper();
}
return appWindow.Position.X;
#else
return -1;
#endif
}
}
///
/// 获取窗体的Y
///
public static int GetPositionY
{
get
{
#if WINDOWS
if (!isInit)
{
InitWindowsHelper();
}
return appWindow.Position.Y;
#else
return -1;
#endif
}
}
}
修改Windows外的滚动条:
添加代码如下:
html::-webkit-scrollbar {
display: none;
}
app.css代码如下:
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
background-color: rebeccapurple;
}
html::-webkit-scrollbar {
display: none;
}
h1:focus {
outline: none;
}
a, .btn-link {
color: #0071c1;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.content {
padding-top: 1.1rem;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
.invalid {
outline: 1px solid red;
}
.validation-message {
color: red;
}
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
.blazor-error-boundary {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
padding: 1rem 1rem 1rem 3.7rem;
color: white;
}
.blazor-error-boundary::after {
content: "An error has occurred."
}
.status-bar-safe-area {
display: none;
}
@supports (-webkit-touch-callout: none) {
.status-bar-safe-area {
display: flex;
position: sticky;
top: 0;
height: env(safe-area-inset-top);
background-color: #f7f7f7;
width: 100%;
z-index: 1;
}
.flex-column, .navbar-brand {
padding-left: env(safe-area-inset-left);
}
}