WPF 窗口居中 & 变更触发机制
窗口居中 & 变更触发机制
解决:
1。单实例窗口,窗口每次隐藏后再显示时,位置居中显示
2。多屏幕下单实例窗口,当父窗口移动到其它屏幕时,单实例窗口再次弹出时,位置才更新到父窗口屏幕。
3。子窗口每次唤醒时,都居中显示。
窗口首次显示的位置 - WindowStartupLocation
windows的启动时位置显示,WindowStartupLocation
- CenterOwner --显示在父窗口的中间(设置Owner)
- CenterScreen --显示在当前屏幕中间
- Manual --默认位置
当第一次window.ShowDialog时,window显示如上设置。
变更触发机制
上面只涉及到了首次显示位置,之后,窗口的位置会继续保留
- 如何设置窗口隐藏之后再次弹出时,显示在中间(CenterOwner/CenterScreen)?
- 如何设置窗口一直停留在显示在中间?
我们先了解一下,有哪些触发机制
- Activated 窗口激活
窗口变更为前台窗口时(即显示在最前面),会触发 - IsVisibleChanged 显示变更
当我们设置窗口隐藏Hide()时,IsVisibile=false.窗口再次ShowDialog时,IsVisibile=true;
利用如上俩种机制,下面就可以搞事情了。
首先定义几个枚举:
/// <summary>
/// 窗口显示变更触发时机
/// </summary>
public enum WindowLocationInvokeOccasion
{
/// <summary>
/// 只要Activated就显示在中间
/// </summary>
Activated = , /// <summary>
/// 只在第一次Activated时,显示在中间一次,之后的变化就不修改
/// </summary>
FirstActivated, /// <summary>
/// 窗口每次显示时,窗口居中
/// <para>可以解决单实例窗口弹出不居中问题</para>
/// </summary>
Visibile, /// <summary>
/// 窗口每次显示时,如父窗口与当前窗口不在同一屏幕时,窗口居中
/// <para>可以解决单实例窗口弹出不居中问题</para>
/// </summary>
VisibileInDifferentScreen, /// <summary>
/// 不触发
/// </summary>
Defatult
}
如上枚举包含了4种触发机制。
我们再定义个附加属性,通过附加属性去设置窗口的额外功能-居中显示触发机制
/// <summary>
/// 窗口显示居中触发时机
/// <para>另:居中显示设置,请使用<see cref="Window"/>的<see cref="WindowStartupLocation"/>属性</para>
/// </summary>
public static readonly DependencyProperty InvokeOccasionProperty = DependencyProperty.RegisterAttached(
"InvokeOccasion", typeof(WindowLocationInvokeOccasion), typeof(WindowLocationOptions),
new PropertyMetadata(default(WindowLocationInvokeOccasion), InvokeOccasionProperty_ChangedCallback));
在属性更改触发事件中,根据不同的触发条件,设置不同的居中显示。
- Activated --只要Activated就显示在中间
每次触发,直接显示窗口即可 - 首次Activated
通过设置window.Activated -= ShowInCenter_Activated;禁用下次触发进入 - Visibile
- VisibileInDifferentScreen
窗口显示时,如父窗口与当前窗口不在同一屏幕时,窗口居中.
怎么判断当前子窗口与父窗口是否在同一屏幕?
var screen = Screen.FromHandle(new WindowInteropHelper(parentWindow).Handle); Graphics currentGraphics = Graphics.FromHwnd(new WindowInteropHelper(parentWindow).Handle);
double dpiXRatio = currentGraphics.DpiX / ;
double dpiYRatio = currentGraphics.DpiY / ; //当子窗口与父窗口所在屏幕相同时,不作处理
var isSubWindowInSameScreen = subWindow.Left > screen.Bounds.Left / dpiXRatio &&
subWindow.Left < screen.Bounds.Left / dpiXRatio + screen.Bounds.Width / dpiXRatio &&
subWindow.Top > screen.Bounds.Top / dpiYRatio &&
subWindow.Top < screen.Bounds.Top / dpiYRatio + screen.Bounds.Height / dpiYRatio;
return isSubWindowInSameScreen;
介绍完成触发条件,下面说下窗口居中显示。
居中显示,分为当前屏幕内居中/主窗口内居中,直接上代码
1.在主窗口中居中显示-CenterOwner
设置窗口的依靠位置Location(Left,Top)(左上角)
- 子窗口最大化时 --WindowState=“Maximized”最大化窗口,固定的弹出到主屏幕,因此需额外处理,根据屏幕Location设置位置;
- 父窗口最大化时 --父窗口最大化时,父窗口的location,因窗口设置margin,有可能不准确,故取屏幕位置
- CenterOwner窗口居中显示 --直接取父窗口的位置/大小和子窗口的大小,进行计算即可
PS:窗口的位置Left/Top可能为负
/// <summary>
/// 在主窗口中居中显示
/// </summary>
/// <param name="subWindow"></param>
/// <param name="parentWindow"></param>
private static void SetWindowInCenterOwner(Window subWindow, Window parentWindow)
{
//最大化窗口,固定的弹出到主屏幕,因此需额外处理
if (subWindow.WindowState == WindowState.Maximized)
{
//子窗口最大化时,需要根据屏幕设置位置;
var screen = Screen.FromHandle(new WindowInteropHelper(parentWindow).Handle); Graphics currentGraphics = Graphics.FromHwnd(new WindowInteropHelper(parentWindow).Handle);
double dpiXRatio = currentGraphics.DpiX / ;
double dpiYRatio = currentGraphics.DpiY / ; subWindow.Left = screen.Bounds.Left / dpiXRatio;
subWindow.Top = screen.Bounds.Top / dpiYRatio;
}
if (parentWindow.WindowState == WindowState.Maximized)
{
//父窗口最大化时,父窗口的location,因窗口设置margin,有可能不准确,故取屏幕位置
var screen = Screen.FromHandle(new WindowInteropHelper(parentWindow).Handle); Graphics currentGraphics = Graphics.FromHwnd(new WindowInteropHelper(parentWindow).Handle);
double dpiXRatio = currentGraphics.DpiX / ;
double dpiYRatio = currentGraphics.DpiY / ; //窗口居中显示
subWindow.Left = screen.Bounds.Left / dpiXRatio +
(screen.Bounds.Width / dpiXRatio - subWindow.ActualWidth) / ;
subWindow.Top = screen.Bounds.Top / dpiYRatio +
(screen.Bounds.Height / dpiYRatio - subWindow.ActualHeight) / ;
}
else
{
//窗口居中显示
subWindow.Left = parentWindow.Left + (parentWindow.ActualWidth - subWindow.ActualWidth) / ;
subWindow.Top = parentWindow.Top + (parentWindow.ActualHeight - subWindow.ActualHeight) / ;
}
}
2.当前屏幕内居中-CenterScreen
窗口位置设置和上面的一样,值得注意的是DPI。
通过win的显示设置,调整文本显示比例,屏幕的位置转换(X,Y),得考虑DPI的换算
/// <summary>
/// 在父窗口所在屏幕居中显示
/// </summary>
/// <param name="subWindow"></param>
/// <param name="parentWindow"></param>
private static void SetWindowInCenterScreen(Window subWindow, Window parentWindow)
{
SetWindowLocationInScreen(subWindow, parentWindow, subWindow.WindowState);
} private const int DpiPercent = ; private static void SetWindowLocationInScreen(Window subWindow, Window parentWindow, WindowState windowState)
{
var intPtr = new WindowInteropHelper(parentWindow).Handle;
var screen = Screen.FromHandle(intPtr); using (Graphics currentGraphics = Graphics.FromHwnd(intPtr))
{
double dpiXRatio = currentGraphics.DpiX / DpiPercent;
double dpiYRatio = currentGraphics.DpiY / DpiPercent; if (windowState == WindowState.Maximized)
{
//设置全屏Location
subWindow.Left = screen.Bounds.Left / dpiXRatio;
subWindow.Top = screen.Bounds.Top / dpiYRatio;
}
else
{
//设置居中Location
subWindow.Left = screen.Bounds.Left / dpiXRatio +
(screen.Bounds.Width / dpiXRatio - subWindow.ActualWidth) / ;
subWindow.Top = screen.Bounds.Top / dpiYRatio +
(screen.Bounds.Height / dpiYRatio - subWindow.ActualHeight) / ;
}
}
}
关键字:单实例窗口,窗口居中,CenterOwner,CenterScreen,当前屏幕DPI
WPF 窗口居中 & 变更触发机制的更多相关文章
- 【深入浅出Linux网络编程】 “基础 -- 事件触发机制”
回顾一下“"开篇 -- 知其然,知其所以然"”中的两段代码,第一段虽然只使用1个线程但却也只能处理一个socket,第二段虽然能处理成百上千个socket但却需要创建同等数量的线程 ...
- window.open窗口居中和窗口最大化
1.window.open()参数 window.open(pageURL,name,parameters) 其中: pageURL为子窗口路径 name为子窗口句柄 parameters为窗口参数( ...
- [WPF疑难]如何禁用WPF窗口的系统菜单(SystemMenu)
原文 [WPF疑难]如何禁用WPF窗口的系统菜单(SystemMenu) [WPF疑难]如何禁用WPF窗口的系统菜单(SystemMenu) 周银辉 点击窗口左上角图标时弹出来的菜单也就是这里所说的系 ...
- Example005控制弹出窗口居中显示
<!-- 实例005控制弹出窗口居中显示 --> <head> <meta charset="UTF-8"> </head> < ...
- WPF C# 命令的运行机制
1.概述 1.1 WPF C# 命令的本质 命令是 WPF 中的输入机制,它提供的输入处理比设备输入具有更高的语义级别. 例如,在许多应用程序中都能找到的“复制”.“剪切”和“粘贴”操作就是命令. W ...
- EventEmitter:nodeJs事件触发机制
Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列 Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs.r ...
- Wpf窗口中打开WinForm窗口
获取wpf窗口对应的句柄窗口 using System; using System.Windows; using System.Windows.Interop; using IWin32Window ...
- win32程序之窗口程序,以及消息机制
win32程序值窗口程序,以及消息机制 一丶简介 通过上一讲.我们了解了窗口其实是绘制出来的.而且是不断绘制的过程. 所以窗口的本质是绘制. 但是我们现在看到的窗口程序.都可以点击关闭按钮. 使用鼠标 ...
- tkinter窗口居中方法
tkinter窗口居中 from tkinter import * class MyFrm(Frame): def __init__(self, master): self.root=master s ...
随机推荐
- RxJava系列6(从微观角度解读RxJava源码)
RxJava系列1(简介) RxJava系列2(基本概念及使用介绍) RxJava系列3(转换操作符) RxJava系列4(过滤操作符) RxJava系列5(组合操作符) RxJava系列6(从微观角 ...
- javascript学习总结一
1. 变量提升hoisting 变量提升的意思是在一个变量作用域里定义的变量的声明会被提升到作用域的顶部,这是变量只会被声明,不会被初始化复制,而是undefined. 代码如下: function ...
- QT 实现在QLabel上画图
QT之所以不能再任意控件上绘图是因为QT的事件过滤器把控件的绘图事件给过滤了. 在paintevent()函数中,通常需要设置QPainter对象,创建QPainter对象的同时需要指定绘图设备,即继 ...
- 【vuejs深入一】深入学习vue指令,自定义指令解决开发痛点
写在前面 一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残. 最近博主我沉淀了几个月,或者说懒了几个月.然而大佬的指点总是一针见血,能够让人看到方向.所以我现在有觉得,一个好的 ...
- scrapy爬取豆瓣电影top250
# -*- coding: utf-8 -*- # scrapy爬取豆瓣电影top250 import scrapy from douban.items import DoubanItem class ...
- MongoDB 分片集群搭建
一.概述 分片是一种在多台机器上分配数据的方法.MongoDB使用分片来支持具有非常大的数据集和高吞吐量操作.有两种解决系统增长的方法:垂直扩展和水平扩展. 垂直扩展涉及增加单个服务器的容量,例如使用 ...
- ES6关于Promise的用法
Promise 对象用于一个异步操作的最终完成(或失败)及其结果值的表示.简单点说,它就是用于处理异步操作的,异步处理成功了就执行成功的操作,异步处理失败了就捕获错误或者停止后续操作. 它的一般表示形 ...
- [C#]Google Chrome 书签导出并生成 MHTML 文件
目的 因为某些原因需要将存放在 Google Chrome 内的书签导出到本地,所幸 Google Chrome 提供了导出书签的功能. 分析 首先在 Google Chrome 浏览器当中输入 ch ...
- .NET Core2.0+MVC 用session,cookie实现的sso单点登录
博主刚接触.NET Core2.0,想做一个单点登录的demo,所以参考了一些资料,这里给上链接: 1.http://www.cnblogs.com/baibaomen/p/sso-sequence- ...
- [LeetCode] Find the Derangement of An Array 找数组的错排
In combinatorial mathematics, a derangement is a permutation of the elements of a set, such that no ...