.NET 创建无边框的跨平台应用

net,创建,无边,跨平台,应用 · 浏览次数 : 2456

小编点评

代码生成内容时需要带简单的排版,以下代码是生成窗体的代码,并带简单的排版: ```C# private static void MessageReceived(object? sender, string message) { var window = sender as PhotinoWindow; if (message.StartsWith("{\")) { var data = JsonSerializer.Deserialize<Message>(message); if (data == null || data.Command != "MouseMove") return; var left = window.Left + data.X; var top = window.Top + data.Y; if (left <= -window.Width / 2) { left = -window.Width / 2; } if (window.Top < 0) { window.Top = 0; } window.SetLeft((int)((int)left)); window.SetTop((int)((int)top)); } } ``` 代码首先定义一个window变量,类型是PhotinoWindow,然后通过window变量进行赋值,最后定义window变量的set方法,用于设置实际窗体的位置。

正文

.NET 创建无边框的跨平台应用

在创建了Photino应用程序以后我们发现它自带了一个标题栏,并且非常丑,我们现在要做的就是去掉这个很丑的自带标题栏,并且自定义一个更好看的,下面我们将用Masa Blazor提供的模板去进行实战。

安装模板

安装Masa Blazor提供的rc2的模板

dotnet new install Masa.Template::1.0.0-rc.2

image.png

创建项目

  • 打开VS2022 => 新建项目
  • 搜索到一下类别!
    image.png
  • 然后创建Gotrays名称的项目

项目结构

image.png

无边框处理

修改Program.cs代码,增加SetChromeless,设置无边框

using Gotrays;
using Microsoft.Extensions.DependencyInjection;
using Photino.Blazor;

internal class Program
{
    [STAThread]
    private static void Main(string[] args)
    {
        var appBuilder = PhotinoBlazorAppBuilder.CreateDefault(args);

        appBuilder.RootComponents.Add<App>("#app");
        appBuilder.Services.AddMasaBlazor();

        var app = appBuilder.Build();

        app.MainWindow
            .SetTitle("Photino Blazor Sample")
            .SetChromeless(true);

        AppDomain.CurrentDomain.UnhandledException += (sender, error) =>
        {
        };

        app.Run();
    }
}

启动以后的效果:
image.png
这样就完成了我们的无边框,但是也并不是直接可以使用,你会发现它无法拖动!下面我们将让他可以被拖动

完善无边框拖动

我们需要支持拖动我们的标题栏的时候带动我们的窗口!

下面开始修改代码实现这个逻辑
image.png

我们的标题栏的css的样式是m-app-bar

打开wwwroot/index.html并且修改为以下代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport"
          content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
    <title>Gotrays</title>
    <base href="/" />
    <link href="_content/Masa.Blazor/css/masa-blazor.min.css" rel="stylesheet">
    <link href="css/app.css" rel="stylesheet" />
    <link href="Gotrays.styles.css" rel="stylesheet" />
    <link href="https://cdn.masastack.com/npm/@mdi/font@7.1.96/css/materialdesignicons.min.css" rel="stylesheet">
    <link href="https://cdn.masastack.com/npm/materialicons/materialicons.css" rel="stylesheet">
    <link href="https://cdn.masastack.com/npm/fontawesome/v5.0.13/css/all.css" rel="stylesheet">
    <style>
        #app .m-app-bar {
            -webkit-app-region: drag;
        }

        html {
            overflow: hidden;
        }
    </style>
</head>

<body>

    <div class="status-bar-safe-area"></div>
    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>

    <script src="_framework/blazor.webview.js"></script>
    <script src="_content/BlazorComponent/js/blazor-component.js"></script>

    <script>
        let start = false;
        let pageX = 0;
        let pageY = 0;
        document.body.addEventListener('mousedown', evt => {
            const { target } = evt;
            const appRegion = getComputedStyle(target)['-webkit-app-region'];
            const app = document.getElementById("m-app-bar");
            app.addEventListener('mousemove', onmousemove)

            app.addEventListener("mousedown", (e) => {
                console.log(e.pageX, e.pageY);
                pageX = e.pageX;
                pageY = e.pageY;
                start = true;
            })

            app.addEventListener("mouseup", (e) => {
                start = false;
            })

            console.log("MouseDownDrag", evt, appRegion, app);
            if (appRegion === 'drag') {
                var data = {
                    Command: "MouseMove",
                    Data: {

                    }
                }
                evt.preventDefault();
                evt.stopPropagation();
            }
        });

        function onmousemove(e) {
            if (start) {
                console.log("MouseMove", e);
                var data = {
                    "Command": "MouseMove",
                    X: e.clientX - pageX,
                    Y: e.clientY - pageY
                }
                window.external.sendMessage(JSON.stringify(data));
            }

        }

    </script>
</body>

</html>

主要是添加了这个样式文件,注意的是这个相当于标记了哪个可以拖动我们的窗口的标记

    <style>
        #app .m-app-bar {
            -webkit-app-region: drag;
        }

        html {
            overflow: hidden;
        }
    </style>

下面的js的代码是为了传递窗口拖动参数实现实际的拖动


    <script>
        let start = false;
        let pageX = 0;
        let pageY = 0;
        document.body.addEventListener('mousedown', evt => {
            const { target } = evt;
            const appRegion = getComputedStyle(target)['-webkit-app-region'];
            const app = document.getElementById("m-app-bar");
            app.addEventListener('mousemove', onmousemove)

            app.addEventListener("mousedown", (e) => {
                console.log(e.pageX, e.pageY);
                pageX = e.pageX;
                pageY = e.pageY;
                start = true;
            })

            app.addEventListener("mouseup", (e) => {
                start = false;
            })

            console.log("MouseDownDrag", evt, appRegion, app);
            if (appRegion === 'drag') {
                var data = {
                    Command: "MouseMove",
                    Data: {

                    }
                }
                evt.preventDefault();
                evt.stopPropagation();
            }
        });
        function onmousemove(e) {
            if (start) {
                console.log("MouseMove", e);
                var data = {
                    "Command": "MouseMove",
                    X: e.clientX - pageX,
                    Y: e.clientY - pageY
                }
                window.external.sendMessage(JSON.stringify(data));
            }
        }

    </script>

修改Program.cs文件的代码支持拖动

using System.Diagnostics;
using Gotrays;
using Microsoft.Extensions.DependencyInjection;
using Photino.Blazor;
using PhotinoNET;
using System.Runtime.InteropServices;
using System.Text.Json;

internal class Program
{
    [STAThread]
    private static void Main(string[] args)
    {
        var appBuilder = PhotinoBlazorAppBuilder.CreateDefault(args);

        appBuilder.RootComponents.Add<App>("#app");
        appBuilder.Services.AddMasaBlazor();

        var app = appBuilder.Build();

        app.MainWindow

            .SetTitle("Photino Blazor Sample")
            .SetChromeless(true)
            .RegisterWebMessageReceivedHandler(MessageReceived);

        AppDomain.CurrentDomain.UnhandledException += (sender, error) =>
        {
        };

        app.Run();
    }

    private static void MessageReceived(object? sender, string message)
    {
        var window = sender as PhotinoWindow;
        if (message.StartsWith("{"))
        {
            var data = JsonSerializer.Deserialize<Message>(message);

            if (data == null || data.Command != "MouseMove") return;

            var left = window.Left + data.X;
            var top = window.Top + data.Y;
            if (left <= -window.Width / 2)
            {
                left = -window.Width / 2;
            }

            if (window.Top < 0)
            {
                window.Top = 0;
            }

            window.SetLeft((int)((int)left));
            window.SetTop((int)((int)top));
        }
    }

}

public class Message
{
    public string Command { get; set; }

    public double X { get; set; }

    public double Y { get; set; }
}

代码实现逻辑

  • 首先需要定义css样式,这个是相当于指定了哪个元素可以被拖动

    #app .m-app-bar {
                -webkit-app-region: drag;
            }
    
  • 然后定义js脚本,通过js去监听拖动的事件然后计算坐标,将计算好的坐标传递到c#的事件中完成实际的窗体拖动事件

            let start = false;
            let pageX = 0;
            let pageY = 0;
            document.body.addEventListener('mousedown', evt => {
                const { target } = evt;
                const appRegion = getComputedStyle(target)['-webkit-app-region'];
                const app = document.getElementById("m-app-bar");
                app.addEventListener('mousemove', onmousemove)
    
                app.addEventListener("mousedown", (e) => {
                    pageX = e.pageX;
                    pageY = e.pageY;
                    start = true;
                })
    
                app.addEventListener("mouseup", (e) => {
                    start = false;
                })
    
                if (appRegion === 'drag') {
                    var data = {
                        Command: "MouseMove",
                        Data: {
    
                        }
                    }
                    evt.preventDefault();
                    evt.stopPropagation();
                }
            });
            function onmousemove(e) {
                if (start) {
                    console.log("MouseMove", e);
                    var data = {
                        "Command": "MouseMove",
                        X: e.clientX - pageX,
                        Y: e.clientY - pageY
                    }
                    window.external.sendMessage(JSON.stringify(data));
                }
            }
    

    当id为m-app-bar的元素被鼠标按下的时候对于pageX = e.pageXpageY = e.pageYstart = true三个值赋值,start 表示鼠标按下并且启动拖动,pageXpageY则是鼠标首次按下坐标

    当元素被mousedown触发将拖动的坐标和首次点击的坐标相减,然后就可以通过window.external.sendMessage传递到c#的代码块中,先判断Message的数据是否为json,如果是则转换模型,然后计算设置实际窗体的位置即可

    
        private static void MessageReceived(object? sender, string message)
        {
            var window = sender as PhotinoWindow;
            if (message.StartsWith("{"))
            {
                var data = JsonSerializer.Deserialize<Message>(message);
    
                if (data == null || data.Command != "MouseMove") return;
    
                var left = window.Left + data.X;
                var top = window.Top + data.Y;
                if (left <= -window.Width / 2)
                {
                    left = -window.Width / 2;
                }
    
                if (window.Top < 0)
                {
                    window.Top = 0;
                }
    
                window.SetLeft((int)((int)left));
                window.SetTop((int)((int)top));
            }
        }
    

    效果

screenshots.gif

结尾

来着token的分享

技术交流群:737776595

与.NET 创建无边框的跨平台应用相似的内容:

.NET 创建无边框的跨平台应用

# .NET 创建无边框的跨平台应用 在创建了`Photino`应用程序以后我们发现它自带了一个标题栏,并且非常丑,我们现在要做的就是去掉这个很丑的自带标题栏,并且自定义一个更好看的,下面我们将用`Masa Blazor`提供的模板去进行实战。 ## 安装模板 安装`Masa Blazor`提供的`

.Net 中间件 - 新开源代码生成器 -ReZero

ReZero AP ReZero是一款.NET中间件 : 全网唯一界面操作就能生成API , 可以集成到任何.NET6+ API项目,无破坏性,也可让非.NET用户使用exe文件 ReZero生成器功能简介 1、表文档导出:支持目录导航 2、在创建表、在线建库 3、一键导入现有表 4、模版在线调试

类WPF跨平台模仿TIM

# 类WPF跨平台模仿TIM ## Avalonia是什么? Avalonia 是一个功能强大的框架,使开发人员能够使用 .NET 创建跨平台应用程序。它使用自己的渲染引擎来绘制UI控件,确保在各种平台上保持一致的外观和行为,包括Windows,macOS,Linux,Android,iOS和Web

创建.Net项目模板包

1. 准备解决方案打包文件 创建文件夹Template 在Template下创建template.csproj,内容如下 Template

创建.NET程序Dump的几种姿势

当一个应用程序运行的有问题时,生成一个Dump文件来调试它可能会很有用。在Windows、Linux或Azure上有许多方法可以生成转储文件。 Windows平台 dotnet-dump (Windows) dotnet-dump全局工具是一种收集和分析.NET核心应用程序Dump的方法。 安装 d

ASP.NET Core中创建中间件的几种方式

前言 今天我们一起来盘点一下在ASP.NET Core应用程序中添加和创建中间件常见的四种方式。 中间件介绍 ASP.NET Core中间件(Middleware)是用于处理HTTP请求和响应的组件,它们被安排在请求处理管道中,并按顺序执行。中间件的设计是为了使其在请求处理管道中能够以灵活和可扩展的

如何在.NET电子表格应用程序中创建流程图

前言 流程图是一种常用的图形化工具,用于展示过程中事件、决策和操作的顺序和关系。它通过使用不同形状的图标和箭头线条,将任务和步骤按照特定的顺序连接起来,以便清晰地表示一个过程的执行流程。 在企业环境中,高管和经理利用流程图来规划业务流程,使他们能够识别瓶颈、优化生产力并增强决策能力……用例列表不胜枚

Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 1: 'no such table: Users'.

今天使用asp.net core + sqlite 创建了一个demo项目,本地运行一切正常。可以添加,修改,删除数据。一旦发布到服务器上(Linux系统)就报错,错误信息如下: ![](https://img2023.cnblogs.com/blog/2912666/202308/2912666-

如何在现有项目中使用`Masa MiniApi`?

首先我们现有创建一个空的WebApi的项目模板,这个项目模板和MasaFramework本身没有任何关联,我们本博客只是使用的MasaFramework的MiniApi的包 创建Asp.NET Core 空的项目模板 项目名称MFMiniApi 其他信息看图,取消Https配置,也可以选择, 这就是

一款WPF的精简版MVVM框架——stylet框架的初体验(包括MVVM绑定、依赖注入等操作)

今天偶然知道一款叫做stylet的MVVM框架,挺小巧的,特别是它的命令触发方式,简单粗暴,让人感觉很巴适,现在我做一个简单的demo来顺便来分享给大家。 本地创建一个WPF项目,此处我使用.NET 8来创建。然后引用stylet最新的nuget包。 然后删掉App.xaml里面自带的启动项 删掉以