如何在外部 Win32 应用程序窗口上添加 WPF 覆盖? [英] How can I add a WPF overlay over an external Win32 application's window?

查看:37
本文介绍了如何在外部 Win32 应用程序窗口上添加 WPF 覆盖?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将 WPF 窗口附加为外部应用程序(例如记事本)的子窗口以提供叠加层.在研究了我可以在 SO 和 MSDN 上找到的所有答案后,我已经在我的 WPF 应用程序运行时在记事本的角落上创建了一个坚实的覆盖层.然而,

  • 一旦记事本获得焦点,叠加层就会消失,
  • 除了在记事本上显示的覆盖图外,覆盖图还单独显示为一个窗口
  • 记事本上的叠加层不会接收任何 MouseMove 事件(但单独的窗口会.

以下是演示问题的最小示例:

叠加.xaml

Overlay.xaml.cs

使用系统;使用 System.Windows;使用 System.Windows.Input;使用 System.Windows.Media;使用 System.Windows.Interop;命名空间 WindowControlTest{公共部分类叠加:窗口{IntPtr m_ParentHwnd;HwndSource m_HwndSource;公共覆盖(IntPtr parentHwnd){初始化组件();m_ParentHwnd = parentHwnd;}私有无效Window_MouseMove(对象发送者,MouseEventArgs e){Console.WriteLine("Overlay.Window_MouseMove:" + e.GetPosition(this));}私有无效Window_GotFocus(对象发送者,RoutedEventArgs e){Console.WriteLine("Overlay.Window_GotFocus");}私有无效Window_Loaded(对象发送者,RoutedEventArgs e){HwndSourceParameters 参数 = new HwndSourceParameters();parameters.WindowStyle = (int) (WindowStyles.WS_VISIBLE | WindowStyles.WS_CHILD);参数.SetPosition(0, 0);parameters.UsesPerPixelOpacity = true;parameters.SetSize((int)Width, (int)Height);parameters.ParentWindow = m_ParentHwnd;m_HwndSource = 新的 HwndSource(参数);m_HwndSource.CompositionTarget.BackgroundColor = Colors.Aqua;m_HwndSource.RootVisual = (Visual)Content;}}}

主窗口.xaml

MainWindow.xaml.cs - 查找和处理记事本并创建一个 Overlay

使用系统;使用 System.Text;使用 System.Windows;命名空间 WindowControlTest{公共部分类 MainWindow : 窗口{私有 IntPtr m_TargetHwnd;私有覆盖 m_Overlay;公共主窗口(){初始化组件();}私有无效 OnLoaded(对象发送者,RoutedEventArgs e){进程窗口();if (m_TargetHwnd != IntPtr.Zero){m_Overlay = new Overlay(m_TargetHwnd);m_Overlay.Show();}}私有无效进程Windows(){Win32.EnumWindows(delegate(IntPtr wnd, IntPtr 参数){String text = GetWindowText(wnd);Console.WriteLine("窗口:" + text);如果(文本.包含(记事本")){m_TargetHwnd = wnd;}返回真;}, IntPtr.Zero);}公共静态字符串 GetWindowText(IntPtr hWnd){int size = Win32.GetWindowTextLength(hWnd);如果(大小++ > 0){var builder = new StringBuilder(size);Win32.GetWindowText(hWnd, builder, builder.Capacity);返回 builder.ToString();}返回 String.Empty;}}}

(注意:许多 SO 问题解决了一个类似但不同的问题,例如

I am trying to attach a WPF Window as a child window of an external application such as Notepad to provide an overlay. Having researched all the answers I can find on SO and MSDN, I've got as far as creating a solid overlay over the corner of Notepad when my WPF application runs. However,

  • as soon as Notepad gains focus, the overlay disappears,
  • as well as the overlay showing on Notepad, the Overlay is also shown separately as a window
  • the overlay on Notepad does not receive any MouseMove events (but the separate window does.

Here is the minimal example to demonstrate the issue:

Overlay.xaml

<Window x:Class="WindowControlTest.Overlay"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="300" Width="300"
        Opacity="1"
        Background="Azure"
        MouseMove="Window_MouseMove"
        GotFocus="Window_GotFocus"
        Loaded="Window_Loaded"
        Title="Overlay" 
        WindowStyle="None"
        >
</Window>

Overlay.xaml.cs

using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Interop;

namespace WindowControlTest
{

    public partial class Overlay : Window
    {
        IntPtr m_ParentHwnd;
        HwndSource m_HwndSource;

        public Overlay(IntPtr parentHwnd)
        {
            InitializeComponent();
            m_ParentHwnd = parentHwnd;
        }

        private void Window_MouseMove(object sender, MouseEventArgs e)
        {
            Console.WriteLine("Overlay.Window_MouseMove: " + e.GetPosition(this));
        }

        private void Window_GotFocus(object sender, RoutedEventArgs e)
        {
            Console.WriteLine("Overlay.Window_GotFocus");
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            HwndSourceParameters parameters = new HwndSourceParameters();
            parameters.WindowStyle = (int) (WindowStyles.WS_VISIBLE | WindowStyles.WS_CHILD);
            parameters.SetPosition(0, 0);
            parameters.UsesPerPixelOpacity = true;
            parameters.SetSize((int)Width, (int)Height);
            parameters.ParentWindow = m_ParentHwnd;
            m_HwndSource = new HwndSource(parameters);
            m_HwndSource.CompositionTarget.BackgroundColor = Colors.Aqua;
            m_HwndSource.RootVisual = (Visual)Content;
        }
    }
}

MainWindow.xaml

<Window x:Class="WindowControlTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        Loaded="OnLoaded"
        >

    <StackPanel>
        <Label x:Name="stateLabel">Label</Label>
    </StackPanel>
</Window>

MainWindow.xaml.cs - Finds and handle to Notepad and creates an Overlay

using System;
using System.Text;
using System.Windows;

namespace WindowControlTest
{
    public partial class MainWindow : Window
    {
        private IntPtr m_TargetHwnd;
        private Overlay m_Overlay;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            processWindows();

            if (m_TargetHwnd != IntPtr.Zero)
            {
                m_Overlay = new Overlay(m_TargetHwnd);
                m_Overlay.Show();
            }
        }

        private void processWindows()
        {
            Win32.EnumWindows(delegate(IntPtr wnd, IntPtr param)
            {
                String text = GetWindowText(wnd);
                Console.WriteLine("Window: " + text);
                if (text.Contains("Notepad"))
                {
                    m_TargetHwnd = wnd;
                }
                return true;
            }, IntPtr.Zero);
        }

        public static string GetWindowText(IntPtr hWnd)
        {
            int size = Win32.GetWindowTextLength(hWnd);
            if (size++ > 0)
            {
                var builder = new StringBuilder(size);
                Win32.GetWindowText(hWnd, builder, builder.Capacity);
                return builder.ToString();
            }
            return String.Empty;
        }
    }
}

(Note: A number of SO questions address a similar but different issue, e.g. How to set Win32 window as owner of WPF window? assumes I am in control of the source code for the Win32 window, as do the examples I can find on MSDN.)

解决方案

I think your going the wrong path, instead of making your overlay a child of the notepad window, you should try and make notepad the child of your main window through an HwndHost and then maintain the overlay above the host.

Here's my blog post introducing a library I wrote for layering a WPF adorner over any hwnd hosted by an HwndHost. A simple web browser demo looks like this:

这篇关于如何在外部 Win32 应用程序窗口上添加 WPF 覆盖?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆