前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >WPF 窗口居中 & 变更触发机制

WPF 窗口居中 & 变更触发机制

作者头像
独立观察员
发布2022-12-06 19:19:52
1.2K0
发布2022-12-06 19:19:52
举报
文章被收录于专栏:独立观察员博客

本文经原作者授权以原创方式二次分享,欢迎转载、分享。 原文作者:唐宋元明清 原文地址:https://www.cnblogs.com/kybs0/p/7420767.html

窗口居中 & 变更触发机制

解决:

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

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

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

窗口首次显示的位置 - WindowStartupLocation

Windows的启动时位置显示,WindowStartupLocation

  • CenterOwner --显示在父窗口的中间(设置Owner)
  • CenterScreen --显示在当前屏幕中间
  • Manual --默认位置 当第一次Window.ShowDialog时,window显示如上设置。

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

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

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

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

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

首先定义几个枚举:

代码语言:javascript
复制
/// <summary>
    /// 窗口显示变更触发时机
    /// </summary>
    public enum WindowLocationInvokeOccasion
    {
        /// <summary>
        /// 只要Activated就显示在中间
        /// </summary>
        Activated = 0,

        /// <summary>
        /// 只在第一次Activated时,显示在中间一次,之后的变化就不修改
        /// </summary>
        FirstActivated,

        /// <summary>
        /// 窗口每次显示时,窗口居中
        /// <para>可以解决单实例窗口弹出不居中问题</para>
        /// </summary>
        Visibile,

        /// <summary>
        /// 窗口每次显示时,如父窗口与当前窗口不在同一屏幕时,窗口居中
        /// <para>可以解决单实例窗口弹出不居中问题</para>
        /// </summary>
        VisibileInDifferentScreen,

        /// <summary>
        /// 不触发
        /// </summary>
        Defatult
    }

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

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

代码语言:javascript
复制
/// <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窗口显示时,如父窗口与当前窗口不在同一屏幕时,窗口居中. 怎么判断当前子窗口与父窗口是否在同一屏幕?
代码语言:javascript
复制
var screen = Screen.FromHandle(new WindowInteropHelper(parentWindow).Handle);

Graphics currentGraphics = Graphics.FromHwnd(new WindowInteropHelper(parentWindow).Handle);
double dpiXRatio = currentGraphics.DpiX / 96;
double dpiYRatio = currentGraphics.DpiY / 96;

//当子窗口与父窗口所在屏幕相同时,不作处理
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可能为负
代码语言:javascript
复制
/// <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 / 96;
        double dpiYRatio = currentGraphics.DpiY / 96;

        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 / 96;
        double dpiYRatio = currentGraphics.DpiY / 96;

        //窗口居中显示
        subWindow.Left = screen.Bounds.Left / dpiXRatio +
                            (screen.Bounds.Width / dpiXRatio - subWindow.ActualWidth) / 2;
        subWindow.Top = screen.Bounds.Top / dpiYRatio +
                        (screen.Bounds.Height / dpiYRatio - subWindow.ActualHeight) / 2;
    }
    else
    {
        //窗口居中显示
        subWindow.Left = parentWindow.Left + (parentWindow.ActualWidth - subWindow.ActualWidth) / 2;
        subWindow.Top = parentWindow.Top + (parentWindow.ActualHeight - subWindow.ActualHeight) / 2;
    }
}

2)当前屏幕内居中-CenterScreen;

  • 窗口位置设置和上面的一样,值得注意的是DPI
  • 通过win的显示设置,调整文本显示比例,屏幕的位置转换(X,Y)``,得考虑DPI`的换算;
代码语言:javascript
复制
/// <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 = 96;

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) / 2;
            subWindow.Top = screen.Bounds.Top / dpiYRatio +
                            (screen.Bounds.Height / dpiYRatio - subWindow.ActualHeight) / 2;
        }
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 独立观察员博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 窗口居中 & 变更触发机制
    • 窗口首次显示的位置 - WindowStartupLocation
      • 我们先了解一下,有哪些触发机制
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档