我的问题:
我希望在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 窗体设计器生成的代码 #region Windows 窗体设计器生成的代码 /**/ /// <summary> /// 设计器支持所需的方法 - 不要使用代码编辑器修改 /// 此方法的内容。 /// </summary> private void InitializeComponent() ... { this .propertyGrid1 = new System.Windows.Forms.PropertyGrid(); this .SuspendLayout(); // // propertyGrid1 // this .propertyGrid1.CommandsVisibleIfAvailable = true ; this .propertyGrid1.LargeButtons = false ; this .propertyGrid1.LineColor = System.Drawing.SystemColors.ScrollBar; this .propertyGrid1.Location = new System.Drawing.Point( 64 , 56 ); this .propertyGrid1.Name = " propertyGrid1 " ; this .propertyGrid1.Size = new System.Drawing.Size( 408 , 384 ); this .propertyGrid1.TabIndex = 0 ; this .propertyGrid1.Text = " propertyGrid1 " ; this .propertyGrid1.ViewBackColor = System.Drawing.SystemColors.Window; this .propertyGrid1.ViewForeColor = System.Drawing.SystemColors.WindowText; // // Form1 // this .AutoScaleBaseSize = new System.Drawing.Size( 6 , 14 ); this .ClientSize = new System.Drawing.Size( 592 , 566 ); this .Controls.Add( this .propertyGrid1); this .Name = " Form1 " ; this .Text = " Form1 " ; this .Load += new System.EventHandler( this .Form1_Load); this .ResumeLayout( false ); } #endregion /**/ /// <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],随后我进行了绑定,然而运行却没有效果,不知道为什么? 此外,我也不知道我的方法是否正确?