TDS文件搜索_Winform版本與avalonia開發(fā)差異比較:(一)系統(tǒng)消息的接收與無標(biāo)題欄/邊框窗體的移動(dòng)與尺寸調(diào)整
一、Winform和Avalonia的選擇
收到私信,有看過上一篇的朋友說想了解Winform相關(guān)內(nèi)容,感謝建議,因?yàn)橐虼松宰骰貞浐缶拖肷宰饕恍┯涗浐蛯?duì)比。
最早的時(shí)候TDS文件搜索這款工具是用Winform實(shí)現(xiàn)的,最早版本是4.72的Winform,后來變成了net8。當(dāng)時(shí)有想過改WPF,但一直感覺必要性不大。直到后來net8開始嘗試了avalonia,才覺得有了些必要對(duì)界面及一些邏輯進(jìn)行了重寫。
個(gè)人看來,Winform開發(fā)速度快,UI資源占用少,界面響應(yīng)也快,而且本身的開發(fā)框架與windows系統(tǒng)功能兼容性很高,不少效果都能通過黑科技般的操作實(shí)現(xiàn)。
為了追求高畫質(zhì)/自由度,可能不得不用Avalonia UI,這時(shí)很多習(xí)慣了的處理都需要重新找解決方案。盡管Avalonia類似WPF,但很多語法細(xì)節(jié)差異還較大,學(xué)習(xí)資料相對(duì)較少,如果沒有ai輔助,剛?cè)腴T甚至?xí)袩o從下手的感覺。而且Avalonia開發(fā)要從sdk開始安裝,開發(fā)界面也沒法直觀拖拉拽調(diào)試,開發(fā)熱更新需要適應(yīng)。

盡管用了Avalonia,但對(duì)Winform還是很喜歡的,給了很多C#程序員一開始寫代碼的動(dòng)力,也是快速測(cè)試想法的首要選擇。因此呢,這里我把一些在tds這個(gè)軟件中winform和avalonia相關(guān)實(shí)現(xiàn)的差異和走過的坑簡(jiǎn)單分享一下。Winform版本的代碼也全部開源推送倉(cāng)庫(kù)了(關(guān)注公眾號(hào)發(fā)送tds消息自取)。如果有仍在用Winform想實(shí)現(xiàn)特殊效果的,或者有想嘗試下Avalonia的可以參考。我們將分為五個(gè)TDS開發(fā)過程中的實(shí)例,對(duì)Windows操作系統(tǒng)上的Winform和Avalonia兩者實(shí)現(xiàn)相同的功能進(jìn)行對(duì)比,分別是:
- 系統(tǒng)消息接收
- 無標(biāo)題欄/邊框窗體的窗體移動(dòng)
- 無標(biāo)題欄/邊框下的窗體尺寸改變
二、系統(tǒng)消息接收
在windows操作系統(tǒng)下,響應(yīng)系統(tǒng)級(jí)別快捷鍵響應(yīng)最高效的實(shí)現(xiàn)自然是RegisterHotKey。當(dāng)程序注冊(cè)成功一個(gè)熱鍵后,用戶按下了熱鍵,操作系統(tǒng)會(huì)向你的程序窗體局部發(fā)送一個(gè)標(biāo)準(zhǔn)的系統(tǒng)消息。注冊(cè)熱鍵直接調(diào)用系統(tǒng)api就可以了,但是如何拿到消息呢?
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint keyValue, Keys vk);
2.1 Winform的消息循環(huán)
Winform拿到消息很簡(jiǎn)單,在Form類下面重寫WndProc函數(shù)即可,通過判定Message的值即可實(shí)現(xiàn)響應(yīng)功能的觸發(fā)。這個(gè)函數(shù)是不斷觸發(fā)的,他不光處理按鍵,還處理其他各種各樣的消息,因此寫功能的時(shí)候一定要考慮到代碼的執(zhí)行效率。
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case 0x0312:
switch (m.WParam.ToString()) //處理熱鍵消息id
{
case "8617":
autoshoworhide(); //窗口隱藏或顯現(xiàn)
break;
}
base.WndProc(ref m);
break;
// ....其他消息的處理
default:
base.WndProc(ref m);
break;
}
}
2.2 Avalonia的消息循環(huán)
Avalonia中,一開始嘗試過用非阻塞式系統(tǒng)索API函數(shù) PeekMessage,盡管同樣能拿到系統(tǒng)消息,但由于各種原因,總是會(huì)出現(xiàn)丟消息以及界面卡頓的情況。知道后來發(fā)現(xiàn)了可通過Avalonia.Controls下面的Win32Properties.AddWndProcHookCallback函數(shù)。Win32Properties.AddWndProcHookCallback(this, WndProc),需要將本窗體對(duì)象(Avalonia.controls.Window)也就是this,和回調(diào)函數(shù)WndProc傳入,即可像Winform一樣流暢處理系統(tǒng)消息了。
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;
}
三、無標(biāo)題欄/邊框窗體的窗體移動(dòng)
無界面下,沒有標(biāo)題欄了,那么窗體的移動(dòng)需要通過鼠標(biāo)與控件的交互來實(shí)現(xiàn)。比如說點(diǎn)擊控件后窗體跟著鼠標(biāo)移動(dòng),被鼠標(biāo)拖拽等。可能大家會(huì)覺得這個(gè)實(shí)現(xiàn)很難,需要計(jì)算各種鼠標(biāo)與窗口位置,控制邊界等...
Wait wait... 如果不親自試一試的話永遠(yuǎn)也不會(huì)知道真正做起來有多簡(jiǎn)單。
3.1 Winform的欺騙
在Winform下的實(shí)現(xiàn)只需在某個(gè)控件的mouseDown事件中加入2行代碼即可。他的原理其實(shí)是欺騙Windows,告訴操作系統(tǒng)你鼠標(biāo)點(diǎn)擊的是程序的標(biāo)題欄,然后程序就可以像拖拽標(biāo)題欄一樣隨鼠標(biāo)移動(dòng)了
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_MOVE = 0xF010;
public const int HTCAPTION = 0x0002; //標(biāo)題欄, 其他對(duì)應(yīng)的功能取值可參考 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) //某個(gè)控件的鼠標(biāo)按下事件
{
//移動(dòng)窗體
ReleaseCapture();
SendMessage(this.Handle, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
}
3.2 Avalonia實(shí)現(xiàn)
可能是為了適配觸控操作,Avalonia無界面移動(dòng)實(shí)現(xiàn)更簡(jiǎn)單,在控件的PointerPressedEvent事件下直接調(diào)用自帶方法this.BeginMoveDrag, 將PointerPressedEventArgs傳入即可完成與Winform一樣的效果。
private void Keywords_MouseDown(object sender, PointerPressedEventArgs e)
{
this.BeginMoveDrag(e);
}
四、無標(biāo)題欄/邊框下的窗體尺寸改變
這個(gè)實(shí)現(xiàn)主要是考慮Winform窗體,設(shè)置FormBorderStyle為None,當(dāng)標(biāo)題欄消失時(shí),往往邊框也消失了,邊框移動(dòng)也沒有了。
4.1 Winform的再次欺騙
仍然是在操作系統(tǒng)消息的處理循環(huán)中,通過改寫 WM_NCHITTEST 消息 (0x0084),欺騙系統(tǒng)鼠標(biāo)在窗體的哪個(gè)區(qū)域,并觸發(fā)操作系統(tǒng)默認(rèn)的機(jī)制。
// 下面的常量是為了模擬有標(biāo)題欄窗體時(shí),觸發(fā)調(diào)整尺寸的上下左右四條邊和四個(gè)角的狀態(tài)標(biāo)識(shí)
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); // 默認(rèn)消息不干擾
Point vPoint = new Point((int)m.LParam & 0xFFFF,
(int)m.LParam >> 16 & 0xFFFF);
vPoint = PointToClient(vPoint);
if (vPoint.X <= 5) //計(jì)算容差然后觸發(fā),后面類似...
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實(shí)現(xiàn)
直接在
<Window
ExtendClientAreaToDecorationsHint="True"
ExtendClientAreaChromeHints="NoChrome"
ExtendClientAreaTitleBarHeightHint="-1"
>
五、最后
其實(shí)Winform也挺好的,那種開箱即用(急用)的感覺,拖幾個(gè)按鈕雙擊進(jìn)去直接開干的痛快,畢竟陪伴了我們這么久,也忘不了剛學(xué)習(xí)編程時(shí)看到自己的界面出現(xiàn)后那種激動(dòng)人心的感受。
感謝您的耐心閱讀,希望各位從零開始的新朋友和老朋友有所收獲!如果你對(duì)這篇文章的內(nèi)容有任何建議或想法,歡迎隨時(shí)交流!本文中TDS文件搜索工具的Winform版本已在倉(cāng)庫(kù)完全開源了!點(diǎn)個(gè) Star ??支持一下!代碼倉(cāng)庫(kù)地址 https://github.com/LdotJdot/TDS_Winform,更多請(qǐng)關(guān)注微信公眾號(hào)“螢火初芒"

下期預(yù)告:
“TDS文件搜索_Winform版本與avalonia開發(fā)差異比較:(二)列表虛擬化以及系統(tǒng)文件圖標(biāo)ico動(dòng)態(tài)獲取與綁定”
重要 關(guān)注微信公眾號(hào)‘螢火初芒’,有問題公眾號(hào)留言,作者務(wù)必第一時(shí)間回復(fù)解答交流~!!!
posted on 2025-09-27 00:24 LdotJdot 閱讀(470) 評(píng)論(4) 收藏 舉報(bào)
浙公網(wǎng)安備 33010602011771號(hào)