我的问题:
我希望在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],随后我进行了绑定,然而运行却没有效果,不知道为什么?
此外,我也不知道我的方法是否正确?
本文介绍了解决PropertyGrid中按键监听问题的方法,包括直接修改内部窗口的消息处理函数和利用内部PropertyGridView控件两种方案。
706

被折叠的 条评论
为什么被折叠?



