关于vs2003中propertygrid的keypress事件无效的解决过程

我的问题:

我希望在propertygrid输入数据时检测enter和ESC键,然后分别执行相应的动作,但是在propertygrid提供的keydown/keypress/keyup事件中输入指令后,运行时并没有执行设定的动作?
就是绑定一个普通的类,在改变其包含的某一个属性的值后,按回车键就可以执行某一个动作,而如果按ESC键就重置该类的值。
而我在propertygrid提供的keydown/keypress/keyup事件中加入的代码并不执行。
希望大家帮帮忙? 

微软工程师的解答:

解答一:

以下帖子解释了为什么PropertyGrid无法对这些Key事件做出反应:http://www.thescripts.com/forum/thread631744.html

PropertyGrid has an internal grid and this is this one that
receives the keyboard events. If you really want to have a handler for
those events, you can access the internal grid (type is
System.Windows.Forms.PropertyGridInternal.Property GridView) with
myPropGrid.Controls[2]. But depending on your needs, this can be
tricky to play inside the private parts of the grid.

谢谢
Jialiang Ge

解答二:

PropertyGrid其实是有三个子窗口组成的:PropertyGridToolBar, PropertyGridView和Description Pane。其中我们在其中设置属性的子窗口是PropertyGridView。

要想获得用户按键消息,一个办法是找到PropertyGridView的handle, 然后调用SetWindowLong函数将其原来的消息处理函数替换成我们自己的函数。这样我们就能自己的函数中捕获任何发送到PropertyGridView中的消息了(SetWindowLong函数在这里其实起了钩子函数的作用)。

当然我们只是想得到按键消息,关于消息的处理,还是让原来的消息处理函数来处理。

以下是个例子:

using  System.Runtime.InteropServices;
public   partial   class  Form1 : Form
    
{
        
int GWL_WNDPROC = -4;
        
int WM_KEYUP = 0x101;
        
int VK_RETURN = 0xD;
        
int VK_ESCAPE = 0x1B;

        [DllImport(
"user32.dll")]
        
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
        [DllImport(
"user32.dll")]
        
static extern int SetWindowLong(IntPtr hWnd,int nIndex,WindowProcDelegate dwNewLong);
        [DllImport(
"user32.dll")]
        
static extern bool EnumChildWindows(IntPtr hWndParent, ChildWindowDelegate lpEnumFunc, string wndCaption);
        [DllImport(
"user32.dll")]
        
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
        [DllImport(
"user32.dll")]
        
static extern IntPtr CallWindowProc(int lpPrevWndFunc, IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

        
delegate bool ChildWindowDelegate(IntPtr hwndChild, string wndCaption);
        
delegate IntPtr WindowProcDelegate(IntPtr hwnd,int msg,IntPtr wParam,IntPtr lParam);

        
private bool EnumChildProc(IntPtr hwndChild, string wndCaption)
        
{
            StringBuilder windowName 
= new StringBuilder(255);
            GetWindowText(hwndChild, windowName, 
255);
            
if (windowName.ToString().Equals(wndCaption))
            
{
                expectedChild 
= hwndChild;
                
return false;
            }

            
else
            
{
                
return true;
            }

        }

        
private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam)
        
{
            
if (msg == WM_KEYUP)
            
{
                
if (wParam.ToInt32() == VK_RETURN)
                
{
                    Console.WriteLine(
"enter key is pressed");
                }

                
else if (wParam.ToInt32() == VK_ESCAPE)
                
{
                    Console.WriteLine(
"escape key is pressed");
                }
          
            }

            
// let the default procedure handle the Windows messages
            IntPtr result = CallWindowProc(defaultProc, expectedChild, msg, wParam, lParam);
            
return result;
        }

        
int defaultProc;
        IntPtr expectedChild;

        
public Form7()
        
{
            InitializeComponent();           
           
        }


        
private void Form1_Load(object sender, EventArgs e)
        
{
            
bool result = EnumChildWindows(this.propertyGrid1.Handle, new ChildWindowDelegate(EnumChildProc), "PropertyGridView");
            
if (result == false)
            
{
                defaultProc 
= GetWindowLong(expectedChild, GWL_WNDPROC);
                SetWindowLong(expectedChild, GWL_WNDPROC, 
new WindowProcDelegate(WindowProc));
            }


            MyClass p 
= new MyClass();
            p.ID 
= 1;
            p.Name 
= "aa";

            
this.propertyGrid1.SelectedObject = p;
        }
      
    }

    

    
class  MyClass
    
{
        
private int id;
        
public int ID
        
{
            
get return id; }
            
set { id = value; }
        }

        
private string name;
        
public string Name
        
{
            
get return name; }
            
set { name = value; }
        }

    }
   


希望对你有帮助。

刘婷
在线技术支持工程师
微软全球技术支持中心

我的测试是按照解答二的方法进行的,但是出现了错误,反馈如下:

首先谢谢你的帮助,但是我按照您的说明进行,启动后一旦PropertyGrid获得焦点,就出现错误,信息为:
未处理的“System.NullReferenceException”类型的异常出现在 system.windows.forms.dll 中。

其他信息: Object reference not set to an instance of an object.

请问是什么原因?
代码如下:

using  System;
using  System.Drawing;
using  System.Collections;
using  System.ComponentModel;
using  System.Windows.Forms;
using  System.Data;
using  System.Runtime.InteropServices;
using  System.Text;

namespace  WindowsApplication1
{
/// <summary>
/// Form1 的摘要说明。
/// </summary>

public class Form1 : System.Windows.Forms.Form
{
int GWL_WNDPROC = -4;
int WM_KEYUP = 0x101;
int VK_RETURN = 0xD;
int VK_ESCAPE = 0x1B;

[DllImport(
"user32.dll")]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport(
"user32.dll")]
static extern int SetWindowLong(IntPtr hWnd,int nIndex,WindowProcDelegate 
dwNewLong);
[DllImport(
"user32.dll")]
static extern bool EnumChildWindows(IntPtr hWndParent, ChildWindowDelegate 
lpEnumFunc, 
string wndCaption);
[DllImport(
"user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int 
nMaxCount);
[DllImport(
"user32.dll")]
static extern IntPtr CallWindowProc(int lpPrevWndFunc, IntPtr hWnd, int 
msg, IntPtr wParam, IntPtr lParam);

delegate bool ChildWindowDelegate(IntPtr hwndChild, string wndCaption);
delegate IntPtr WindowProcDelegate(IntPtr hwnd,int msg,IntPtr 
wParam,IntPtr lParam);


private System.Windows.Forms.PropertyGrid propertyGrid1;
/// <summary>
/// 必需的设计器变量。
/// </summary>

private System.ComponentModel.Container components = null;

public Form1()
{
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();

//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}


/// <summary>
/// 清理所有正在使用的资源。
/// </summary>

protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null
{
components.Dispose();
}

}

base.Dispose( disposing );
}


Windows 窗体设计器生成的代码

/// <summary>
/// 应用程序的主入口点。
/// </summary>

[STAThread]
static void Main() 
{
Application.Run(
new Form1());
}


private bool EnumChildProc(IntPtr hwndChild, string wndCaption)
{
StringBuilder windowName 
= new StringBuilder(255);
GetWindowText(hwndChild, windowName, 
255);
if (windowName.ToString().Equals(wndCaption))
{
expectedChild 
= hwndChild;
return false;
}

else
{
return true;
}

}

private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr 
lParam)
{
if (msg == WM_KEYUP)
{
if (wParam.ToInt32() == VK_RETURN)
{
Console.WriteLine(
"enter key is pressed");
}

else if (wParam.ToInt32() == VK_ESCAPE)
{
Console.WriteLine(
"escape key is pressed");
}
          
}

    IntPtr result 
=IntPtr.Zero;
try
{
// let the default procedure handle the Windows messages
result = CallWindowProc(defaultProc, expectedChild, msg, wParam, lParam);
}

catch(Exception e)
{
Console.WriteLine(e.Message);
}

return result;
}

int defaultProc;
IntPtr expectedChild;
private void Form1_Load(object sender, EventArgs e)
{
bool result = EnumChildWindows(this.propertyGrid1.Handle, new 
ChildWindowDelegate(EnumChildProc), 
"PropertyGridView");
if (result == false)
{
defaultProc 
= GetWindowLong(expectedChild, GWL_WNDPROC);
SetWindowLong(expectedChild, GWL_WNDPROC, 
new 
WindowProcDelegate(WindowProc));
}


MyClass p 
= new MyClass();
p.ID 
= 1;
p.Name 
= "aa";

this.propertyGrid1.SelectedObject = p;
}
      

class MyClass
{
private int id;
public int ID
{
get return id; }
set { id = value; }
}

private string name;
public string Name
{
get return name; }
set { name = value; }
}

}
   

}

}

微软工程师的解答:

谢谢你的反馈!

我原先是在VS2005中做了测试。现在在VS.NET2003中测试了一下,确实看到了你说的问题。

这个问题的原因对于从unmanaged code到managed code的回调,在回调方法被调用时,送传递的delegate必须是active的,不能正准备垃圾回收。在我们的程序中有两个回调,一个是EnumChildWindows, 另一个是SetWindowLong。对于前者,找到PropertyGridView子窗口的handle以后就不需要再回调了,因此我们可以将所传递的ChildWindowDelegate声明为局部变量;对于后者,只要你在PropertyGrid中有任何操作,对应的回调方法都将被执行,因此所传递的WindowProcDelegate变量必须是全局的。

因此解决的办法是申明一个全局的WindowProcDelegate变量,然后传递给SetWindowLong函数。

public partial class Form1 : Form
{
     ...
     WindowProcDelegate windowProcDelegate;
     private void Form1_Load(object sender, EventArgs e)
    {
         windowProcDelegate = new WindowProcDelegate(WindowProc);
         bool result = EnumChildWindows(this.propertyGrid1.Handle, new ChildWindowDelegate(EnumChildProc), "PropertyGridView");
            if (result == false)
            {
                defaultProc = GetWindowLong(expectedChild, GWL_WNDPROC);
                SetWindowLong(expectedChild, GWL_WNDPROC, windowProcDelegate);
            }
            MyClass p = new MyClass();
            p.ID = 1;
            p.Name = "aa";
            this.propertyGrid1.SelectedObject = p;
     }        
}

希望对你有帮助。

刘婷
在线技术支持工程师
微软全球技术支持中心

=========================================================

至此问题解决。

未完的路:

如果按照解答一的方法进行,我目前没有测试成功,我的思路是找到PropertyGrid中的System.Windows.Forms.PropertyGridInternal.PropertyGridView子控件,然后将其KeyPress事件绑定到一个指定的处理过程上来实现需要的功能。但是我在VS2003中发现PropertyGridView并不是网上说的Controls[2],而是Controls[1],随后我进行了绑定,然而运行却没有效果,不知道为什么?

此外,我也不知道我的方法是否正确?

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值