窗口居中 & 变更触发机制

解决:

1。单实例窗口,窗口每次隐藏后再显示时,位置居中显示

2。多屏幕下单实例窗口,当父窗口移动到其它屏幕时,单实例窗口再次弹出时,位置才更新到父窗口屏幕。

3。子窗口每次唤醒时,都居中显示。

窗口首次显示的位置 - WindowStartupLocation

windows的启动时位置显示,WindowStartupLocation

  • CenterOwner --显示在父窗口的中间(设置Owner)
  • CenterScreen --显示在当前屏幕中间
  • Manual --默认位置

当第一次window.ShowDialog时,window显示如上设置。

变更触发机制

上面只涉及到了首次显示位置,之后,窗口的位置会继续保留

  • 如何设置窗口隐藏之后再次弹出时,显示在中间(CenterOwner/CenterScreen)?
  • 如何设置窗口一直停留在显示在中间?

我们先了解一下,有哪些触发机制

  1. Activated 窗口激活
    窗口变更为前台窗口时(即显示在最前面),会触发
  2. IsVisibleChanged 显示变更
    当我们设置窗口隐藏Hide()时,IsVisibile=false.窗口再次ShowDialog时,IsVisibile=true;

利用如上俩种机制,下面就可以搞事情了。

首先定义几个枚举:

  1. /// <summary>
  2. /// 窗口显示变更触发时机
  3. /// </summary>
  4. public enum WindowLocationInvokeOccasion
  5. {
  6. /// <summary>
  7. /// 只要Activated就显示在中间
  8. /// </summary>
  9. Activated = ,
  10.  
  11. /// <summary>
  12. /// 只在第一次Activated时,显示在中间一次,之后的变化就不修改
  13. /// </summary>
  14. FirstActivated,
  15.  
  16. /// <summary>
  17. /// 窗口每次显示时,窗口居中
  18. /// <para>可以解决单实例窗口弹出不居中问题</para>
  19. /// </summary>
  20. Visibile,
  21.  
  22. /// <summary>
  23. /// 窗口每次显示时,如父窗口与当前窗口不在同一屏幕时,窗口居中
  24. /// <para>可以解决单实例窗口弹出不居中问题</para>
  25. /// </summary>
  26. VisibileInDifferentScreen,
  27.  
  28. /// <summary>
  29. /// 不触发
  30. /// </summary>
  31. Defatult
  32. }

如上枚举包含了4种触发机制。

我们再定义个附加属性,通过附加属性去设置窗口的额外功能-居中显示触发机制

  1. /// <summary>
  2. /// 窗口显示居中触发时机
  3. /// <para>另:居中显示设置,请使用<see cref="Window"/>的<see cref="WindowStartupLocation"/>属性</para>
  4. /// </summary>
  5. public static readonly DependencyProperty InvokeOccasionProperty = DependencyProperty.RegisterAttached(
  6. "InvokeOccasion", typeof(WindowLocationInvokeOccasion), typeof(WindowLocationOptions),
  7. new PropertyMetadata(default(WindowLocationInvokeOccasion), InvokeOccasionProperty_ChangedCallback));

在属性更改触发事件中,根据不同的触发条件,设置不同的居中显示。

    • Activated --只要Activated就显示在中间
      每次触发,直接显示窗口即可
    • 首次Activated
      通过设置window.Activated -= ShowInCenter_Activated;禁用下次触发进入
    • Visibile
    • VisibileInDifferentScreen
      窗口显示时,如父窗口与当前窗口不在同一屏幕时,窗口居中.
      怎么判断当前子窗口与父窗口是否在同一屏幕?
  1. var screen = Screen.FromHandle(new WindowInteropHelper(parentWindow).Handle);
  2.  
  3. Graphics currentGraphics = Graphics.FromHwnd(new WindowInteropHelper(parentWindow).Handle);
  4. double dpiXRatio = currentGraphics.DpiX / ;
  5. double dpiYRatio = currentGraphics.DpiY / ;
  6.  
  7. //当子窗口与父窗口所在屏幕相同时,不作处理
  8. var isSubWindowInSameScreen = subWindow.Left > screen.Bounds.Left / dpiXRatio &&
  9. subWindow.Left < screen.Bounds.Left / dpiXRatio + screen.Bounds.Width / dpiXRatio &&
  10. subWindow.Top > screen.Bounds.Top / dpiYRatio &&
  11. subWindow.Top < screen.Bounds.Top / dpiYRatio + screen.Bounds.Height / dpiYRatio;
  12. return isSubWindowInSameScreen;

介绍完成触发条件,下面说下窗口居中显示。
居中显示,分为当前屏幕内居中/主窗口内居中,直接上代码

1.在主窗口中居中显示-CenterOwner

设置窗口的依靠位置Location(Left,Top)(左上角)

  • 子窗口最大化时 --WindowState=“Maximized”最大化窗口,固定的弹出到主屏幕,因此需额外处理,根据屏幕Location设置位置;
  • 父窗口最大化时 --父窗口最大化时,父窗口的location,因窗口设置margin,有可能不准确,故取屏幕位置
  • CenterOwner窗口居中显示 --直接取父窗口的位置/大小和子窗口的大小,进行计算即可

PS:窗口的位置Left/Top可能为负

  1. /// <summary>
  2. /// 在主窗口中居中显示
  3. /// </summary>
  4. /// <param name="subWindow"></param>
  5. /// <param name="parentWindow"></param>
  6. private static void SetWindowInCenterOwner(Window subWindow, Window parentWindow)
  7. {
  8. //最大化窗口,固定的弹出到主屏幕,因此需额外处理
  9. if (subWindow.WindowState == WindowState.Maximized)
  10. {
  11. //子窗口最大化时,需要根据屏幕设置位置;
  12. var screen = Screen.FromHandle(new WindowInteropHelper(parentWindow).Handle);
  13.  
  14. Graphics currentGraphics = Graphics.FromHwnd(new WindowInteropHelper(parentWindow).Handle);
  15. double dpiXRatio = currentGraphics.DpiX / ;
  16. double dpiYRatio = currentGraphics.DpiY / ;
  17.  
  18. subWindow.Left = screen.Bounds.Left / dpiXRatio;
  19. subWindow.Top = screen.Bounds.Top / dpiYRatio;
  20. }
  21. if (parentWindow.WindowState == WindowState.Maximized)
  22. {
  23. //父窗口最大化时,父窗口的location,因窗口设置margin,有可能不准确,故取屏幕位置
  24. var screen = Screen.FromHandle(new WindowInteropHelper(parentWindow).Handle);
  25.  
  26. Graphics currentGraphics = Graphics.FromHwnd(new WindowInteropHelper(parentWindow).Handle);
  27. double dpiXRatio = currentGraphics.DpiX / ;
  28. double dpiYRatio = currentGraphics.DpiY / ;
  29.  
  30. //窗口居中显示
  31. subWindow.Left = screen.Bounds.Left / dpiXRatio +
  32. (screen.Bounds.Width / dpiXRatio - subWindow.ActualWidth) / ;
  33. subWindow.Top = screen.Bounds.Top / dpiYRatio +
  34. (screen.Bounds.Height / dpiYRatio - subWindow.ActualHeight) / ;
  35. }
  36. else
  37. {
  38. //窗口居中显示
  39. subWindow.Left = parentWindow.Left + (parentWindow.ActualWidth - subWindow.ActualWidth) / ;
  40. subWindow.Top = parentWindow.Top + (parentWindow.ActualHeight - subWindow.ActualHeight) / ;
  41. }
  42. }

2.当前屏幕内居中-CenterScreen

窗口位置设置和上面的一样,值得注意的是DPI。

通过win的显示设置,调整文本显示比例,屏幕的位置转换(X,Y),得考虑DPI的换算

  1. /// <summary>
  2. /// 在父窗口所在屏幕居中显示
  3. /// </summary>
  4. /// <param name="subWindow"></param>
  5. /// <param name="parentWindow"></param>
  6. private static void SetWindowInCenterScreen(Window subWindow, Window parentWindow)
  7. {
  8. SetWindowLocationInScreen(subWindow, parentWindow, subWindow.WindowState);
  9. }
  10.  
  11. private const int DpiPercent = ;
  12.  
  13. private static void SetWindowLocationInScreen(Window subWindow, Window parentWindow, WindowState windowState)
  14. {
  15. var intPtr = new WindowInteropHelper(parentWindow).Handle;
  16. var screen = Screen.FromHandle(intPtr);
  17.  
  18. using (Graphics currentGraphics = Graphics.FromHwnd(intPtr))
  19. {
  20. double dpiXRatio = currentGraphics.DpiX / DpiPercent;
  21. double dpiYRatio = currentGraphics.DpiY / DpiPercent;
  22.  
  23. if (windowState == WindowState.Maximized)
  24. {
  25. //设置全屏Location
  26. subWindow.Left = screen.Bounds.Left / dpiXRatio;
  27. subWindow.Top = screen.Bounds.Top / dpiYRatio;
  28. }
  29. else
  30. {
  31. //设置居中Location
  32. subWindow.Left = screen.Bounds.Left / dpiXRatio +
  33. (screen.Bounds.Width / dpiXRatio - subWindow.ActualWidth) / ;
  34. subWindow.Top = screen.Bounds.Top / dpiYRatio +
  35. (screen.Bounds.Height / dpiYRatio - subWindow.ActualHeight) / ;
  36. }
  37. }
  38. }

关键字:单实例窗口,窗口居中,CenterOwner,CenterScreen,当前屏幕DPI

WPF 窗口居中 & 变更触发机制的更多相关文章

  1. 【深入浅出Linux网络编程】 “基础 -- 事件触发机制”

    回顾一下“"开篇 -- 知其然,知其所以然"”中的两段代码,第一段虽然只使用1个线程但却也只能处理一个socket,第二段虽然能处理成百上千个socket但却需要创建同等数量的线程 ...

  2. window.open窗口居中和窗口最大化

    1.window.open()参数 window.open(pageURL,name,parameters) 其中: pageURL为子窗口路径 name为子窗口句柄 parameters为窗口参数( ...

  3. [WPF疑难]如何禁用WPF窗口的系统菜单(SystemMenu)

    原文 [WPF疑难]如何禁用WPF窗口的系统菜单(SystemMenu) [WPF疑难]如何禁用WPF窗口的系统菜单(SystemMenu) 周银辉 点击窗口左上角图标时弹出来的菜单也就是这里所说的系 ...

  4. Example005控制弹出窗口居中显示

    <!-- 实例005控制弹出窗口居中显示 --> <head> <meta charset="UTF-8"> </head> < ...

  5. WPF C# 命令的运行机制

    1.概述 1.1 WPF C# 命令的本质 命令是 WPF 中的输入机制,它提供的输入处理比设备输入具有更高的语义级别. 例如,在许多应用程序中都能找到的“复制”.“剪切”和“粘贴”操作就是命令. W ...

  6. EventEmitter:nodeJs事件触发机制

    Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列 Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs.r ...

  7. Wpf窗口中打开WinForm窗口

    获取wpf窗口对应的句柄窗口 using System; using System.Windows; using System.Windows.Interop; using IWin32Window ...

  8. win32程序之窗口程序,以及消息机制

    win32程序值窗口程序,以及消息机制 一丶简介 通过上一讲.我们了解了窗口其实是绘制出来的.而且是不断绘制的过程. 所以窗口的本质是绘制. 但是我们现在看到的窗口程序.都可以点击关闭按钮. 使用鼠标 ...

  9. tkinter窗口居中方法

    tkinter窗口居中 from tkinter import * class MyFrm(Frame): def __init__(self, master): self.root=master s ...

随机推荐

  1. python入门(10)使用List和tuple

    python入门(10)使用List和tuple list Python内置的一种数据类型是列表:list.list是一种有序的集合,可以随时添加和删除其中的元素. 比如,列出班里所有同学的名字,就可 ...

  2. 复习HTML+CSS(3)

    n  超级链接 l  语法格式:<a 属性 = "值">---</a> l  常用属性: n  Href:目标文件的地址URL,该URL可以是相对地址,也可 ...

  3. Java课后练习

    1.利用循环输出:************************* public class Shape { public static void main(String[] args) { for ...

  4. SpringBoot2.x开发案例之整合Quartz任务管理系统

    基于spring-boot 2.x + quartz 的CRUD任务管理系统,适用于中小项目. 基于spring-boot +quartz 的CRUD任务管理系统: https://gitee.com ...

  5. ZOJ-1586 QS Network---最小生成树Prim

    题目链接: https://vjudge.net/problem/ZOJ-1586 题目大意: 首先给一个t,代表t个测试样例,再给一个n,表示有n个QS装置,接下来一行是n个QS装置的成本.接下来是 ...

  6. 搭建自己的maven私服 必过

       教你一步一步搭建自己的maven私服 一. 应用场景 有些公司都不提供外网给项目组人员,因此就不能使用maven访问远程的仓库地址,所以很有必要在局域网里找一台有外网权限的机器,搭建nexus私 ...

  7. Spring(2)——Spring IoC 详解

    Spring IoC 概述 IoC:Inverse of Control(控制反转) 读作"反转控制",更好理解,不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控 ...

  8. 【图解数据结构】 栈&队列

    [TOC] 勤于总结,持续输出! 1.栈 1.1栈的定义 栈(stack)是限定在表尾进行插入和删除的操作的线性表. 我们把允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不包 ...

  9. iframe 里的高度适应的问题

    iframe 这个东西功能是很强大,但是有一个巨大的问题就是高度自适应的问题: 不过这个问题,百度或者谷歌上有很多解决办法,但是,很多时候都有兼容性问题: 所有我就每个方法都试了一遍,终于找到了一个 ...

  10. 0418 jQuery笔记(添加事件、each、prop、$(this))

    1.添加点击事件.each.prop.$(this) //全选框的被动操作 //定义一个标志保存最终状态 var flag = false; //为每一个选择框添加点击事件,数组.click() $( ...