【问题标题】:How to use WndProc in WPF UserControl? [duplicate]如何在 WPF 用户控件中使用 WndProc? [复制]
【发布时间】:2019-09-01 21:28:28
【问题描述】:

我试图在 ElementHost 中托管的 WPF UserControl 上捕获 WM_LBUTTONDOWN WM_MOUSEMOVE WM_LBUTTONUP

这是我当前的代码,它根本不起作用:

 public CoreUI()
{
    InitializeComponent();

    var s = HwndSource.FromVisual(this) as HwndSource;
    s?.AddHook(WndProcHook);
}

   public const int WM_LBUTTONDOWN = 0x201;
   public const int WM_LBUTTONUP = 0x202;
   public const int WM_MOUSEMOVE = 0x200;

static IntPtr WndProcHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {

   switch (m.Msg)
        {

            case WM_LBUTTONDOWN:
            MessageBox.Show("LBUTTONDOWN");
            handled = true;
            break;

            case WM_MOUSEMOVE:
            MessageBox.Show("MOUSEMOVE");
            handled = true;
            break;


            case WM_LBUTTONUP:
            MessageBox.Show("LBUTTONUP");
            handled = true;
            break;

        }
        return IntPtr.Zero;
     }

它不工作,我怎样才能让它工作?

【问题讨论】:

  • @AlwaysLearning 你读过这个问题吗?我是说它在用户控件上根本不起作用!
  • @RossetaGotStoned “你有没有读过这个问题” - 你有没有读过副本?对于某些事情,您的代码与其他代码不匹配,我可以使用s?.AddHook 在您的代码中看到一个可能的错误。您似乎没有正确处理这种情况。此外,这可能不是执行该操作的最佳位置。这不是 WinForms
  • @RossetaGotStoned StackOverflow 社区可以帮助您解决编程难题。我相信我发布的第二个链接包含您问题的答案。这应该是提交问题之前向您提出的建议之一。
  • @MickyD s?.AddHook 工作正常,并且 KillingByCoding 的答案是正确的。
  • 因此 "...这可能不是执行该操作的最佳位置..."。很高兴听到你成功了

标签: c# wpf


【解决方案1】:

在阅读此答案之前,请考虑以下几点:

  • 在 System.Windows.Forms 应用程序中,每个控件都是一个具有自己的窗口句柄的窗口(即:Control.Handle
  • 在 WPF 应用程序中,只有顶级窗口才能获得窗口句柄,即:Application.Current.MainWindow
  • WPF 用户控件负责在其父窗口的缓冲区中绘制自己的内容,但它们实际上从未获得自己的窗口句柄。
  • 这就是为什么 new WindowInteropHelper(this).Handle 及其同类仅在从顶级窗口的代码调用时,并且仅在窗口完全初始化后才在 WPF 应用程序中有效。

以下代码演示了如何从 UserControl 挂钩主窗口的 WndProc...

CoreUI.xaml

<UserControl x:Class="StackOverflow.CoreUI"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:StackOverflow"
             mc:Ignorable="d" 
             d:DesignHeight="30" d:DesignWidth="100">
    <Grid>
        <TextBox Text="{Binding Text}" />
    </Grid>
</UserControl>

CoreUI.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;

namespace StackOverflow
{
    public class LogEventArgs : EventArgs {
        public string Message { get; set; }
    }

    public delegate void LogEventHandler(object sender, LogEventArgs e);

    public partial class CoreUI : UserControl
    {
        public string Text { get; set; } = "Hello, world!";
        public LogEventHandler OnLog;

        public CoreUI()
        {
            InitializeComponent();
            DataContext = this;
        }

        public void HookWndProc(Window window)
        {
            var source = HwndSource.FromVisual(window) as HwndSource;
            source.AddHook(new HwndSourceHook(WndProc));
        }

        protected IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            MouseButtonEventArgs args;

            const int WM_LBUTTONDOWN = 0x0201;
            const int WM_LBUTTONUP = 0x0202;
            const int WM_MOUSEMOVE = 0x0200;
            ////Not required...
            //// REF: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest
            //const int WM_NCHITTEST = 0x0084;
            //const int HTCLIENT = 0x0001;

            switch (msg)
            {
                ////Not required...
                //case WM_NCHITTEST:
                //    {
                //        handled = true;
                //        return new IntPtr(HTCLIENT);
                //    }
                case WM_LBUTTONDOWN:
                    {
                        OnLog?.Invoke(this, new LogEventArgs() { Message = "LBUTTONDOWN" });
                        handled = true;
                        break;
                    }

                case WM_LBUTTONUP:
                    {
                        OnLog?.Invoke(this, new LogEventArgs() { Message = "LBUTTONUP" });
                        handled = true;
                        break;
                    }

                case WM_MOUSEMOVE:
                    {
                        OnLog?.Invoke(this, new LogEventArgs() { Message = "MOUSEMOVE" });
                        handled = true;
                        break;
                    }
            }
            return IntPtr.Zero;
        }
    }
}

MainWindow.xaml

<Window x:Class="StackOverflow.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:StackOverflow"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <local:CoreUI x:Name="coreUI" Height="30" VerticalAlignment="Top"/>
        <TextBox x:Name="textBox" Margin="0,30,0,0" TextWrapping="Wrap" Text="TextBox"/>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Windows;

namespace StackOverflow
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            textBox.Clear();
            coreUI.OnLog += new LogEventHandler(OnLogHandler);
        }

        protected void OnLogHandler(object sender, LogEventArgs e)
        {
            textBox.AppendText(e.Message + "\r\n");
        }

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);
            coreUI.HookWndProc(App.Current.MainWindow);
        }
    }
}

注意OnSourceInitialized() 的使用。如果您从OnInitialized() 尝试此操作,则主窗口尚未分配窗口句柄。

UserControl (CoreUI) 接收整个应用程序窗口的WM_LBUTTONDOWNWM_LBUTTONUPWM_MOUSEMOVE 事件。如果您的 UserControl 没有填满整个应用程序窗口,那么您的控件将负责通过检查鼠标坐标与其自身的边界来过滤这些事件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-14
    • 2012-03-09
    • 1970-01-01
    • 2014-06-04
    • 2012-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多