基於Windows Form架構下,電容式觸控螢幕的視覺回饋解決方案
如果你是採用Win32來進行環境的開發,那麼Windows Form架構還是現在業界的主流程式設計架構,盡管Windows Presentation Foundation(WPF)已經被微軟大力的推行多年了,但是相信坊間還是有很多需要維護的架構,無法在這個環境中被徹底的重構改寫。這篇文章不是要解決重構改寫的問題,而是要解決舊有的Win32架構套用在新式的電容式觸控螢幕介面下,可以被觸發OnMouseDown的模擬。
因應扁平化的設計範本,微軟對Windows Form的Button提供了System.Windows.Forms.FlatStyle.Flat來讓你的按鈕在視覺上可以貼合作業系統的設計風格,另外也提供FlatAppearance.MouseDownBackColor來讓你點擊這顆按鈕時,可以產生對應的背景著色。現在的問題是,無論是原來的OnMouseDown事件,或者是MouseDownBackColor屬性,都只有適用於滑鼠、電阻式螢幕的點擊事件觸發,對於電容螢幕完全無法捕捉事件,電容螢幕的觸發點只會觸發Click事件(發生在OnMouseUp),要測試這個按鈕真正的事件是否有被觸發,你可以把執行檔丟到Microsoft Surface上面,用手點選按鈕試看看就知道了,我可以直接跟你說:「看不到點擊按鈕的事件」。
解決方案
一開始我思考錯方向,因此一直在Windows Touch Gestures所提供的API間繞轉,問題是Gestures API是在處理Action的問題,繞了很久後才發現微軟明明就已經載明Tap / Double Tap根本不由Gestures支援,詳見下方圖表的第一列,因此開始把方向轉回Window Message Handlers。
Window Message Handlers
看來是得回到大家都很不願意再面對的MFC世界,Window Message Handlers相關的知識請自行搜尋MFC、C++關鍵字,在這邊我就不多說了。網路上用C#處理的相關範例往往都繞了一大圈,用全域的方式在監聽WindowMessage,然後再去觸發想做的事情,這類的做法我也不能說它錯啦,只是,這樣處理方式效能真的吃得很重,程式碼其實也會被複雜化。我的做法是反過來,先產生一個自我定義的按鈕(TouchButton),去繼承System.Windows.Forms.Button,如此一來我就可以改寫WndProc來完成我想要的System.Windows.Forms.Message監聽,之後表單上所有的按鈕都自TouchButton生出實例(Instances),如此一來程式碼與效能都被最佳化了。
TouchButton.cs
namespace SimplyWinform
{
public class TouchButton : System.Windows.Forms.Button
{
private System.Drawing.Color oBackgroundColor = System.Drawing.Color.FromArgb(51, 51, 51);
private System.Drawing.Color oClickDownColor = System.Drawing.Color.FromArgb(0, 186, 196);
public TouchButton()
{
//在建構時期,先處理按鈕的通用外在
this.Font = new System.Drawing.Font("微軟正黑體", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this.ForeColor = System.Drawing.Color.WhiteSmoke;
this.BackColor = oBackgroundColor;
this.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.FlatAppearance.BorderSize = 0;
this.FlatAppearance.MouseDownBackColor = oClickDownColor;
}
protected override void WndProc(ref System.Windows.Forms.Message msg)
{
const int WM_POINTERDOWN = 0x0246;
const int WM_POINTERUP = 0x247;
switch (msg.Msg)
{
case WM_POINTERDOWN:
this.BackColor = oClickDownColor;
break;
case WM_POINTERUP:
this.BackColor = oBackgroundColor;
break;
}
base.WndProc(ref msg);
}
}
}
Form1.Designer.cs
namespace SimplyWinform
{
partial class Form1
{
/// <summary>
/// 設計工具所需的變數。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清除任何使用中的資源。
/// </summary>
/// <param name="disposing">如果應該處置 Managed 資源則為 true,否則為 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form 設計工具產生的程式碼
/// <summary>
/// 此為設計工具支援所需的方法 - 請勿使用程式碼編輯器修改
/// 這個方法的內容。
/// </summary>
private void InitializeComponent()
{
this.touchButton1 = new SimplyWinform.TouchButton();
this.SuspendLayout();
//
// touchButton1
//
this.touchButton1.Location = new System.Drawing.Point(12, 12);
this.touchButton1.Name = "touchButton1";
this.touchButton1.Size = new System.Drawing.Size(294, 169);
this.touchButton1.TabIndex = 0;
this.touchButton1.Text = "Touch Click";
this.touchButton1.UseVisualStyleBackColor = true;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(318, 193);
this.Controls.Add(this.touchButton1);
this.Name = "Form1";
this.Text = "Touch Screen Events Test";
this.ResumeLayout(false);
}
#endregion
private TouchButton touchButton1;
}
}
總結
要捕捉傳統Windows Form(Win32)於電容式觸控螢幕上的OnMouseDown事件,就是這麼的麻煩,你可以看到在VisualStudio 2015 / .NET Framework 4.6+的時代,微軟幾乎已經不在維護System.Windows.Forms的相關類別了,所以要嘛就採用Universal Windows Platform(UWP)來全面改寫你的專案,要嘛只好鼻子摸摸去寫底層了,這也是令人挺無奈的事情啊!
Touch Screen Events Test Demo EXE Download