使用 ElementUI 组件构建无边框 Window 桌面应用(WinForm/WPF)

生活不可能像你想象得那么好,但也不会像你想象得那么糟。 我觉得人的脆弱和坚强都超乎自己的想象。 有时,我可能脆弱得一句话就泪流满面;有时,也发现自己咬着牙走了很长的路。

——莫泊桑 《一生》

一、技术栈

Vite + Vue3 + TS + ElementUI(plus) + .NET Framework 4.7.2,开发环境为 Win10,VS2019,VS Code。 

二、实现原理

1、WinForm 窗口无边框

设置属性 FormBorderStyle 为 None ,

FormBorderStyle = FormBorderStyle.None;

2、WPF 窗口无边框

设置属性 WindowStyle ="None" ,

WindowStyle = WindowStyle.None;
<Window x:Class="SerialDevTool.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"  
        Title="MainWindow" 
        WindowStyle ="None"
        AllowsTransparency="True"
        WindowState="Normal"
        WindowStartupLocation="CenterScreen">
    <Grid>
    </Grid>
</Window> 

3、user32.dll 库

该库包含了一些与用户界面交互相关的函数,其中,ReleaseCapture 函数用于释放鼠标捕获,SendMessage 函数用于向指定的窗口发送消息。

// https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-releasecapture?redirectedfrom=MSDN
// 从当前线程中的窗口释放鼠标捕获,并还原正常鼠标输入处理。 捕获鼠标的窗口接收所有鼠标输入,而不考虑光标的位置,但当光标热点位于另一个线程的窗口中时单击鼠标按钮除外。
BOOL ReleaseCapture();

// https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-sendmessage
// 将指定的消息发送到一个或多个窗口。 SendMessage 函数调用指定窗口的窗口过程,在窗口过程处理消息之前不会返回。
LRESULT SendMessage(
  [in] HWND   hWnd,
  [in] UINT   Msg,
  [in] WPARAM wParam,
  [in] LPARAM lParam
);

4、自定义窗口拖拽实现

引入 user32.dll 库,监听界面上某区域的鼠标事件,触发鼠标事件后,通过 ReleaseCapture 函数释放当前鼠标捕获并还原正常鼠标输入处理,由 SendMessage 函数实现当前窗口的移动过程。

5、Chromium Embedded Framework

通过 CefSharp 库内嵌一个浏览器控件到 DotNet 窗口应用中。

6、接收 Javascript 信息

ChromiumWebBrowser 类提供了 JavascriptMessageReceived 方法,

//
// 摘要:
//     Event handler that will get called when the message that originates from CefSharp.PostMessage
public event EventHandler<JavascriptMessageReceivedEventArgs> JavascriptMessageReceived;

三、TitleBar 样式设计与实现

1、布局

左边三个按钮分别触发最小化、最大/正常化、关闭窗口,标题居中,

2、代码实现

// app\src\components\TitleBarSimple.vue
<template>
  <div class="common grid-content">
    <div class="common my-button">
      <el-button id="minimizedButton" @click="minimizedWindow" type="danger" circle />
      <el-button id="normalizedButton" @click="normalizedWindow" type="primary" circle />
      <el-button id="closeButton" @click="closeWindow" type="default" circle />
    </div>
    <div @mousedown="handleMouseDown" class="common my-title-bar" id="my-title">
      <div> <el-text tag="b">{
  {mytitle}}</el-text> </div>
    </div>
  </div>
</template>

<script lang="ts" setup>

const mytitle:string = 'Awesome Application 版本 1.0.0.0(开发版本) (64 位)'

/**最小化窗口 */
const minimizedWindow = () => {
  const ret = { type: 'minimized' };
  CefSharp.PostMessage(ret);
}

/**关闭窗口 */
const closeWindow = () => {
  const ret = { type: 'close' };
  CefSharp.PostMessage(ret);
}

/**最大/正常窗口 */
const normalizedWindow = () => {
  const ret = { type: 'normalized' };
  CefSharp.PostMessage(ret);
}


/**鼠标左键点击事件 */
const handleMouseDown = (event: any) => {
  // 检查是否是鼠标左键点击事件
  if (event.button === 0) {
    const ret = { type: 'mousedown' };
    CefSharp.PostMessage(ret);
  }
}

</script>

<style lang="scss">
/* cnpm install -D sass */

.el-row {
  margin-bottom: 20px;
}

.el-row:last-child {
  margin-bottom: 0;
}

.el-col {
  border-radius: 4px;
}

.el-button.is-circle {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  padding: 8px;
}

.common {
  display: flex;
  /* 水平居中 */
  justify-content: center; 
  /* 垂直居中 */
  align-items: center; 
}

.grid-content {
  min-height: 30px;
  margin-bottom: 5px;
  background: #FAFAFA;
}

.my-button {
  padding-left: 5px;
  width: 105px;
}

.my-title-bar {
  w
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

余衫马

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值