標籤:des winform style blog http color io os 使用
一、訊息概述
Windows 下應用程式的執行是通過訊息驅動的。訊息是整個應用程式的工作引擎,我們需要理解掌握我們使用的程式設計語言是如何封裝訊息的原理。
1. 什麼是訊息(Message)
訊息就是通知和命令。在.NET架構類庫中的System.Windows.Forms命名空間中微軟採用面對對象的方式重新定義了Message。新的訊息(Message)結構的公用部分屬性基本與早期的一樣,不過它是面對對象的。
公用屬性:
public IntPtr HWnd { get; set; } |
擷取或設定訊息的視窗控制代碼 |
public int Msg { get; set; } |
擷取或設定訊息的 識別碼 |
public IntPtr Result { get; set; } |
指定為響應訊息處理而向 Windows 返回的值 |
public IntPtr LParam { get; set; } |
指定訊息的 System.Windows.Forms.Message.LParam 欄位 |
public IntPtr WParam { get; set; } |
擷取或設定訊息的 System.Windows.Forms.Message.WParam 欄位 |
2. 訊息驅動的過程
所有的外來事件,如鍵盤輸入、滑鼠移動、按動滑鼠都由OS系統轉換成相應的訊息發送到應用程式的訊息佇列。每個應用程式都有一段相應的程式碼來檢索、分發這些訊息到對應的表單,然後由表單的處理函數來處理。
二、C#中的訊息的封裝
C#對訊息重新進行了面對對象的封裝,在C#中訊息被封裝成了事件。System.Windows.Forms.Application 類具有用於啟動和停止應用程式和線程以及處理Windows訊息的方法。
調用Run以啟動當前線程上的應用程式訊息迴圈,並可以選擇使其表單可見。
調用Exit或ExitThread來停止訊息迴圈。
C#中用Application類來處理訊息的接收和發送。訊息的迴圈是由它負責的。
從本質上來講,每個表單一般都對應一個表單過程處理函數。那麼,C#的一個Form執行個體(相當於一個表單)收到訊息後是如何處理訊息的?其實,這個問題的分析也就是展示了C#的訊息封裝原理。
實現滑鼠左鍵按下的訊息的響應(WM_LBUTTONDOWN)
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown1);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown2);
}
private void Form1_MouseDown1(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
System.Windows.Forms.MessageBox.Show("訊息被Form1_MouseDown1函數響應");
}
private void Form1_MouseDown2(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
System.Windows.Forms.MessageBox.Show("訊息被Form1_MouseDown2函數響應");
}
}
上面 this.MouseDown 是C#中的一個事件。它的定義如下:
//
// 摘要:
// 當滑鼠指標位於控制項上並按下滑鼠鍵時發生。
public event MouseEventHandler MouseDown;
// 摘要:
// 表示將處理表單、控制項或其他組件的 MouseDown、MouseUp 或 MouseMove 事件的方法。
//
// 參數:
// sender:
// 事件來源。
//
// e:
// 包含事件數目據的 System.Windows.Forms.MouseEventArgs。
public delegate void MouseEventHandler(object sender, MouseEventArgs e);
實際上,上面定義了一個委託類型 MouseEventHandler。委託了啟用了其它程式設計語言中的函數指標的解決方案。與C++的函數指標不同,委託是完全物件導向的,同時封裝了對象 執行個體和方法。本質上,委託把一個執行個體和該執行個體上的方法函數封裝成一個可調用的實體,它是面對對象的、安全的。
我們可以把 this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown1); 這條語句看成向 this.MouseDown 添加一個函數指標。
事件是對象發送的訊息,以發送訊號通知操作的發生。引發(觸發)事件的對象叫做事件發送方。捕獲事件並對事件作出響應的對象叫做事件接收方。在事件通訊中,事件發送方類並不知道哪個對象或方法將接收到(處理)它引發的事件。所需要的是在發送方和接收方之間存在一個媒介(類似指標的機制)。.NET架構定義了一個特殊的類型(Delegate委託),該類型提供函數指標的功能。這樣,委託就等效於一個型別安全的函數指標或一個回呼函數。
前面我們向this.MouseDown事件添加了兩個委託。
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown1);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown2);
結果,我們的兩個函數 Form1_MouseDown1、Form1_MouseDown2 在我們單擊滑鼠左鍵的時候都會被調用,而且調用的順序和我們添加委託的順序一致。
WM_LBUTTONDOWN訊息首先被Application類從應用程式訊息佇列中取出,然後分發到相應的表單。表單使用MouseDown事件中的函數指標調用已經添加的響應函數。所以, C# 中的事件欄位實質上是一個函數指標列表,用來維護一些訊息到達時的響應函數的地址。
三、結論
C# 中訊息的工作流程:
C# 中的訊息被Application類從應用程式訊息佇列中取出,然後分發到訊息對應的表單,表單對象的第一個響應函數是對象中的 protected override void WndProc(ref System.Windows.Forms.Message e)方法。
它再根據訊息的類型調用預設的訊息響應函數(如OnMouseDown),預設的響應函數然後根據對象的事件欄位(如this.MouseDown )中的函數指標列表,調用使用者所加入的響應函數(如Form1_MouseDown1和Form1_MouseDown2),而且調用順序和使用者添加順序一致。
四、再回首 Application 類
Application 類有一個 AddMessageFilter 的靜態方法,通過它我們可以添加訊息篩選器,以便在向目標傳遞Windows訊息時,檢視這些訊息。使用訊息篩選器來防止引發特定事件,或在將某事件傳遞給事件處理常式之前使用訊息篩選器對其執行特殊操作。我們必須提供 IMessageFilter 介面的一個實現,然後才可以使用訊息篩選器。以下的示範代碼將示範在訊息發往表單前我們如何攔截它。我們攔截的同樣是WM_LBUTTONDOWN訊息。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApplication1
{
/// <summary>
/// 實現訊息過濾器介面
/// </summary>
public class CLButtonDownFilter : IMessageFilter
{
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == 0x0201) // WM_LBUTTONDOWN
{
System.Windows.Forms.MessageBox.Show("App中滑鼠左鍵按下");
// 傳回值為true, 表示訊息已被處理,不要再往後傳遞,因此訊息被截獲
// 傳回值為false,表示訊息未被處理,需要再往後傳遞,因此訊息未被截獲
return true;
}
return false;
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// 安裝自訂訊息過濾器
CLButtonDownFilter MyFilter = new CLButtonDownFilter();
Application.AddMessageFilter(MyFilter);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown1);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown2);
}
private void Form1_MouseDown1(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
System.Windows.Forms.MessageBox.Show("訊息被Form1_MouseDown1函數響應");
}
private void Form1_MouseDown2(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
System.Windows.Forms.MessageBox.Show("訊息被Form1_MouseDown2函數響應");
}
/// <summary>
/// 通過覆蓋基類的表單函數攔截訊息
/// </summary>
/// <param name="e"></param>
protected override void WndProc(ref System.Windows.Forms.Message e)
{
//如果需要截獲訊息,
//if(e.Msg==0x0201)// WM_LBUTTONDOWN
// System.Windows.Forms.MessageBox.Show("訊息被WndProc函數響應");
//else
// base.WndProc(ref e);
//不需要截獲訊息則為
if (e.Msg == 0x0201)// WM_LBUTTONDOWN
System.Windows.Forms.MessageBox.Show("訊息被WndProc函數響應");
base.WndProc(ref e);
}
/// <summary>
/// 通過覆蓋基類的事件引發函數攔截訊息
/// </summary>
/// <param name="e"></param>
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
System.Windows.Forms.MessageBox.Show("訊息被OnMouseDown函數響應");
//如果需要截獲訊息,可將base.OnMouseDown(e);語句注釋掉
base.OnMouseDown(e);
}
}
}
以上代碼我們首先用類 CLButtonDownFilter 實現了 IMessageFilter 介面,在WinForm初始化的時候我們安裝了訊息篩選器。程式實際執行的時候,在點擊滑鼠左鍵的時候,程式僅僅會彈出一個"App中滑鼠左鍵按下"的消 息框。因為我們在訊息發往表單前攔截了它,所以表單將接收不到WM_LBUTTONDOWN訊息。
如果我們把這個代碼塊改為返回 false,
那麼,我們在Application類處理訊息後,訊息將繼續發往表單。表單的函數將可以處理此訊息。程式執行效果是順序彈出 5 個訊息框。
1:<<App中滑鼠左鍵按下>>
2:<<訊息被WndProc函數響應>>
3:<<訊息被OnMouseDown函數響應>>
4:<<訊息被Form1_MouseDown1函數響應>>
5:<<訊息被Form1_MouseDown2函數響應>>
其實本文中已經說的挺詳細的.彈出的對話方塊只是為了讓你更直觀的看出導致的結果.
C# 訊息處理機制及自訂過濾方式