基於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

WinForm WindowsForm Win32 TouchScreen OnMouseDown OnMouseUp OnClick UAP