如何在外部Win32应用程序窗口上添加WPF覆盖?

Tim*_* MB 1 c# windows wpf winapi xaml

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

  • 一旦记事本获得焦点,叠加层就会消失,
  • 以及在记事本上显示的叠加层,叠加层也单独显示为窗口
  • 记事本上的叠加层不会收到任何MouseMove事件(但单独的窗口会.

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

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>
Run Code Online (Sandbox Code Playgroud)

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

MainWindow.xaml.cs - 查找和处理记事本并创建一个 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

(注意:一些SO问题解决了类似但不同的问题,例如 如何将Win32窗口设置为WPF窗口的所有者?假设我控制Win32窗口的源代码,我可以在MSDN上找到的示例.)

Mic*_*ton 5

我认为你走错了道路,而不是让你的叠加层成为记事本窗口的子窗口,你应该尝试通过HwndHost将记事本作为主窗口的子窗口,然后将叠加层保持在主机上方.

这是我的博客文章,介绍了我为在HwndHost托管的任何hwnd上分层WPF adorner而编写的.一个简单的Web浏览器演示如下所示:

快照http://i58.tinypic.com/kej3bd.png