Давно приводил его в качестве примера кому-то. Недавно понадобилось самому, и чтобы в дальнйшем не искать решил выложить сюда.
Общая идея очень проста. Вместо использования огромного switch-а можно использовать этот небольшой класс. Помечаете метод атрибутам содержащими идентификатор сообщения и управление автоматом приходит в этот метод.
Реализация крэкера сообщений (файл MsgCrecker.cs):
using System;
using System.Collections;
using System.Windows.Forms;
using System.Reflection;
/// <summary>
/// Атрибут используемый для пометки матода и ассоциации с ним
/// нужного идентификатора сообщения.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class WinMsgAttribute : Attribute
{
int _msgID;
public WinMsgAttribute(int msgID)
{
_msgID = msgID;
}
public int MsgID
{
get { return _msgID; }
set { _msgID = value; }
}
}
delegate bool WinMsgHandler(ref Message msg);
/// <summary>
/// Вскрывальщик сообщений. Находит метода помеченные атрибутом
/// WinMsgAttribute и ассоциирует их с сообщениями идентификаторы
/// которых хранятся в WinMsgAttribute.
/// </summary>
public class MsgCrecker
{
Hashtable _msgMap = new Hashtable();
static Type _attrType = typeof(WinMsgAttribute);
static Type _delegateType = typeof(WinMsgHandler);
public MsgCrecker(Form form)
{
// Получаем список методов формы.
MethodInfo[] methods = form.GetType().GetMethods(
BindingFlags.Instance | BindingFlags.Public
| BindingFlags.NonPublic);
foreach (MethodInfo method in methods)
{
// Получаем список атрибутов WinMsgAttribute каждого метода.
object[] attrs = method.GetCustomAttributes(_attrType, false);
// Если атрибут WinMsgAttribute ассоциирован с методом...
if (attrs.Length > 0)
{
// Перебираем их и...
foreach (WinMsgAttribute attr in attrs)
{
// Засандаливаем в хэш-таблицу, ассоциируя с делегатом содержащим
// ссылку на метод-обработчик.
_msgMap.Add(attr.MsgID,
Delegate.CreateDelegate(_delegateType, form, method.Name, true));
}
}
}
}
/// <summary>
/// Пытается обработать сообщение. Если не удается, возвращает true.
/// Если удается, то возвращает значение возвращенное методом-обработчиком.
/// </summary>
/// <example>
/// protected override void WndProc(ref Message msg)
/// {
/// if (_msgCrecker.ProcessMsg(ref msg))
/// base.WndProc(ref msg);
/// }
/// </example>
/// <param name="msg">Сообщение.</param>
/// <returns>Если true, то нужно вызывать метод WndProc базового класса.</returns>
public bool ProcessMsg(ref Message msg)
{
// Пытаемся нафти делегат ассоциированный с пришедшим сообщением.
WinMsgHandler handler = (WinMsgHandler)_msgMap[msg.Msg];
if (handler != null) // Если удается...
return handler(ref msg); // Вызываем метод.
return true; // Приказываем вызвать базовую реализацию WndProc.
}
}
Ниже пример его использования. В нем обрабатываются сообщения WM_LBUTTONDOWN и WM_RBUTTONDOWN.
На WM_LBUTTONDOWN BackColor-у присваевается синий цвет, на WM_RBUTTONDOWN — расный. Или наоборот.
Уменее ничего не придумал
:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace AutoMsg
{
public class Form1 : System.Windows.Forms.Form
{
private System.ComponentModel.Container components = null;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
// TODO: Add any constructor code after InitializeComponent call
_msgCrecker = new MsgCrecker(this);
}
/// <summary>Clean up any resources being used.</summary>
protected override void Dispose(bool disposing)
{
if(disposing && components != null)
components.Dispose();
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Name = "Form1";
this.Text = "Form1";
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
// Крэкер. Инициализируется в конструкторе так как ему нужена форма.
MsgCrecker _msgCrecker = null;
// Чтобы крэкер работал нужно переопределить WndProc следующим образом:
protected override void WndProc(ref Message msg)
{
if (_msgCrecker.ProcessMsg(ref msg))
base.WndProc(ref msg);
}
// Костанты-идентификаторы сообщений. Можно надыбать на www.codeproject.com или в winuser.h
const int WM_LBUTTONDOWN = 0x0201;
const int WM_RBUTTONDOWN = 0x0204;
// Обработчики сообщений. Атрибутом WinMsg с ними ассоциируются сами сообщения.
[WinMsg(WM_RBUTTONDOWN)]
bool OnRButtonDown(ref Message msg)
{
BackColor = Color.Blue;
return true;
}
[WinMsg(WM_LBUTTONDOWN)]
bool OnLButtonDown(ref Message msg)
{
BackColor = Color.Red;
return true;
}
}
}
... << RSDN@Home 1.1.3 beta 2 >>