原文:设置WPF窗口相对于非WPF窗口的位置

在前一个Post当中,指出了在WPF的WindowInteropHelper类中的一个BUG:通过WindowInteropHelper的Owner属性不能实现把WPF窗口的Owner属性设置为一个非WPF窗口的句柄。

在我的Post帖出后不到一天,在WPF SDK的Blog上,就针对这个BUG给出了一个非常完美的解决方案。既然不同通过设置WindowStartupLocation.CenterOwner来改变窗口的位置。那么我们就用WindowStartupLocation.Manual来手动计算设置窗口的位置。大致的代码如下:

using System.Windows;
using System.Windows.Interop; // WindowInteropHelper

...

// Instantiate the owned WPF window
Window cw = new Window();

// Set the owned WPF window’s owner with the non-WPF owner window
IntPtr ownerWindowHandle = ...;
 
// Set the owned WPF window’s owner with the non-WPF owner window

WindowInteropHelper helper = new WindowInteropHelper(cw);
helper.Owner = ownerWindowHandle;

// Manually calculate Top/Left to appear centered
int nonWPFOwnerLeft = ...;  // Get non-WPF owner’s Left
int nonWPFOwnerWidth = ...;  // Get non-WPF owner’s Width
int nonWPFOwnerTop = ...;  // Get non-WPF owner’s Top
int nonWPFOwnerHeight = ...;  // Get non-WPF owner’s Height

cw.WindowStartupLocation = WindowStartupLocation.Manual;
cw.Left = nonWPFOwnerLeft + (nonWPFOwnerWidth - cw.Width) / 2;
cw.Top = nonWPFOwnerTop + (nonWPFOwnerHeight - cw.Height) / 2;

// Show the owned WPF window
cw.Show();

这段代码理论上没有什么问题呢?但是WPF是支持设备独立的。因此,在非WPF Owner窗口的某些情况下可能会因为DPI的而不能正常工作。解决这个问题,可以利用HwndSource类进行窗口位置的设备独立计算:

using System.Windows; // Window, WindowStartupLocation, Point
using System.Windows.Interop; // WindowInteropHelper, HwndSource
using System.Windows.Media; // Matrix

...

// Instantiate the owned WPF window
CenteredWindow cw = new CenteredWindow();

// Get the handle to the non-WPF owner window
IntPtr ownerWindowHandle = ...; // Get hWnd for non-WPF window
 
// Set the owned WPF window’s owner with the non-WPF owner window
WindowInteropHelper helper = new WindowInteropHelper(cw);
helper.Owner = ownerWindowHandle;

// Center window
// Note - Need to use HwndSource to get handle to WPF owned window,
//        and the handle only exists when SourceInitialized has been
//        raised

cw.SourceInitialized += delegate
{
    // Get WPF size and location for non-WPF owner window
    int nonWPFOwnerLeft = ...; // Get non-WPF owner’s Left
    int nonWPFOwnerWidth = ...; // Get non-WPF owner’s Width
    int nonWPFOwnerTop = ...; // Get non-WPF owner’s Top
    int nonWPFOwnerHeight = ...; // Get non-WPF owner’s Height

// Get transform matrix to transform non-WPF owner window
    // size and location units into device-independent WPF
    // size and location units

HwndSource source = HwndSource.FromHwnd(helper.Handle);
    if (source == null) return;
    Matrix matrix = source.CompositionTarget.TransformFromDevice;
    Point ownerWPFSize = matrix.Transform(
      new Point(nonWPFOwnerWidth, nonWPFOwnerHeight));
    Point ownerWPFPosition = matrix.Transform(
      new Point(nonWPFOwnerLeft, nonWPFOwnerTop));

// Center WPF window
    cw.WindowStartupLocation = WindowStartupLocation.Manual;
    cw.Left = ownerWPFPosition.X + (ownerWPFSize.X - cw.Width) / 2;
    cw.Top = ownerWPFPosition.Y + (ownerWPFSize.Y - cw.Height) / 2;

};

// Show WPF owned window
cw.Show();

在上面的代码中需要注意的是HwndSource的使用。这个类需要一个窗口句柄,因此它的代码被放在一个SourceInitialized的事件委派函数中执行。

最后,除了上面这种方法,其实我们还可以用Win32 API函数来实现,在ATL的CWindow类中,就有这样的一个函数,我直接把放在下面,有兴趣的朋友参考其中的实现原理:

BOOL CenterWindow(HWND hWndCenter = NULL) throw()
...{
    ATLASSERT(::IsWindow(m_hWnd));

    // determine owner window to center against
    DWORD dwStyle = GetStyle();
    if(hWndCenter == NULL)
    ...{
        if(dwStyle & WS_CHILD)
            hWndCenter = ::GetParent(m_hWnd);
        else
            hWndCenter = ::GetWindow(m_hWnd, GW_OWNER);
    }

    // get coordinates of the window relative to its parent
    RECT rcDlg;
    ::GetWindowRect(m_hWnd, &rcDlg);
    RECT rcArea;
    RECT rcCenter;
    HWND hWndParent;
    if(!(dwStyle & WS_CHILD))
    ...{
        // don't center against invisible or minimized windows
        if(hWndCenter != NULL)
        ...{
            DWORD dwStyleCenter = ::GetWindowLong(hWndCenter, GWL_STYLE);
            if(!(dwStyleCenter & WS_VISIBLE) || (dwStyleCenter & WS_MINIMIZE))
                hWndCenter = NULL;
        }

        // center within screen coordinates
        ::SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcArea, NULL);
        if(hWndCenter == NULL)
            rcCenter = rcArea;
        else
            ::GetWindowRect(hWndCenter, &rcCenter);
    }
    else
    ...{
        // center within parent client coordinates
        hWndParent = ::GetParent(m_hWnd);
        ATLASSERT(::IsWindow(hWndParent));

        ::GetClientRect(hWndParent, &rcArea);
        ATLASSERT(::IsWindow(hWndCenter));
        ::GetClientRect(hWndCenter, &rcCenter);
        ::MapWindowPoints(hWndCenter, hWndParent, (POINT*)&rcCenter, 2);
    }

    int DlgWidth = rcDlg.right - rcDlg.left;
    int DlgHeight = rcDlg.bottom - rcDlg.top;

    // find dialog's upper left based on rcCenter
    int xLeft = (rcCenter.left + rcCenter.right) / 2 - DlgWidth / 2;
    int yTop = (rcCenter.top + rcCenter.bottom) / 2 - DlgHeight / 2;

    // if the dialog is outside the screen, move it inside
    if(xLeft < rcArea.left)
        xLeft = rcArea.left;
    else if(xLeft + DlgWidth > rcArea.right)
        xLeft = rcArea.right - DlgWidth;

    if(yTop < rcArea.top)
        yTop = rcArea.top;
    else if(yTop + DlgHeight > rcArea.bottom)
        yTop = rcArea.bottom - DlgHeight;

    // map screen coordinates to child coordinates
    return ::SetWindowPos(m_hWnd, NULL, xLeft, yTop, -1, -1,
        SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}

设置WPF窗口相对于非WPF窗口的位置的更多相关文章

  1. 解析Delphi 窗口置顶,及非主窗口置顶

    方法一: procedure TForm1.Button2Click(Sender: TObject);begin Form2.Show; Application.NormalizeTopMosts; ...

  2. Android窗口管理服务WindowManagerService对窗口的组织方式分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8498908 我们知道,在Android系统中, ...

  3. [WPF疑难] 模式窗口被隐藏后重新显示时变成了非模式窗口

    原文:[WPF疑难] 模式窗口被隐藏后重新显示时变成了非模式窗口 [WPF疑难] 模式窗口被隐藏后重新显示时变成了非模式窗口 周银辉 现象: 大家可以试试下面这个很有趣但会带来Defect的现象:当我 ...

  4. C# WPF 中WebBrowser拖动来移动窗口,改变窗口位置

    前言 wpf中的WebBrowser相比之前的winform阉割了不少东西,也增加了不少东西,但是msdn对wpf也没有较好的文档 WebBrowser可以说是一个.NET控件,相对于WPF中的控件, ...

  5. WPF 使用 WindowChrome,在自定义窗口标题栏的同时最大程度保留原生窗口样式(类似 UWP/Chrome)

    WPF 自定义窗口样式有多种方式,不过基本核心实现都是在修改 Win32 窗口样式.然而,Windows 上的应用就应该有 Windows 应用的样子嘛,在保证自定义的同时也能与其他窗口样式保持一致当 ...

  6. WPF中用于嵌入其他进程窗口的自定义控件(AppContainer)

    原文:WPF中用于嵌入其他进程窗口的自定义控件(AppContainer) 版权声明:本文为博主原创文章,转载请注明作者和出处 https://blog.csdn.net/ZZZWWWPPP11199 ...

  7. WPF 创建无边框的圆角窗口

    原文:WPF 创建无边框的圆角窗口 如题所述,在WPF中要创建一个没有边框且为圆角的窗体,有如下几步工作要进行: 第一步:去掉窗体默认样式的边框 首先将窗体的背景设为透明,将允许透明的属性设置为Tru ...

  8. WPF疑难杂症之二(全屏幕窗口)

    原文:WPF疑难杂症之二(全屏幕窗口) 近日的学习中遇到一个非常奇怪的问题:用XAML文件创建了一个全屏幕窗口,然后,在窗口中建立了一个非常简单的动画.一切都在我的掌控之中,实现非常的顺利. WPF中 ...

  9. WPF 解决弹出模态窗口关闭后,主窗口不在最前

    本文告诉大家如何解决这个问题,在 WPF 的软件,弹出一个模态窗口.使用另一个窗口在模态窗口前面.从任务栏打开模态窗口.关闭模态窗口.这时发现,主窗口会在刚才使用的另一个窗口下面 这是 Windows ...

随机推荐

  1. 使用代码创建rabbitmq交换机和队列绑定

    1.获取channel对象 2.声明(创建)对列 // 第一个参数,queueName:对列名称.数据类型:String// 第二个参数,durable:是否持久化, 队列的声明默认是存放到内存中的, ...

  2. bzoj 1093 [ZJOI2007]最大半连通子图——缩点+拓扑

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1093 缩点+拓扑,更新长度的时候维护方案数. 结果没想到处理缩点后的重边,这样的话方案数会算 ...

  3. 使用Jedis操作Redis-使用Java语言在客户端操作---String类型

    前提:需要引入Jedis的jar包. /** * 我的redis在Linux虚拟机Centos7中,192.168.222.129是我虚拟机的ip地址. */ private static Jedis ...

  4. Angungular.js 的过滤器&工具方法

    字母大小写 数字 货币 截取字符串 截取数组 用JS操作 ----------------------------------------------------------------------- ...

  5. python实例 函数

    #! /usr/bin/python # -*- coding: utf8 -*- def sum(a,b):     return a+b func = sum r = func(5,6) prin ...

  6. Codevs2490 导弹防御塔

    2490 导弹防御塔 2490 导弹防御塔 时间限制: 1 s 空间限制: 64000 KB 题目等级 : 大师 Master         题目描述 Description Freda的城堡—— ...

  7. Centos系统Python环境搭建和项目部署

    目录 一.Python 1. 源安装 Python3 2. SCL安装 Python3 3. 虚拟环境venv 4. 安装Flask 5. 安装gunicorn 二.安装Nginx 1. 安装Ngin ...

  8. 【python之路16】lambda表达式

    1.lambda表达式,实际是建立一个简易的函数 下面代码中f1和f2表示是相同过程的代码 def f1(args): return args f2 = lambda args:args print( ...

  9. 洛谷 1083 (NOIp2012) 借教室——标记永久化线段树 / 差分+二分

    题目:https://www.luogu.org/problemnew/show/P1083 听说线段树不标记永久化会T一个点. 注意mn记录的是本层以下.带上标记的min! #include< ...

  10. Html5之localStorage和sessionStorage缓存

    <!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8" ...