很长一段时间内,一直在做一个SCSF(Smart Client Software Factory)的项目,已经进入UAT阶段。最近,用户提出了一个要求:需要通过按键方式来控制竖直滚动条。具体来讲就是说,如果一个容器内容过多,用户可以通过按键PageUp和PageDown来控制上下的滚动。刚开始,我试图采用注册事件的方式来实现,但是效果不理想,一来是没有一个单一的地方来对所有相关空间进行事件注册操作,二来如果容器被子控件完全遮挡,容器空间的事件将不会正常出发。有个同事提示采用自定义MessageFilter的方式,我觉得可行,于是进行了一番尝试。
一、实现原理简介
对于一个Windows Form应用来说,所有事件的触发都是采用消息(Message)的方式来实现的。比如,你点击了一个按钮,Windows会为这个操作之生成一个消息,并将这个消息分发(Dispatch)给按钮对象。如果能够在消息被分发给目标对象之前,能够对该消息进行了拦截,那么我们就可以按照我们希望的方式从新生成一个消息,并将其发送给我希望的目标对象,那么就能过随心所欲地控制目标对象的行为了。而自定义MessageFilter为我们提供了一个最好的消息拦截方式。
就拿我们上面给出控制滚动条的场景来说,当前容器由于内容过多而产生竖直滚动条(假设子控件的宽度和容器相同),用户键入PageDown按键试图向下滚动。Windows为本次键盘操作生成一个消息,并分发给目标对象(可能并不是我们需要控制的当前容器对象)。在此期间,我们通过MessageFilter对该消息实施拦截,从新产生一个基于“向下滚动”操作的消息,并分发给我们需要对其进行控制的容器,那么就实现了对于容器空间滚动条进行控制的目的。
二、实例应用场景简介
熟悉SCSF的朋友应该很清楚,SCSF的通过一个称为Shell的Form作为主界面,利用一个称为Workspace的容器最为整个应用的工作平台。应用动态运行过程中,各个Module的界面采用相同的方式添加到该Workspace之中。下图的就是我们将要演示的例子运行时的截图,为了简单起见,我直接通过一个System.Windows.Forms.TabControl作为Workspace。主菜单的两个菜单项分别代表两个模块,点击相应的菜单项后,会把相应的界面添加到Workspace中。在这里,我通过System.Windows.Forms.UserControl的方式定义Customer和Order模块的界面,当Customer和Order菜单被点击之后,会动态地在TabControl中添加相应的TabPage,并把相应的UserControl置于其中。由于整个TabControl的高度时固定的,而TabPage中显示的内容则依赖于具体的逻辑,所以对于内容过多的TabPage,将会有一个竖直滚动条。而我们需要通过按键的方式控制的就是当前TabPage的这个滚动条。
下面是该Form相关的代码,静态属性ActiveTabPage代表当前显示的TabPage。UserInfo和OrderInfo是两个UserControl,代表与具体模块相关的界面呈现。
1: using System;
<!--CRLF-->
2: using System.Collections.Generic;
<!--CRLF-->
3: using System.Windows.Forms;
<!--CRLF-->
4:
<!--CRLF-->
5: namespace MessageFilterDemos
<!--CRLF-->
6: {
<!--CRLF-->
7: public partial class MainForm : Form
<!--CRLF-->
8: {
<!--CRLF-->
9: public static TabPage ActiveTabPage
<!--CRLF-->
10: { get;private set; }
<!--CRLF-->
11:
<!--CRLF-->
12: private IDictionary<string, UserControl> keyedViews
<!--CRLF-->
13: { get; set; }
<!--CRLF-->
14:
<!--CRLF-->
15: public MainForm()
<!--CRLF-->
16: {
<!--CRLF-->
17: InitializeComponent();
<!--CRLF-->
18: this.keyedViews = new Dictionary<string, UserControl>();
<!--CRLF-->
19: }
<!--CRLF-->
20:
<!--CRLF-->
21: protected override void OnLoad(EventArgs e)
<!--CRLF-->
22: {
<!--CRLF-->
23: base.OnLoad(e);
<!--CRLF-->
24: this.keyedViews.Add("CustomerInfo", new CustomerInfo());
<!--CRLF-->
25: this.keyedViews.Add("OrderInfo", new OrderInfo());
<!--CRLF-->
26: }
<!--CRLF-->
27:
<!--CRLF-->
28: private void Show(string key, string text, UserControl view)
<!--CRLF-->
29: {
<!--CRLF-->
30: if (!this.mainWorkspace.TabPages.ContainsKey(key))
<!--CRLF-->
31: {
<!--CRLF-->
32: this.mainWorkspace.TabPages.Add(key, text);
<!--CRLF-->
33: this.mainWorkspace.TabPages[key].Controls.Add(view);
<!--CRLF-->
34: this.mainWorkspace.TabPages[key].AutoScroll = true;
<!--CRLF-->
35: }
<!--CRLF-->
36: this.mainWorkspace.SelectedTab = this.mainWorkspace.TabPages[key];
<!--CRLF-->
37: ActiveTabPage = this.mainWorkspace.TabPages[key];
<!--CRLF-->
38: }
<!--CRLF-->
39:
<!--CRLF-->
40: private void ordeToolStripMenuItem_Click(object sender, EventArgs e)
<!--CRLF-->
41: {
<!--CRLF-->
42: this.Show("OrderInfo", "Order", keyedViews["OrderInfo"]);
<!--CRLF-->
43: }
<!--CRLF-->
44:
<!--CRLF-->
45: private void customerToolStripMenuItem_Click(object sender, EventArgs e)
<!--CRLF-->
46: {
<!--CRLF-->
47: this.Show("CustomerInfo", "Customer", keyedViews["CustomerInfo"]);
<!--CRLF-->
48: }
<!--CRLF-->
49:
<!--CRLF-->
50: private void mainWorkspace_SelectedIndexChanged(object sender, EventArgs e)
<!--CRLF-->
51: {
<!--CRLF-->
52: ActiveTabPage = this.mainWorkspace.SelectedTab;
<!--CRLF-->
53: }
<!--CRLF-->
54: }
<!--CRLF-->
55: }
<!--CRLF-->
三、自定义MessageFilter
现在我们进入重点话题,如何创建我们需要的自定义MessageFilter,由于我们这个MessageFilter旨在控制TabPag的滚动条,我们将其命名为ScrollbarControllerMessageFilter。ScrollbarControllerMessageFilter实现了接口System.Windows.Forms.IMessageFilter。下面是IMessageFilter的定义,它仅仅包含一个唯一的成员:PreFilterMessage,对消息的拦截、筛选操作就实现在这里。而Bool类新的返回值表示是否继续将消息分发的目标对象。
1: public interface IMessageFilter
<!--CRLF-->
2: {
<!--CRLF-->
3: [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
<!--CRLF-->
4: bool PreFilterMessage(ref Message m);
<!--CRLF-->
5: }
<!--CRLF-->
下面是ScrollbarControllerMessageFilter的定义,代码不是很复杂,在这里只需简单的介绍一下流程:在PreFilterMessage方法中,先判断当前的TabPage是否存在,如果不存在,则不加干涉;然后通过System.Windows.Forms.Message的Msg属性确定当前事件是否是KeyDown,如果不是则直接返回;最后根据System.Windows.Forms.Message的WParam属性判断当前的按键是否是PageUp或者PageDown,并相应的向目标对象(当前的TabPage)发送一个关于向上或者向下滚动的消息。消息的发送通过调用Native方法SendMessage实现。
1: using System;
<!--CRLF-->
2: using System.Runtime.InteropServices;
<!--CRLF-->
3: using System.Windows.Forms;
<!--CRLF-->
4:
<!--CRLF-->
5: namespace MessageFilterDemos
<!--CRLF-->
6: {
<!--CRLF-->
7: public class ScrollbarControllerMessageFilter: IMessageFilter
<!--CRLF-->
8: {
<!--CRLF-->
9:
<!--CRLF-->
10: private const int WM_KEYDOWN = 0x100;//Key down
<!--CRLF-->
11: private const int WM_VSCROLL = 277; //Scroll
<!--CRLF-->
12: private const int SB_PAGEUP = 2; // Scroll Up
<!--CRLF-->
13: private const int SB_PAGEDOWN = 3; //Scroll Down
<!--CRLF-->
14:
<!--CRLF-->
15: #region IMessageFilter Members
<!--CRLF-->
16:
<!--CRLF-->
17: [DllImport("user32.dll")]
<!--CRLF-->
18: static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
<!--CRLF-->
19:
<!--CRLF-->
20: public bool PreFilterMessage(ref Message m)
<!--CRLF-->
21: {
<!--CRLF-->
22: if (MainForm.ActiveTabPage == null)
<!--CRLF-->
23: {
<!--CRLF-->
24: return false;
<!--CRLF-->
25: }
<!--CRLF-->
26:
<!--CRLF-->
27: if (WM_KEYDOWN != m.Msg)
<!--CRLF-->
28: {
<!--CRLF-->
29: return false;
<!--CRLF-->
30: }
<!--CRLF-->
31:
<!--CRLF-->
32: if (m.WParam.ToInt32() == (int)(Keys.PageUp))
<!--CRLF-->
33: {
<!--CRLF-->
34: SendMessage(MainForm.ActiveTabPage.Handle, WM_VSCROLL, SB_PAGEUP, 0);
<!--CRLF-->
35: return true;
<!--CRLF-->
36: }
<!--CRLF-->
37:
<!--CRLF-->
38: if (m.WParam.ToInt32() == (int)(Keys.PageDown))
<!--CRLF-->
39: {
<!--CRLF-->
40: SendMessage(MainForm.ActiveTabPage.Handle, WM_VSCROLL, SB_PAGEDOWN, 0);
<!--CRLF-->
41: return true;
<!--CRLF-->
42: }
<!--CRLF-->
43:
<!--CRLF-->
44: return false;
<!--CRLF-->
border-bottom-style: none; text-align: left; padding-bottom: 0
分享到:
相关推荐
POSTMESSAGE后台模拟按键源码,可以POSTMESSAGE按键消息到其他程序中
在这个程序片中,QInputDialog使用getText()方法来取得使用者输入字符串,然后使用QMessageBox 来显示输入的字符串;QInputDialog若取得输入,会把isOK中设定为true,由此可判断使用者是否有输入,QMessageBox可以...
http://blog.csdn.net/fxz1982/article/details/7480073
自己定义的ip地址控件,好用 #include "stdafx.h" #include "IPTest.h" #include "IPTestDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ////////////...
Java Message Service 2nd Edition 的源代码
smack Message添加自定义节点
日历表格面板 [ConfigLine.java] 控制条类 [RoundBox.java] 限定选择控件 [MonthMaker.java] 月份表算法类 [Pallet.java] 调色板,统一配色类 Java扫雷源码 Java生成自定义控件源代码 2个目标文件 Java实现HTTP连接...
PostMessage 后台模拟按键 MOUSE VC原码 PostMessage 后台模拟按键 MOUSE VC原码 可以后台操作的,模拟按键,VC 代码.
基于java的开发源码-Message-Driven Bean EJB实例源代码.zip 基于java的开发源码-Message-Driven Bean EJB实例源代码.zip 基于java的开发源码-Message-Driven Bean EJB实例源代码.zip 基于java的开发源码-Message-...
MFC 自定义 MessageBox.zipMFC 自定义 MessageBox.zipMFC 自定义 MessageBox.zipMFC 自定义 MessageBox.zipMFC 自定义 MessageBox.zipMFC 自定义 MessageBox.zipMFC 自定义 MessageBox.zipMFC 自定义 MessageBox....
所以我们本次的自定义控件就是由ViewPager和LinearLayout叠加起来组成的。 一、创建一个自定义的ViewPager 先上完整的代码 package com.kcode.autoscrollviewpager.view; import android.content.Context; import ...
Message-Driven Bean EJB实例源代码
ON_MESSAGE(ID,Handler) LRESULT Handler(WPARAM,LPARAM);//响应的函数 BEGIN_MESSAGE_MAP(CShellDlg, CDialog) 1. //{{AFX_MSG_MAP(CShellDlg) 2. ON_MESSAGE(WM_SENDOFFLINE, OnSendOffline) //自定义消息 3....
1、自定义控件RadarView用来画雷达的效果图,可以自定义属性包括 backgroundColor:背景颜色 circleNum:圆的数量 startColor:开始颜色 endColor:结束颜色 lineColor:线的颜色 2、通过Handler循环发送...
IPMSG源代码,可以在局域网内自由通讯和转发文件
演示2和演示3使用的都是SendMessage来发放通知消息,所以不能异步通讯,SetWindowText不能跨进程使用,PostMessage只能在WM_USER以上的自定义消息中才能传递指针,那么如何才能异步通讯呢?我还没有好主意,就用...
闪烁应用程序窗体,不用timer控件,C#源代码[DllImport("User32")] public static extern bool FlashWindow(IntPtr hWnd, bool bInvert); try { int MyTimes = 50; int MyTime = 50; for (int MyCount = 0; ...
编写程序实现进程的管道通信。用系统调用pipe( )建立一管道,二个子进程P1... Child 1 is sending a message! Child 2 is sending a message! 父进程从管道中读出二个来自子进程的信息并显示(要求先接收P1,后P2)。