C# 在运行时重写方法

发布于 2024-09-13 23:38:09 字数 1665 浏览 10 评论 0原文

我有两个问题。

1)我发现了一些关于如何制作的代码控件滚动平滑

伟大的。但它重写了 WndProc 方法,因此要使用它,我必须撕掉在设计时放在表单上的 FlowLayoutPanel,子类化 FlowLayoutPanel,然后最终实例化我的新类并手动创建所有属性并更改所有引用控件为 this.Controls["ControlName"]。 (或者我想我可以创建一个类级变量,它本质上就是控件最初的内容,尽管当它没有在任何地方声明时,它们如何让你在它上使用智能感知?)

所以现在我只是想知道实际上是否有一种运行时的方式来做到这一点。

我可以做一些简单的事情吗,其中 MainPanel 是控件的名称:

MainPanel = (SmoothScrollingFlowLayoutPanel)MainPanel

这不可能那么容易,不是吗?即便如此,这还是很烦人,因为我仍然必须拥有子类(这可能是一个很好的设计决策,但我希望能够自由地一次性使用它)。那么是否可以将代码放入 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),而不是比 AND 然后 OR。

I have two questions.

1) I found a little gem of code for how to make a control scroll smoothly.

Great. But it overrides the WndProc method, so to use it, I had to tear out the FlowLayoutPanel I'd dropped on the form at design time, subclass FlowLayoutPanel, and then finally instantiate my new class and create all the properties manually and change all references to the control to be this.Controls["ControlName"]. (Or I guess I could make a class-level variable which is essentially what the control originally was, though how do they let you use intellisense on it when it's not declared anywhere?)

So now I'm just wondering if there was in fact a runtime way to do it.

Can I do something simple as this, where MainPanel is the name of the control:

MainPanel = (SmoothScrollingFlowLayoutPanel)MainPanel

It can't be that easy, can it? Even so, it's annoying because I still have to have the subclass (which may be a good design decision but I'd like the freedom to one-off it). So would it be possible to put the code into the parent of the FlowLayoutPanel something like this:

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);
  }

I realize this is probably pretty naive. I'm treating the WndProc method like it is an event, and it's not.

2) So then my second question is, if WndProc was an event instead of a method, how would I do the same thing—store a copy of the original list of handlers for an event, install my own event handler to run first, then call all the original event handlers?

TASTY BITS

In case anyone is interested I noticed an optimization possible in the smooth scrolling code:

//m.WParam = (IntPtr)(((int)m.WParam & ~0xFFFF) | 4);
m.WParam = (IntPtr)((int)m.WParam ^ 1);

Since we want to turn the last 16 bits from 5 to 4, we can just flip the last bit (XOR) rather than AND then OR.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

岁月静好 2024-09-20 23:38:09

如果我正确理解您的问题,您所需要做的就是在运行时覆盖 WndProc 。如果是这样,您所需要的只是一点 Win32 魔法。

每个控件都有一个“句柄”来识别它,以便操作系统可以向它发送消息。该句柄通过每个控件上的 Handle 属性公开。实际上,底层 Win32 系统允许您监听任何控件的 WndProc,只要您拥有该控件的句柄即可。这意味着您不必继承 Winforms 控件即可修改其 Win32 行为。 .NET 中的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);
    } 
}

然后在后面的代码中,将拦截附加到控件:

SmoothScrollIntercept intercept = new SmoothScrollIntercept(myControl.Handle);

// myControl is now using smooth scrolling, without inheriting from the control

If I understand your question correctly, all you want to do is override WndProc at runtime. If so, all you need is a little Win32 magic.

Every control has a "handle" which identifies it so the operating system can send messages to it. This handle is exposed through the Handle property on every control. The underlying Win32 system actually allows you to listen to any control's WndProc as long as you have its handle. This means that you don't have to inherit from a Winforms control to modify its Win32 behavior. System.Windows.Forms.NativeWindow in .NET wraps this underlying functionality.

Here's an example of how you might accomplish this:

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);
    } 
}

Then in the code behind, attach the intercept to the control:

SmoothScrollIntercept intercept = new SmoothScrollIntercept(myControl.Handle);

// myControl is now using smooth scrolling, without inheriting from the control
伤痕我心 2024-09-20 23:38:09

不,你所要求的是不可能的。您必须像以前一样进行子类化。

即使这是一个事件,你也无法做你想做的事。事件的公共接口仅公开 addremove;无法获取或分配附加到事件的实际委托。

但是,从不同的角度看待问题,您也许可以利用IMessageFilter 接口,以实现您正在寻找的最终结果。

编辑

再次查看代码后,IMessageFilter 将不起作用,因为您无法修改 PreFilterMessage 中的消息;你只能检查或抑制它。此时最好的选择是重写父 Form 中的 WndProc 并尝试在那里进行操作。看起来您的问题没有通用的解决方案。

No, what you are asking for is impossible. You'll have to subclass as you did before.

Even if it were an event, you wouldn't be able to do what you're after. The public interface for an event only exposes add and remove; there is no way to obtain or assign the actual delegate(s) attached to the event.

However, looking at the problem from a different perspective, you may be able to make use of the IMessageFilter interface in order to accomplish the end result you're looking for.

Edit

After looking at your code again, IMessageFilter won't work, as you cannot modify the message within PreFilterMessage; you can only examine or suppress it. Your best bet at this point is to override WndProc in the parent Form and try to make your manipulations there. It doesn't look like there's a generic solution to your problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文