TDS文件搜索_Winform版本与avalonia开发差异比较:(一)系统消息的接收与无标题栏/边框窗体的移动与尺寸调整
一、Winform和Avalonia的选择
收到私信,有看过上一篇的朋友说想了解Winform相关内容,感谢建议,因为因此稍作回忆后就想稍作一些记录和对比。
最早的时候TDS文件搜索这款工具是用Winform实现的,最早版本是4.72的Winform,后来变成了net8。当时有想过改WPF,但一直感觉必要性不大。直到后来net8开始尝试了avalonia,才觉得有了些必要对界面及一些逻辑进行了重写。
个人看来,Winform开发速度快,UI资源占用少,界面响应也快,而且本身的开发框架与windows系统功能兼容性很高,不少效果都能通过黑科技般的操作实现。
为了追求高画质/自由度,可能不得不用Avalonia UI,这时很多习惯了的处理都需要重新找解决方案。尽管Avalonia类似WPF,但很多语法细节差异还较大,学习资料相对较少,如果没有ai辅助,刚入门甚至会有无从下手的感觉。而且Avalonia开发要从sdk开始安装,开发界面也没法直观拖拉拽调试,开发热更新需要适应。
尽管用了Avalonia,但对Winform还是很喜欢的,给了很多C#程序员一开始写代码的动力,也是快速测试想法的首要选择。因此呢,这里我把一些在tds这个软件中winform和avalonia相关实现的差异和走过的坑简单分享一下。Winform版本的代码也全部开源推送仓库了(关注公众号发送tds消息自取)。如果有仍在用Winform想实现特殊效果的,或者有想尝试下Avalonia的可以参考。我们将分为五个TDS开发过程中的实例,对Windows操作系统上的Winform和Avalonia两者实现相同的功能进行对比,分别是:
- 系统消息接收
- 无标题栏/边框窗体的窗体移动
- 无标题栏/边框下的窗体尺寸改变
二、系统消息接收
在windows操作系统下,响应系统级别快捷键响应最高效的实现自然是RegisterHotKey
。当程序注册成功一个热键后,用户按下了热键,操作系统会向你的程序窗体局部发送一个标准的系统消息。注册热键直接调用系统api就可以了,但是如何拿到消息呢?
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint keyValue, Keys vk);
2.1 Winform的消息循环
Winform拿到消息很简单,在Form类下面重写WndProc函数即可,通过判定Message的值即可实现响应功能的触发。这个函数是不断触发的,他不光处理按键,还处理其他各种各样的消息,因此写功能的时候一定要考虑到代码的执行效率。
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case 0x0312:
switch (m.WParam.ToString()) //处理热键消息id
{
case "8617":
autoshoworhide(); //窗口隐藏或显现
break;
}
base.WndProc(ref m);
break;
// ....其他消息的处理
default:
base.WndProc(ref m);
break;
}
}
2.2 Avalonia的消息循环
Avalonia中,一开始尝试过用非阻塞式系统索API函数 PeekMessage,尽管同样能拿到系统消息,但由于各种原因,总是会出现丢消息以及界面卡顿的情况。知道后来发现了可通过Avalonia.Controls下面的Win32Properties.AddWndProcHookCallback函数。Win32Properties.AddWndProcHookCallback(this, WndProc),需要将本窗体对象(Avalonia.controls.Window)也就是this,和回调函数WndProc传入,即可像Winform一样流畅处理系统消息了。
private IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// If not a hotkey message or the global hotkey for showing the window
if ((int)wParam == GlobalHotkey.HotKeyId)
{
AutoShowOrHide();
}
// ....其他消息的处理
return IntPtr.Zero;
}
三、无标题栏/边框窗体的窗体移动
无界面下,没有标题栏了,那么窗体的移动需要通过鼠标与控件的交互来实现。比如说点击控件后窗体跟着鼠标移动,被鼠标拖拽等。可能大家会觉得这个实现很难,需要计算各种鼠标与窗口位置,控制边界等...
Wait wait... 如果不亲自试一试的话永远也不会知道真正做起来有多简单。
3.1 Winform的欺骗
在Winform下的实现只需在某个控件的mouseDown事件中加入2行代码即可。他的原理其实是欺骗Windows,告诉操作系统你鼠标点击的是程序的标题栏,然后程序就可以像拖拽标题栏一样随鼠标移动了
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_MOVE = 0xF010;
public const int HTCAPTION = 0x0002; //标题栏, 其他对应的功能取值可参考 https://learn.microsoft.com/zh-cn/windows/win32/inputdev/wm-nchittest
[DllImport("user32.dll")]
public static extern bool ReleaseCapture();
[DllImport("user32.dll")]
public static extern bool SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);
private void Keywords_MouseDown(object sender, MouseEventArgs e) //某个控件的鼠标按下事件
{
//移动窗体
ReleaseCapture();
SendMessage(this.Handle, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
}
3.2 Avalonia实现
可能是为了适配触控操作,Avalonia无界面移动实现更简单,在控件的PointerPressedEvent
事件下直接调用自带方法this.BeginMoveDrag, 将PointerPressedEventArgs传入即可完成与Winform一样的效果。
private void Keywords_MouseDown(object sender, PointerPressedEventArgs e)
{
this.BeginMoveDrag(e);
}
四、无标题栏/边框下的窗体尺寸改变
这个实现主要是考虑Winform窗体,设置FormBorderStyle为None,当标题栏消失时,往往边框也消失了,边框移动也没有了。
4.1 Winform的再次欺骗
仍然是在操作系统消息的处理循环中,通过改写 WM_NCHITTEST 消息 (0x0084),欺骗系统鼠标在窗体的哪个区域,并触发操作系统默认的机制。
// 下面的常量是为了模拟有标题栏窗体时,触发调整尺寸的上下左右四条边和四个角的状态标识
const int HTLEFT = 10;
const int HTRIGHT = 11;
const int HTTOP = 12;
const int HTTOPLEFT = 13;
const int HTTOPRIGHT = 14;
const int HTBOTTOM = 15;
const int HTBOTTOMLEFT = 0x10;
const int HTBOTTOMRIGHT = 17;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case 0x0084: // WM_NCHITTEST
base.WndProc(ref m); // 默认消息不干扰
Point vPoint = new Point((int)m.LParam & 0xFFFF,
(int)m.LParam >> 16 & 0xFFFF);
vPoint = PointToClient(vPoint);
if (vPoint.X <= 5) //计算容差然后触发,后面类似...
if (vPoint.Y <= 5)
m.Result = (IntPtr)HTTOPLEFT;
else if (vPoint.Y >= ClientSize.Height - 5)
m.Result = (IntPtr)HTBOTTOMLEFT;
else
m.Result = (IntPtr)HTLEFT;
else if (vPoint.X >= ClientSize.Width - 5)
if (vPoint.Y <= 5)
m.Result = (IntPtr)HTTOPRIGHT;
else if (vPoint.Y >= ClientSize.Height - 5)
m.Result = (IntPtr)HTBOTTOMRIGHT;
else
m.Result = (IntPtr)HTRIGHT;
else if (vPoint.Y <= 5)
m.Result = (IntPtr)HTTOP;
else if (vPoint.Y >= ClientSize.Height - 5)
m.Result = (IntPtr)HTBOTTOM;
break;
default:
base.WndProc(ref m);
break;
}
}
4.2 Avalonia实现
直接在xml中配置,将以下三个参数添加到已有的配置中即可。
<Window
ExtendClientAreaToDecorationsHint="True"
ExtendClientAreaChromeHints="NoChrome"
ExtendClientAreaTitleBarHeightHint="-1"
>
五、最后
其实Winform也挺好的,那种开箱即用(急用)的感觉,拖几个按钮双击进去直接开干的痛快,毕竟陪伴了我们这么久,也忘不了刚学习编程时看到自己的界面出现后那种激动人心的感受。
感谢您的耐心阅读,希望各位从零开始的新朋友和老朋友有所收获!如果你对这篇文章的内容有任何建议或想法,欢迎随时交流!本文中TDS文件搜索工具的Winform版本已在仓库完全开源了!点个 Star ️支持一下!代码仓库地址 不清楚的请关注微信公众号“萤火初芒",发送消息 “TDS”即可查看!
下期预告:
“TDS文件搜索_Winform版本与avalonia开发差异比较:(二)列表虚拟化以及系统文件图标ico动态获取与绑定”
TDS文件搜索_Winform版本与avalonia开发差异比较:(一)系统消息的接收与无标题栏/边框窗体的移动与尺寸调整的更多相关文章
- 关于FlexPaper 2.1.2版本 二次开发 Logo 、打印、搜索、缩略图、添加按钮、js交互、右键菜单、书签等相关问题
2015-03-02 更新文章,由于需求修改,更改了flexpaper插件,故增加第9.10.11小节,下载代码时请注意. 先废话几句.最近用到文档在线浏览功能,之前用的是print2flash(一个 ...
- ionic2如何升级到最新版本、配置开发环境
好久没写东西了,去年用了angular2的RC版本和ionic2写了一个项目,因为开发周期和有些版本不稳定,所以一直没有升级,ng2新版本引用Aot打包,听说优化还不错,现在尝试升级ioni ...
- 文件搜索工具everything
Everything是voidtools开发的一款文件搜索工具,官网描述为“基于名称实时定位文件和目录(Locate files and folders by name instantly) (“Ev ...
- Everything(文件搜索神器)
前言 Everything官网: http://www.voidtools.com/ 软件版本: V1.3.4.686 (x64) 操作系统: windows 7/10 搜索FTP(内网)资源 比如内 ...
- gcc编译时头文件和库文件搜索路径
特殊情况:用户自定义的头文件使用#include"mylib"时,gcc编译器会从当前目录查找头文件 一.头文件 gcc 在编译时寻找所需要的头文件 : ※搜寻会从-I开始( ...
- Android开发学习总结(一)——搭建最新版本的Android开发环境
Android开发学习总结(一)——搭建最新版本的Android开发环境(转) 最近由于工作中要负责开发一款Android的App,之前都是做JavaWeb的开发,Android开发虽然有所了解,但是 ...
- 文件搜索神器 Everything
Everything 是一款 NTFS 磁盘格式下的文件搜索工具,1月5日发布测试版本 1.3.0.631b Beta,增加文件列表.收藏夹.自定义快捷键.高级搜索等功能,取消了比较实用的 etp/f ...
- 用pyenv和virtualenv搭建单机多版本python虚拟开发环境
作为主流开发语言, 用python 开发的程序越来越多. 方便的是大多linux系统里面都默认集成了python, 开发可以随时随地开始. 但有时候这也成为了一个短板, 比如说有时候我们需要开发和调试 ...
- 搭建最新版本的Android开发环境
只为成功找方法,不为失败找借口! Android开发学习总结(一)——搭建最新版本的Android开发环境 最近由于工作中要负责开发一款Android的App,之前都是做JavaWeb的开发,Andr ...
- 用pyenv 和 virtualenv 搭建单机多版本python 虚拟开发环境
作为主流开发语言, 用python 开发的程序越来越多. 方便的是大多linux系统里面都默认集成了python, 开发可以随时随地开始. 但有时候这也成为了一个短板, 比如说有时候我们需要开发和调试 ...
随机推荐
- 华为机试题 Sudoku
简介 使用回溯算法. 其实回溯算法属于暴力算法. 进行一定的减枝算法即可. 这里要使用弱检查, 全局flag 进行退出. code #include <iostream> #include ...
- java 套接字
简介 RT code package com.kuang; import java.io.ByteArrayOutputStream; import java.io.IOException; impo ...
- 低代码开发平台,快速提升API开发效率
RestCloud低代码开发平台相比于传统API开发模式具有更轻量级.更快速.更全面.更低代码量.更易于学习等特点,是完全自主研发的基于微服务架构的专注API高效开发的专业化平台,通过平台可以实现无代 ...
- SciTech-BigDataAIML-Tensorflow-Variables
tf.config.run_functions_eagerly(True) tf.data.experimental.enable_debug_mode() tf.debugging.set_log_ ...
- 网站SSL证书怎么选?不用SSL证书会怎么样?
SSL 证书(Secure Sockets Layer Certificate)作为一种数字证书,由可信赖的第三方机构(CA,Certificate Authority)予以颁发.其主要功能在于,在客 ...
- if-else问题
为什么会有这个问题,最近碰到很多逻辑判断,此时需要用到if-else 以前就听说判断if语句,一定要注意else问题,以前不以为意.现在碰到好多异常现象,每个if语句后面需要重新分析其他分支情况
- fatal: Unable to create .git/index.lock: File exists.
解决方法 进入你当前的项目的 .git 目录 cd ./.git/ 删除 index.lock(linux) rm -rf ./index.lock 删除 index.lock(windows) rm ...
- 前端使用highcharts报错“Error: Highcharts error #13”
报错情况如下: 错误原因: 查找了下这个错误,图形容器无法找到,会导致报这个错误,两个页面都在使用同一个容器id时可能也会导致这样的问题,我遇到的是后者....所以就改了一id然后就成功解决 如果是前 ...
- Java泛型-类型参数化
一. 泛型 解决支持多类型参数的方案: 1, 重载: 在同一个类中,方法名相同,参数不同 不足之处: 代码过于重复 public class Tool{ public String arrToS ...
- Java集合——15.使用Collections
目录 Java 集合工具类 Collections 批量添加元素:addAll() List排序与洗牌:sort()和shuffle() 排序 随机打乱元素 不可变集合的转换 总结 Java 集合工具 ...