【问题标题】:How can I add a WPF overlay over an external Win32 application's window?如何在外部 Win32 应用程序窗口上添加 WPF 覆盖?
【发布时间】:2015-06-16 16:14:50
【问题描述】:

我正在尝试将 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>

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 - 查找并处理记事本并创建一个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;
        }
    }
}

(注意:一些 SO 问题解决了一个类似但不同的问题,例如 How to set Win32 window as owner of WPF window? 假设我控制 Win32 窗口的源代码,我可以在 MSDN 上找到 examples。)

【问题讨论】:

    标签: c# wpf windows xaml winapi


    【解决方案1】:

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

    这是我的博客post 介绍了library 我写的用于在由 HwndHost 托管的任何 hwnd 上分层 WPF 装饰器。一个简单的 Web 浏览器演示如下所示:

    【讨论】:

    • 这是我们最终走的路。这并不完全令人满意,因为让您的子窗口看起来像原始的免费窗口并不总是微不足道的。但是,当将 WPF 保留为父级时,情况肯定会变得更好。
    • 另外 - 不错的博文!
    • 不错的博文和不错的演示项目。不幸的是,我们无法使用您的代码,因为它受版权保护(无许可证):/
    • 现在拥有 MIT 许可证。
    猜你喜欢
    • 2011-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-29
    • 2011-03-09
    • 1970-01-01
    • 2021-08-26
    • 2018-10-27
    相关资源
    最近更新 更多