博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何通过自定义MessageFilter的方式利用按键方式操作控件滚动条[附源代码]
阅读量:6078 次
发布时间:2019-06-20

本文共 6179 字,大约阅读时间需要 20 分钟。

很长一段时间内,一直在做一个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之中。下图的就是我们将要演示的例子运行时的截图,为了简单起见,我直接通过一个作为Workspace。主菜单的两个菜单项分别代表两个模块,点击相应的菜单项后,会把相应的界面添加到Workspace中。在这里,我通过的方式定义Customer和Order模块的界面,当Customer和Order菜单被点击之后,会动态地在TabControl中添加相应的TabPage,并把相应的UserControl置于其中。由于整个TabControl的高度时固定的,而TabPage中显示的内容则依赖于具体的逻辑,所以对于内容过多的TabPage,将会有一个竖直滚动条。而我们需要通过按键的方式控制的就是当前TabPage的这个滚动条。

下面是该Form相关的代码,静态属性ActiveTabPage代表当前显示的TabPage。UserInfo和OrderInfo是两个UserControl,代表与具体模块相关的界面呈现。

1: using System;
2: using System.Collections.Generic;
3: using System.Windows.Forms;
4: 
5: namespace MessageFilterDemos
6: {
7:     public partial class MainForm : Form
8:     {
9:         public static TabPage ActiveTabPage
10:         { get;private set; }
11: 
12:         private IDictionary
keyedViews
13:         { get; set; }
14: 
15:         public MainForm()
16:         {
17:             InitializeComponent();
18:             this.keyedViews = new Dictionary
();
19:         }
20: 
21:         protected override void OnLoad(EventArgs e)
22:         {
23:             base.OnLoad(e);
24:             this.keyedViews.Add("CustomerInfo", new CustomerInfo());
25:             this.keyedViews.Add("OrderInfo", new OrderInfo());
26:         }
27: 
28:         private void Show(string key, string text, UserControl view)
29:         {
30:             if (!this.mainWorkspace.TabPages.ContainsKey(key))
31:             {
32:                 this.mainWorkspace.TabPages.Add(key, text);
33:                 this.mainWorkspace.TabPages[key].Controls.Add(view);
34:                 this.mainWorkspace.TabPages[key].AutoScroll = true;
35:             }
36:             this.mainWorkspace.SelectedTab = this.mainWorkspace.TabPages[key];
37:             ActiveTabPage = this.mainWorkspace.TabPages[key];
38:         }
39: 
40:         private void ordeToolStripMenuItem_Click(object sender, EventArgs e)
41:         {
42:             this.Show("OrderInfo", "Order", keyedViews["OrderInfo"]);
43:         }
44: 
45:         private void customerToolStripMenuItem_Click(object sender, EventArgs e)
46:         {
47:             this.Show("CustomerInfo", "Customer", keyedViews["CustomerInfo"]);
48:         }
49: 
50:         private void mainWorkspace_SelectedIndexChanged(object sender, EventArgs e)
51:         {
52:             ActiveTabPage = this.mainWorkspace.SelectedTab;
53:         }
54:     }
55: }

 

三、自定义MessageFilter

现在我们进入重点话题,如何创建我们需要的自定义MessageFilter,由于我们这个MessageFilter旨在控制TabPag的滚动条,我们将其命名为ScrollbarControllerMessageFilter。ScrollbarControllerMessageFilter实现了接口。下面是IMessageFilter的定义,它仅仅包含一个唯一的成员:PreFilterMessage,对消息的拦截、筛选操作就实现在这里。而Bool类新的返回值表示是否继续将消息分发的目标对象。

1: public interface IMessageFilter
2: {
3:     [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
4:     bool PreFilterMessage(ref Message m);
5: }

下面是ScrollbarControllerMessageFilter的定义,代码不是很复杂,在这里只需简单的介绍一下流程:在PreFilterMessage方法中,先判断当前的TabPage是否存在,如果不存在,则不加干涉;然后通过的Msg属性确定当前事件是否是KeyDown,如果不是则直接返回;最后根据的WParam属性判断当前的按键是否是PageUp或者PageDown,并相应的向目标对象(当前的TabPage)发送一个关于向上或者向下滚动的消息。消息的发送通过调用Native方法SendMessage实现。

1: using System;
2: using System.Runtime.InteropServices;
3: using System.Windows.Forms;
4: 
5: namespace MessageFilterDemos
6: {
7:     public class ScrollbarControllerMessageFilter: IMessageFilter
8:     {
9: 
10:         private const int WM_KEYDOWN = 0x100;//Key down
11:         private const int WM_VSCROLL = 277; //Scroll
12:         private const int SB_PAGEUP = 2; // Scroll Up
13:         private const int SB_PAGEDOWN = 3; //Scroll Down
14: 
15:         #region IMessageFilter Members
16: 
17:         [DllImport("user32.dll")]
18:         static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
19: 
20:         public bool PreFilterMessage(ref Message m)
21:         {
22:             if (MainForm.ActiveTabPage == null)
23:             {
24:                 return false;
25:             }
26: 
27:             if (WM_KEYDOWN != m.Msg)
28:             {
29:                 return false;
30:             }
31: 
32:             if (m.WParam.ToInt32() == (int)(Keys.PageUp))
33:             {
34:                 SendMessage(MainForm.ActiveTabPage.Handle, WM_VSCROLL, SB_PAGEUP, 0);
35:                 return true;
36:             }
37: 
38:             if (m.WParam.ToInt32() == (int)(Keys.PageDown))
39:             {
40:                 SendMessage(MainForm.ActiveTabPage.Handle, WM_VSCROLL, SB_PAGEDOWN, 0);
41:                 return true;
42:             }
43: 
44:             return false;
45:         }
46: 
47:         #endregion
48:     }
49: }

四、注册ScrollbarControllerMessageFilter

对MessageFilter的注册很简单,仅仅需要的是调用的AddMessageFilter方法即可。实例代码下载地址:。

1: using System;
2: using System.Windows.Forms;
3: 
4: namespace MessageFilterDemos
5: {
6:     static class Program
7:     {
8:         [STAThread]
9:         static void Main()
10:         {
11:             Application.AddMessageFilter(new ScrollbarControllerMessageFilter());
12:             Application.EnableVisualStyles();
13:             Application.SetCompatibleTextRenderingDefault(false);
14:             Application.Run(new MainForm());
15:         }
16:     }
17: }
作者:蒋金楠
微信公众账号:大内老A
微博:
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号
蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
你可能感兴趣的文章
c++ MFC 利用组码 自己生成DXF
查看>>
MVP模式在Android实际项目中的应用和优化
查看>>
3016: [Usaco2012 Nov]Clumsy Cows
查看>>
React 深度学习:ReactSideEffectTags
查看>>
dmidecode输出详解
查看>>
统计单词个数
查看>>
Vue.js 第二天: 组件
查看>>
Django - Celery异步任务队列
查看>>
Android自定义View全解
查看>>
<HTTP权威指南>记录 ---- HTTP连接管理
查看>>
关于oss上传文件的封装函数
查看>>
一步一步学习SignalR进行实时通信_8_案例2
查看>>
windows环境利用hexo+github搭建个人博客
查看>>
小猿圈之浅析Java面试需要注意的方面?
查看>>
Prometheus学习系列(十四)之配置规则
查看>>
Humble Numbers
查看>>
SpringCloud(六):Zuul的多个使用场景
查看>>
用css怎么制作下拉列表
查看>>
从零开始搭建Android组件化框架
查看>>
day20 异常处理和约束
查看>>