Winform中在Form上截取消息的两种方法比较

本文介绍了在WinForms中通过重载DefWndProc方法和使用IMessageFilter接口进行消息处理的方法,并对比了这两种方式的区别。

比较常用的是重载Form的DefWndProc方法,例如截取鼠标按下的消息:

protected   override   void  DefWndProc( ref  Message m)
        
{
            
if ( m.Msg ==  0x0201 )
            
{
                MessageBox.Show(m.Msg.ToString());
            }

            
else
            
{
                
base.DefWndProc (ref m);
            }

        }

还可以通过另一种办法,使用IMessageFilter 接口:

public   class  MessageFilter : IMessageFilter 
    {
                
public   bool  PreFilterMessage( ref  Message m) 
               {
                        
if  (m.Msg  ==   0x0201
                       {
                                MessageBox.Show(
" WM_LBUTTONDOWN is:  "   +  m.Msg);
                              
return   true ;
                       }
                      
return   false ;
        }

    }

然后使用Application.AddMessageFilter方法,例如:
private static MessageFilter msgFliter = new MessageFilter();
在Main方法中注册消息筛选器:
Application.AddMessageFilter(msgFliter);
如果要取消注册,可以调用Application.RemoveMessageFilter方法

 

实际上我们简单的区分这两者的不同:

 

DefWndProc : (From MSDN)

通常,仅在截获窗口消息并需要默认窗口过程来处理该消息时,才调用 DefWndProc 方法。不应调用 DefWndProc 将窗口消息发送到窗口,而应调用 Win32 SendMessage 函数。

Handle 属性为 0 时,调用 DefWndProc 是错误的。

给继承者的说明 在派生类中重写此方法可实现特定本机窗口或 .NET Windows 窗体 Control 的通用自定义消息处理。可以在调用此方法的基类实现(执行标准消息处理)之前或之后执行自定义处理,也可以使用自定义处理而不调用此方法的基类实现。 为应用程序添加自定义消息处理则可以通过 Application 类的 AddMessageFilter 方法来实现。

 

Application.AddMessageFilter() : (From MSDN)

使用消息筛选器来防止引发特定事件,或在将某事件传递给事件处理程序之前使用消息筛选器对其执行特殊操作。消息筛选器对特定线程是唯一的。

若要防止调度某消息,传递给该方法的 value 参数实例必须用处理该消息的代码重写 PreFilterMessage 方法。该方法必须返回 false

 

简单的说明下:

两者的功能实际上是不一样的:

有一个简单的例子来说明:

 我们新建一个Form,添加两种方法的消息处理Wm_Lbutton=0x021;另外在Form上添加一个Button,响应函数是MessageBox.Show();

代码如下:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            MessageDo();
        }
        private const int WM_Lbutton = 0x201;  
        protected override void DefWndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_Lbutton:
                    {
                        MessageBox.Show(" DefWndProc ");
                    }
                    break;
                case WM_ShowString:
                    {
                        MessageBox.Show("Show Stinrg Message have happened and Params are "+m.WParam.ToString()+":"+m.LParam.ToString());
                    }
                    break;
                default:
                    base.DefWndProc(ref m);
                    break;
            }
           
            //base.DefWndProc(ref m);
        }      
        private void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Button 1 was clicked just now !");
        }    
        private MessageFilter myfilter = new MessageFilter();
        private void MessageDo()
        {
            Application.AddMessageFilter(myfilter);
        }   
     
    }
    public class MessageFilter:IMessageFilter
    {
        public bool PreFilterMessage(ref Message m)
        {
            if (m.Msg == 0x201)
            {
                MessageBox.Show(" PreFilter ");
                return true;
            }
            return false;

        }
    }

运行后的结果是这样的:

 一直显示的  MessageBox() : PrefilTer,就算是Button按键也不能响应,整个UI的LButton都被他过滤了。

如果删除MessageFilter,结果就是: 当LButton发生的时候,只要不是在Button上面,就会MessageBox():DefWndProc

 

这个例子就可以看出最大的不同。

明天把这个东西补充下,有时间查查资料。

<think> 我们有一个需求:在WinForm的DataGridView中只显示200行数据。这意味着无论数据源中有多少数据,我们只想在DataGridView控件中显示前200条(或最新的200条,根据需求而定,这里假设是前200条)。 根据引用[1][2][3][4],我们知道DataGridView通常通过数据源(如DataTable、BindingList等)来绑定数据。因此,要实现只显示200行,我们不应该直接操作DataGridView的行,而是应该操作其数据源。 思路: 1. 如果数据源本身只有200条数据,那么绑定后自然只显示200条。 2. 如果数据源有超过200条数据,我们可以通过创建一个只包含前200条(或所需200条)的新数据源,然后将其绑定到DataGridView。 然而,有时我们可能希望数据源保持不变(比如有300条),但只显示其中200条。这时,我们可以考虑以下方法方法一:在绑定之前,从原始数据源中取出前200条记录,然后绑定这200条记录。 方法二:使用数据绑定,但通过一个中间层(如BindingSource)来过滤,但过滤条件设置为只取前200条。不过,BindingSource本身没有直接取前N行的功能,所以我们可以用LINQ来获取前200条。 但是注意,引用[3]中提到,直接操作DataGridView的行(如Rows.Clear)对于绑定数据源是不合适的,会导致错误。因此,我们应该操作数据源。 步骤(假设使用DataTable作为数据源): 1. 假设我们有一个DataTable(名为dtAll),它包含超过200条数据。 2. 我们可以创建一个新的DataTable(名为dt200),复制dtAll的结构。 3. 然后从dtAll中复制前200行到dt200。 4. 将DataGridView的数据源设置为dt200。 或者,我们可以使用LINQ的Take方法(如果数据源是List或BindingList等),例如: var first200 = originalList.Take(200).ToList(); dataGridView.DataSource = first200; 但是,如果数据源是动态增加的,我们可能需要每次增加数据时检查,确保显示的数据不超过200行。这时,我们可以考虑在数据源增加数据后,重新绑定前200条(或移除超出部分)。 另外,如果希望显示最新的200条(即最后200条),则可以使用Skip和Take,或者直接取索引较大的部分。 下面给出两种常见情况的代示例: 情况1:使用DataTable,显示前200条。 ```csharp // 假设dtSource是原始数据源(DataTable),有超过200条 DataTable dt200 = dtSource.Clone(); // 复制结构 for (int i = 0; i < 200 && i < dtSource.Rows.Count; i++) { dt200.ImportRow(dtSource.Rows[i]); } dataGridView1.DataSource = dt200; ``` 情况2:使用List<T>,显示前200条。 ```csharp // 假设listSource是原始数据源(List<T>) var first200 = listSource.Take(200).ToList(); dataGridView1.DataSource = first200; ``` 情况3:如果使用BindingList,并且希望动态添加时保持不超过200条,可以在添加数据时检查,并重新绑定前200条(或者移除最早添加的,保留最新的200条)。但注意,频繁重新绑定可能影响性能,而且会丢失当前用户的操作状态(如选择的行)。 另一种做法是使用BindingSource作为中介,我们可以设置BindingSource的Filter或Sort,然后通过位置来限制。但是BindingSource没有直接限制显示行数的属性。因此,我们可以考虑在BindingSource上使用LINQ,但要注意BindingSource本身不支持LINQ,我们需要将其转换为List再操作。 但是,如果希望实时更新(比如数据源增加时,自动只显示200条),我们可以考虑在数据源变化时,重新设置DataGridView的数据源为数据源的前200条(或者用BindingSource,然后重新设置BindingSource.DataSource为新的前200条)。但是这样会重新绑定整个网格,如果数据量大可能影响性能。 所以,如果数据源是动态的,且需要频繁更新,建议使用一个中间数据源(比如一个只包含200条记录的BindingList),每次原始数据源更新时,我们更新这个中间数据源(例如,添加新数据时,如果中间数据源已经达到200条,则移除第一条,然后添加新数据,这样始终保持200条)。 示例(使用BindingList和中间数据源): 假设我们有一个原始数据源BindingList<JData> allData,但我们绑定到DataGridView的是另一个BindingList<JData> displayData,它只包含allData中的最新200条。 但是,这样就需要维护两个列表,而且当allData变化时,我们需要同步更新displayData。我们可以通过事件来监听allData的变化(如ListChanged事件),然后更新displayData。 然而,为了简单起见,如果不需要保留原始数据源中的所有数据(即只保留最新的200条),那么我们可以直接操作原始数据源,当数据超过200条时,移除最早的数据。 例如: ```csharp private BindingList<JData> jDatas = new BindingList<JData>(); public Form1() { InitializeComponent(); dataGridView1.DataSource = jDatas; } private void AddData(JData data) { jDatas.Add(data); // 如果超过200条,移除第一条 while (jDatas.Count > 200) { jDatas.RemoveAt(0); } } ``` 这样,jDatas中最多只保留200条数据,DataGridView自然也就只显示200条。 总结: 1. 静态数据:在绑定时只取前200条。 2. 动态数据:在添加数据时控制数据源不超过200条(移除最早的数据),这样绑定就只需要绑定整个数据源(因为数据源最多200条)。 根据需求选择合适的方式。 注意:如果数据源是DataTable,那么移除行(RemoveAt)可能会比较慢,因为DataTable的移除操作会涉及索引的重新构建。如果性能要求高,且数据更新频繁,可以考虑使用其他数据结构(如环形缓冲区)来优化。 因此,针对你的问题,如果你希望DataGridView只显示200行,最直接的方法就是控制绑定到DataGridView的数据源只有200行。 如果原始数据源需要保留所有数据,而显示只需要200条,那么你可以使用一个中间数据源(比如另一个DataTable或List)来存放要显示的200条,然后绑定这个中间数据源。当原始数据源变化时,更新这个中间数据源(例如,只取原始数据源的前200条或最新200条)。 根据引用[4]中提到的BindingList的方式,我们可以类似地创建一个用于显示的BindingList,然后通过事件来更新它。 但是,最简单常用的方法还是直接控制绑定到DataGridView的数据源行数不超过200。 下面给出一个使用BindingList并动态维护显示200条(最新200条)的示例: ```csharp // 原始数据源(保存所有数据) private BindingList<JData> allData = new BindingList<JData>(); // 用于显示的数据源(只保留最新的200条) private BindingList<JData> displayData = new BindingList<JData>(); public Form1() { InitializeComponent(); dataGridView1.DataSource = displayData; // 监听allData的变化 allData.ListChanged += AllData_ListChanged; } private void AllData_ListChanged(object sender, ListChangedEventArgs e) { // 当allData变化时,更新displayData // 我们取allData的最后200条(也就是最新的200条) var newData = allData.Skip(Math.Max(0, allData.Count - 200)).Take(200).ToList(); displayData.Clear(); foreach (var item in newData) { displayData.Add(item); } } // 添加数据到allData private void AddData(JData data) { allData.Add(data); } ``` 这样,原始数据源allData会保存所有数据,而displayData只保留最新的200条,并显示在DataGridView中。 注意:频繁的Clear和Add可能会引起界面闪烁,如果数据量大也可能有性能问题。我们可以考虑更高效的方式,比如只添加一个数据时,如果allData.Count<=200,则直接添加到displayData,否则添加新数据并移除displayData中的第一个(如果displayData已经有200条)。但是这样需要处理各种ListChanged事件类型(如添加、删除、修改等),逻辑会复杂一些。 因此,根据实际需求选择方案。如果数据更新不频繁,上面的方法可以接受。如果更新频繁且数据量大,建议采用更高效的更新方式。 综上所述,你可以根据具体场景选择合适的方法。 </think>在WinForm中实现DataGridView仅显示200行数据,可以通过以下方法实现(根据数据绑定方式选择合适方案): ### 方法一:绑定数据源时截取数据(推荐) ```csharp // 从原始数据源截取前200条 var limitedData = originalData.Take(200).ToList(); // 绑定到DataGridView dataGridView1.DataSource = limitedData; ``` ### 方法二:使用BindingSource过滤 ```csharp // 创建BindingSource并设置过滤 BindingSource bs = new BindingSource(); bs.DataSource = originalData; // 原始数据源 bs.Filter = "RowNumber <= 200"; // 需要行号列支持 // 绑定到DataGridView dataGridView1.DataSource = bs; ``` ### 方法三:动态控制数据添加 ```csharp // 使用BindingList时控制添加数量 private BindingList<JData> jDatas = new BindingList<JData>(); private void AddData(JData newItem) { if (jDatas.Count < 200) { jDatas.Add(newItem); } else { // 达到200条后移除首条再添加 jDatas.RemoveAt(0); jDatas.Add(newItem); } } ``` ### 方法四:DataTable行限制 ```csharp DataTable dt = originalDataTable.Clone(); // 复制结构 // 仅导入200行 for (int i = 0; i < 200 && i < originalDataTable.Rows.Count; i++) { dt.ImportRow(originalDataTable.Rows[i]); } dataGridView1.DataSource = dt; ``` ### 重要注意事项: 1. **数据绑定模式**: - 直接操作`Rows`集合仅适用于**非数据绑定模式** - 数据绑定模式下应操作数据源(如DataTable/BindingList)[^3] 2. **性能优化**: ```csharp // 暂停绘制提升性能 dataGridView1.SuspendLayout(); // 更新数据操作... dataGridView1.ResumeLayout(); ``` 3. **显示行号**(可选): ```csharp // 添加行号列(引用自示例) dataGridView1.Columns.Insert(0, new DataGridViewTextBoxColumn { Name = "RowNumber", HeaderText = "#", Width = 40, ReadOnly = true }); ``` >[!TIP] >对于实时数据流,建议使用方法三(动态添加/移除),并结合`BindingList<T>`实现自动UI更新[^4]。若需要严格保持200行,添加新数据时移除最旧行即可。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值