C#在运行时重写方法
我有两个问题 1) 我发现了一个小宝石的代码 太好了。但是它重写了WndProc方法,所以要使用它,我必须在设计时撕掉掉表单上的FlowLayoutPanel子类FlowLayoutPanel,然后最终实例化我的新类并手动创建所有属性,并将对控件的所有引用更改为this.Controls[“ControlName”]。(或者我想我可以创建一个类级别的变量,它本质上就是控件最初的样子,但是当它没有在任何地方声明时,它们如何允许您对它使用intellisense?) 所以现在我只是想知道是否真的有一种运行时的方法来实现它 我可以做一些简单的事情吗,其中MainPanel是控件的名称:C#在运行时重写方法,c#,methods,runtime,overriding,C#,Methods,Runtime,Overriding,我有两个问题 1) 我发现了一个小宝石的代码 太好了。但是它重写了WndProc方法,所以要使用它,我必须在设计时撕掉掉表单上的FlowLayoutPanel子类FlowLayoutPanel,然后最终实例化我的新类并手动创建所有属性,并将对控件的所有引用更改为this.Controls[“ControlName”]。(或者我想我可以创建一个类级别的变量,它本质上就是控件最初的样子,但是当它没有在任何地方声明时,它们如何允许您对它使用intellisense?) 所以现在我只是想知道是否真的有一
MainPanel = (SmoothScrollingFlowLayoutPanel)MainPanel
SmoothScrollIntercept intercept = new SmoothScrollIntercept(myControl.Handle);
// myControl is now using smooth scrolling, without inheriting from the control
不会那么容易吧?尽管如此,这还是很烦人,因为我仍然需要子类(这可能是一个很好的设计决策,但我希望有一次性的自由)。因此,是否可以将代码放入FlowLayoutPanel的父级中,如下所示:
private Delegate void WndProcHandler(ref Message m);
private WndProcHandler w;
public void SomeCode() {
w = MainPanel.WndProc; // get reference to existing wndproc method
MainPanel.WndProc = WndProcSmoothScroll; //replace with new method
}
private void WndProcSmoothScroll(ref Message m) { // make smooth scrolling work
if (
(m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL)
&& (((int)m.WParam & 0xFFFF) == 5)
) {
m.WParam = (IntPtr)(((int)m.WParam & ~0xFFFF) | 4);
}
if (w != null) { w(); }
base.WndProc(ref m);
}
我意识到这可能很幼稚。我将WndProc方法视为一个事件,而事实并非如此
2) 那么我的第二个问题是,如果WndProc是一个事件而不是一个方法,我将如何做同样的事情存储一个事件的原始处理程序列表的副本,首先安装我自己的事件处理程序以运行,然后调用所有原始的事件处理程序
美味小吃
如果有人感兴趣,我注意到平滑滚动代码中可能存在优化:
//m.WParam = (IntPtr)(((int)m.WParam & ~0xFFFF) | 4);
m.WParam = (IntPtr)((int)m.WParam ^ 1);
由于我们希望将最后16位从5位转换为4位,因此我们可以将最后一位(XOR)翻转,而不是先“和”再“或”。不,您所要求的是不可能的。您必须像以前一样创建子类 即使这是一个事件,你也不能做你所追求的。事件的公共界面仅公开
添加
和删除
;无法获取或分配附加到事件的实际委托
然而,从另一个角度来看问题,您可能能够利用接口来完成您所寻找的最终结果
编辑
再次查看代码后,
IMessageFilter
将无法工作,因为您无法在PreFilterMessage
中修改消息;您只能检查或抑制它。在这一点上,最好的办法是覆盖父级表单中的WndProc
,并尝试在那里进行操作。您的问题似乎没有通用的解决方案。如果我正确理解您的问题,您所要做的就是在运行时重写WndProc
。如果是这样,你所需要的只是一点Win32魔法
每个控件都有一个“句柄”,用于标识它,以便操作系统可以向它发送消息。此句柄通过每个控件上的handle
属性公开。基本的Win32系统实际上允许您监听任何控件的WndProc
,只要您有它的句柄。这意味着您不必从Winforms控件继承来修改其Win32行为System.Windows.Forms.NativeWindow
包装了此基础功能
下面是一个您可以如何实现这一目标的示例:
class SmoothScrollIntercept : System.Windows.Forms.NativeWindow
{
public SmoothScrollIntercept(IntPtr hWnd)
{
// assign the handle and listen to this control's WndProc
this.AssignHandle(hWnd);
}
protected override void WndProc(ref Message m)
{
// listen to WndProc here, do things
if ((m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL)
&& (((int)m.WParam & 0xFFFF) == 5))
{
m.WParam = (IntPtr)(((int)m.WParam & ~0xFFFF) | 4);
}
base.WndProc(ref m);
}
}
然后在代码隐藏中,将截取连接到控件:
MainPanel = (SmoothScrollingFlowLayoutPanel)MainPanel
SmoothScrollIntercept intercept = new SmoothScrollIntercept(myControl.Handle);
// myControl is now using smooth scrolling, without inheriting from the control
谢谢你,扎克,但我已经完成了让我的窗口平滑滚动。我只是讨厌我必须从设计器中删除控件并动态执行所有操作。@emtucifer:没问题,我只是想让您知道,您不必从
FlowLayoutPanel
继承来覆盖它的WndProc
。哦,现在我明白了。谢谢我承认我没有读得像我应该读的那样仔细。这是有用的!事实上,你是100%正确的,这是我一直在寻找的答案。(拍了拍额头)谢谢。我知道一点javascript,在那里你可以用方法来实现这一点,所以我只是好奇。现在我想到了,我可以继承FlowLayoutPanel和UserControl吗?这样我的新SmoothScrollingFlowLayoutPanel就可以在设计时放到表单上了?@Emtucifor:不,没有一种.NET语言允许多重继承。但是,您可以从FlowLayoutPanel
继承并将控件放入工具箱中。如果控件在您的项目中,它应该自动显示在那里。如果它在另一个项目中,您必须右键单击工具箱,单击“选择项…”,然后浏览到该.dll并以这种方式添加控件。这很有帮助。我知道子类UserControls会显示在工具箱中,但不知何故,我忽略了子类常规控件也会出现在工具箱中。谢谢