在WPF中,如果窗口不在屏幕上,如何将其移动到屏幕上?

2024-04-01

如果我有一个窗口,如何确保该窗口永远不会隐藏在屏幕之外?

这很重要,因为有时如果用户添加或删除监视器,如果我们记住了之前的位置,窗口可能会永久隐藏在屏幕之外。

我在用WPF + MVVM.


这个答案已经在大规模的现实世界应用中得到了测试。

从任何附加属性调用此函数可将窗口移回到可见屏幕上:

public static class ShiftWindowOntoScreenHelper
{
    /// <summary>
    ///     Intent:  
    ///     - Shift the window onto the visible screen.
    ///     - Shift the window away from overlapping the task bar.
    /// </summary>
    public static void ShiftWindowOntoScreen(Window window)
    {
        // Note that "window.BringIntoView()" does not work.                            
        if (window.Top < SystemParameters.VirtualScreenTop)
        {
            window.Top = SystemParameters.VirtualScreenTop;
        }

        if (window.Left < SystemParameters.VirtualScreenLeft)
        {
            window.Left = SystemParameters.VirtualScreenLeft;
        }

        if (window.Left + window.Width > SystemParameters.VirtualScreenLeft + SystemParameters.VirtualScreenWidth)
        {
            window.Left = SystemParameters.VirtualScreenWidth + SystemParameters.VirtualScreenLeft - window.Width;
        }

        if (window.Top + window.Height > SystemParameters.VirtualScreenTop + SystemParameters.VirtualScreenHeight)
        {
            window.Top = SystemParameters.VirtualScreenHeight + SystemParameters.VirtualScreenTop - window.Height;
        }

        // Shift window away from taskbar.
        {
            var taskBarLocation = GetTaskBarLocationPerScreen();

            // If taskbar is set to "auto-hide", then this list will be empty, and we will do nothing.
            foreach (var taskBar in taskBarLocation)
            {
                Rectangle windowRect = new Rectangle((int)window.Left, (int)window.Top, (int)window.Width, (int)window.Height);

                // Keep on shifting the window out of the way.
                int avoidInfiniteLoopCounter = 25;
                while (windowRect.IntersectsWith(taskBar))
                {
                    avoidInfiniteLoopCounter--;
                    if (avoidInfiniteLoopCounter == 0)
                    {
                        break;
                    }

                    // Our window is covering the task bar. Shift it away.
                    var intersection = Rectangle.Intersect(taskBar, windowRect);

                    if (intersection.Width < window.Width
                        // This next one is a rare corner case. Handles situation where taskbar is big enough to
                        // completely contain the status window.
                        || taskBar.Contains(windowRect))
                    {
                        if (taskBar.Left == 0)
                        {
                            // Task bar is on the left. Push away to the right.
                            window.Left = window.Left + intersection.Width;
                        }
                        else
                        {
                            // Task bar is on the right. Push away to the left.
                            window.Left = window.Left - intersection.Width;
                        }
                    }

                    if (intersection.Height < window.Height
                        // This next one is a rare corner case. Handles situation where taskbar is big enough to
                        // completely contain the status window.
                        || taskBar.Contains(windowRect))
                    {
                        if (taskBar.Top == 0)
                        {
                            // Task bar is on the top. Push down.
                            window.Top = window.Top + intersection.Height;
                        }
                        else
                        {
                            // Task bar is on the bottom. Push up.
                            window.Top = window.Top - intersection.Height;
                        }
                    }

                    windowRect = new Rectangle((int)window.Left, (int)window.Top, (int)window.Width, (int)window.Height);
                }
            }
        }
    }

    /// <summary>
    /// Returned location of taskbar on a per-screen basis, as a rectangle. See:
    /// https://stackoverflow.com/questions/1264406/how-do-i-get-the-taskbars-position-and-size/36285367#36285367.
    /// </summary>
    /// <returns>A list of taskbar locations. If this list is empty, then the taskbar is set to "Auto Hide".</returns>
    private static List<Rectangle> GetTaskBarLocationPerScreen()
    {
        List<Rectangle> dockedRects = new List<Rectangle>();
        foreach (var screen in Screen.AllScreens)
        {
            if (screen.Bounds.Equals(screen.WorkingArea) == true)
            {
                // No taskbar on this screen.
                continue;
            }

            Rectangle rect = new Rectangle();

            var leftDockedWidth = Math.Abs((Math.Abs(screen.Bounds.Left) - Math.Abs(screen.WorkingArea.Left)));
            var topDockedHeight = Math.Abs((Math.Abs(screen.Bounds.Top) - Math.Abs(screen.WorkingArea.Top)));
            var rightDockedWidth = ((screen.Bounds.Width - leftDockedWidth) - screen.WorkingArea.Width);
            var bottomDockedHeight = ((screen.Bounds.Height - topDockedHeight) - screen.WorkingArea.Height);
            if ((leftDockedWidth > 0))
            {
                rect.X = screen.Bounds.Left;
                rect.Y = screen.Bounds.Top;
                rect.Width = leftDockedWidth;
                rect.Height = screen.Bounds.Height;
            }
            else if ((rightDockedWidth > 0))
            {
                rect.X = screen.WorkingArea.Right;
                rect.Y = screen.Bounds.Top;
                rect.Width = rightDockedWidth;
                rect.Height = screen.Bounds.Height;
            }
            else if ((topDockedHeight > 0))
            {
                rect.X = screen.WorkingArea.Left;
                rect.Y = screen.Bounds.Top;
                rect.Width = screen.WorkingArea.Width;
                rect.Height = topDockedHeight;
            }
            else if ((bottomDockedHeight > 0))
            {
                rect.X = screen.WorkingArea.Left;
                rect.Y = screen.WorkingArea.Bottom;
                rect.Width = screen.WorkingArea.Width;
                rect.Height = bottomDockedHeight;
            }
            else
            {
                // Nothing found!
            }

            dockedRects.Add(rect);
        }

        if (dockedRects.Count == 0)
        {
            // Taskbar is set to "Auto-Hide".
        }

        return dockedRects;
    }
}

作为奖励,您可以实现自己的拖放,当拖动完成时,窗口将移回到屏幕上。

从用户的角度来看,如果窗口快速滑回可见区域而不是直接跳回到可见区域,会更直观,但至少这种方法得到了正确的结果。

/// <summary>
///     Intent: Add this Attached Property to any XAML element, to allow you to click and drag the entire window.
///     Essentially, it searches up the visual tree to find the first parent window, then calls ".DragMove()" on it. Once the drag finishes, it pushes
///     the window back onto the screen if part or all of it wasn't visible.
/// </summary>
public class EnableDragAttachedProperty
{
    public static readonly DependencyProperty EnableDragProperty = DependencyProperty.RegisterAttached(
        "EnableDrag",
        typeof(bool),
        typeof(EnableDragAttachedProperty),
        new PropertyMetadata(default(bool), OnLoaded));

    private static void OnLoaded(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
    {
        try
        {
            var uiElement = dependencyObject as UIElement;
            if (uiElement == null || (dependencyPropertyChangedEventArgs.NewValue is bool) == false)
            {
                return;
            }
            if ((bool)dependencyPropertyChangedEventArgs.NewValue == true)
            {
                uiElement.MouseMove += UIElement_OnMouseMove;
            }
            else
            {
                uiElement.MouseMove -= UIElement_OnMouseMove;
            }
        }
        catch (Exception ex)
        {
             // Log exception here.
        }
    }

    /// <summary>
    ///     Intent: Fetches the parent window, so we can call "DragMove()"on it. Caches the results in a dictionary,
    ///     so we can apply this same property to multiple XAML elements.
    /// </summary>
    private static void UIElement_OnMouseMove(object sender, MouseEventArgs mouseEventArgs)
    {
        try
        {
            var uiElement = sender as UIElement;
            if (uiElement != null)
            {
                Window window = GetParentWindow(uiElement);

                if (mouseEventArgs.LeftButton == MouseButtonState.Pressed)
                {
                    // DragMove is a synchronous call: once it completes, the drag is finished and the left mouse
                    // button has been released.
                    window?.DragMove();

                    // See answer in section 'Additional Links' below in the SO answer.
                //HideAndShowWindowHelper.ShiftWindowIntoForeground(window);

                    // When the use has finished the drag and released the mouse button, we shift the window back
                    // onto the screen, it it ended up partially off the screen.
                    ShiftWindowOntoScreenHelper.ShiftWindowOntoScreen(window);
                }
            }
        }
        catch (Exception ex)
        {
            _log.Warn($"Exception in {nameof(UIElement_OnMouseMove)}. " +
                      $"This means that we cannot shift and drag the Toast Notification window. " +
                      $"To fix, correct C# code.", ex);
        }
    }

    public static void SetEnableDrag(DependencyObject element, bool value)
    {
        element.SetValue(EnableDragProperty, value);
    }

    public static bool GetEnableDrag(DependencyObject element)
    {
        return (bool)element.GetValue(EnableDragProperty);
    }

    #region GetParentWindow
    private static readonly Dictionary<UIElement, Window> _parentWindow = new Dictionary<UIElement, Window>();
    private static readonly object _parentWindowLock = new object();

    /// <summary>
    ///     Intent: Given any UIElement, searches up the visual tree to find the parent Window.
    /// </summary>
    private static Window GetParentWindow(UIElement uiElement)
    {
        bool ifAlreadyFound;
        lock (_parentWindowLock)
        {
            ifAlreadyFound = _parentWindow.ContainsKey(uiElement) == true;
        }

        if (ifAlreadyFound == false)
        {
            DependencyObject parent = uiElement;
            int avoidInfiniteLoop = 0;
            // Search up the visual tree to find the first parent window.
            while ((parent is Window) == false)
            {
                parent = VisualTreeHelper.GetParent(parent);
                avoidInfiniteLoop++;
                if (avoidInfiniteLoop == 1000)
                {
                    // Something is wrong - we could not find the parent window.
                    return null;
                }
            }
            lock (_parentWindowLock)
            {
                _parentWindow[uiElement] = parent as Window;
            }
        }
        lock(_parentWindowLock)
        {
            return _parentWindow[uiElement];
        }
    }
    #endregion
}

附加链接

有关如何避免通知窗口被其他窗口隐藏的提示,请参阅我的回答:在 WPF 中将窗口置于最前面 https://stackoverflow.com/questions/257587/bring-a-window-to-the-front-in-wpf.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在WPF中,如果窗口不在屏幕上,如何将其移动到屏幕上? 的相关文章

  • 如何使用 PowerShell 中的凭据从本地复制到远程位置?

    我是 PowerShell 的新手 我有用户名和密码来访问远程位置的共享文件夹 I need 复制文件foo txt从当前位置到 Bar foo myCOmpany com logs在为以下内容编写的 PS1 脚本中Powershell v
  • 如何创建可以像 UserControl 一样编辑的 TabPage 子类?

    我想创建一个包含一些控件的 TabPage 子类 并且我想通过设计器来控制这些控件的布局和属性 但是 如果我在设计器中打开子类 我将无法像在 UserControl 上那样定位它们 我不想创建一个带有 UserControl 实例的 Tab
  • 使用post方法将多个参数发送到asp.net core 3 mvc操作

    使用 http post 方法向 asp net mvc core 3 操作发送具有多个参数的 ajax 请求时存在问题 参数不绑定 在 dot net 框架 asp net web api 中存在类似的限制 但在 asp net mvc
  • C++:重写已弃用的虚拟方法时出现弃用警告

    我有一个纯虚拟类 它有一个纯虚拟方法 应该是const 但不幸的是不是 该接口位于库中 并且该类由单独项目中的其他几个类继承 我正在尝试使用这个方法const不会破坏兼容性 至少在一段时间内 但我找不到在非常量方法重载时产生警告的方法 以下
  • POCO HTTPSClientSession 发送请求时遇到问题 - 证书验证失败

    我正在尝试使用 POCO 库编写一个向服务器发出 HTTPS 请求的程序 出于测试目的 我正在连接到具有自签名证书的服务器 并且我希望允许客户端进行连接 为了允许这种情况发生 我尝试安装InvalidCertificateHandler这是
  • C++ 异步线程同时运行

    我是 C 11 中线程的新手 我有两个线程 我想让它们同时启动 我可以想到两种方法 如下 然而 似乎它们都没有按照我的预期工作 他们在启动另一个线程之前启动一个线程 任何提示将不胜感激 另一个问题是我正在研究线程队列 所以我会有两个消费者和
  • 从多个类访问串行端口

    我正在尝试使用串行端口在 arduino 和 C 程序之间进行通信 我对 C 编程有点陌生 该程序有多种用户控制形式 每一个都需要访问串口来发送数据 我需要做的就是从每个类的主窗体中写入串行端口 我了解如何设置和写入串行端口 这是我的 Fo
  • 在 2D 中将一个点旋转另一个点

    我想知道当一个点相对于另一个点旋转一定角度时如何计算出新的坐标 我有一个块箭头 想要将其相对于箭头底部中间的点旋转角度 theta 这是允许我在两个屏幕控件之间绘制多边形所必需的 我无法使用和旋转图像 从我到目前为止所考虑的情况来看 使问题
  • 如何将 WPF 大小转换为物理像素?

    将 WPF 与分辨率无关 宽度和高度转换为物理屏幕像素的最佳方法是什么 我正在 WinForms 表单中显示 WPF 内容 通过 ElementHost 并尝试制定一些大小调整逻辑 当操作系统以默认 96 dpi 运行时 我可以正常工作 但
  • Azure 事件中心 - 按顺序接收事件

    我使用下面的代码从 Azure Event Hub 接收事件 https learn microsoft com en us azure event hubs event hubs dotnet framework getstarted s
  • 基于xsd模式生成xml(使用.NET)

    我想根据我的 xsd 架构 cap xsd 生成 xml 文件 我找到了这篇文章并按照说明进行操作 使用 XSD 文件生成 XML 文件 https stackoverflow com questions 6530424 generatin
  • 如何在c#中的内部类中访问外部类的变量[重复]

    这个问题在这里已经有答案了 我有两个类 我需要声明两个类共有的变量 如果是嵌套类 我需要访问内部类中的外部类变量 请给我一个更好的方法来在 C 中做到这一点 示例代码 Class A int a Class B Need to access
  • 如何一步步遍历目录树?

    我发现了很多关于遍历目录树的示例 但我需要一些不同的东西 我需要一个带有某种方法的类 每次调用都会从目录返回一个文件 并逐渐遍历目录树 请问我该怎么做 我正在使用函数 FindFirstFile FindNextFile 和 FindClo
  • 是否可以有一个 out ParameterExpression?

    我想定义一个 Lambda 表达式out范围 有可能做到吗 下面是我尝试过的 C Net 4 0 控制台应用程序的代码片段 正如您在 procedure25 中看到的 我可以使用 lambda 表达式来定义具有输出参数的委托 但是 当我想使
  • 耐用功能是否适合大量活动?

    我有一个场景 需要计算 500k 活动 都是小算盘 由于限制 我只能同时计算 30 个 想象一下下面的简单示例 FunctionName Crawl public static async Task
  • strcmp 给出分段错误[重复]

    这个问题在这里已经有答案了 这是我的代码给出分段错误 include
  • 转到定义:“无法导航到插入符号下的符号。”

    这个问题的答案是社区努力 help privileges edit community wiki 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 我今天突然开始在我的项目中遇到一个问题 单击 转到定义 会出现一个奇怪的错误 无法导航到
  • WinRT 定时注销

    我正在开发一个 WinRT 应用程序 要求之一是应用程序应具有 定时注销 功能 这意味着在任何屏幕上 如果应用程序空闲了 10 分钟 应用程序应该注销并导航回主屏幕 显然 执行此操作的强力方法是在每个页面的每个网格上连接指针按下事件 并在触
  • 带重定向标准流的 C# + telnet 进程立即退出

    我正在尝试用 C 做一个 脚本化 telnet 项目 有点类似于Tcl期望 http expect nist gov 我需要为其启动 telnet 进程并重定向 和处理 其 stdin stdout 流 问题是 生成的 telnet 进程在
  • 实例化 Microsoft.Office.Interop.Excel.Application 对象时出现错误:800700c1

    实例化 Microsoft Office Interop Excel Application 以从 winforms 应用程序生成 Excel 时 出现以下错误 这之前是有效的 但突然间它停止工作了 尽管代码和 Excel 版本没有变化 我

随机推荐

  • 如何将抓取的项目放入 Pyqt5 小部件中?

    我正在尝试为 Scrapy 爬虫制作一个简单的 GUI 用户可以按 开始 按钮来运行抓取并在 textBrowser 或其他 qt 小部件 请告知 中查看抓取的结果 我的蜘蛛 import scrapy json class CarSpid
  • 如果元素的位置是绝对的,浏览器的渲染是否会回流?

    如果我有一个具有绝对位置的元素 并且更改其左侧和顶部位置 则会回流到其父子元素吗 如果它自己的孩子不受影响 因为它们也是由左轴和上轴绝对定位的 那么它们又怎么样呢 如果我更改元素的宽度 高度但在父元素及其子元素中不重要 具有绝对位置的对象不
  • 如何在 Typescript 中创建抽象工厂模式?

    我正在尝试在 Typescript 中实现标准抽象工厂模式 但编译器不合作 这是我的代码的简化版本 abstract class Model class User extends Model abstract class ModelFact
  • 如何取消设置全局变量。

    我有一个id一个项目和一个id客户端的会话是以 JSON 格式传递的 php 会话 这些存储在全局变量中id p and id c所以我可以使用这些 id 进行多次插入和更新选择等 当用户选择另一个项目或更改页面时 我需要取消设置这些变量
  • 如何使用正则表达式来忽略包含特定子字符串的字符串?

    我将如何使用负向后查找 或任何其他方法 正则表达式来忽略包含特定子字符串的字符串 我读过之前的两个 stackoverflow 问题 java 正则表达式用于文件过滤 https stackoverflow com questions 36
  • macports PHP5 的 Pear 安装

    我通过 macports 在 opt local macports 的默认位置安装了 PHP5 pear 没有端口文件 如果我从 pear 站点进行标准 pear 安装 则 pear 应该放置在什么目录位置才能与 PHP 一起使用 虽然cu
  • 我如何知道哪个初始化程序是指定的初始化程序?

    我如何知道哪个初始值设定项是任何类的指定初始值设定项 我猜它是需要最多参数的一个 但有时这可能是不正确的 omz的答案可以更坚定地表述 The Documentation for a Framework classwill指定哪个是指定的初
  • C++:从用户输入调用函数

    在Python中 当我有几个根据用户输入调用的不同函数时 我有一个字典 其中用户输入作为键 函数名称作为值 def x y return y def z y return y functions x x z z print function
  • 可以从 CUDD 管理器中删除变量吗?

    谁能告诉我是否可以安全地从 CUDD 中的管理器中删除变量 例如 我通过以下方式注册两个变量v1 Cudd bddNewVar manager and v2 Cudd bddNewVar manager 我可以删除吗v2来自经理 我认为不可
  • 如何使用分隔符连接 PySpark 中的多个列?

    我有一个pyspark Dataframe 我想加入3个专栏 id column 1 column 2 column 3 1 12 34 67 2 45 78 90 3 23 93 56
  • 如何使用 ConstraintLayout 使视图“wrap_content 但不大于”?

    我连续有 3 个视图 标题 版本和图像视图 用作按钮 标题应该是wrap content但遵守以下规则 版本应该是wrap content 位于标题右侧和图像视图左侧 imageview 具有固定大小 位于父级的右上角 问题是 如果标题太大
  • 如何在 Android Studio (Gradle) 中添加 apache commons 集合

    我正在尝试使用 ListUtils 但是当我运行该应用程序时 我收到此错误 Caused by java lang ClassNotFoundException Didn t find class org apache commons co
  • 流星 mongo 驱动程序可以处理 $each 和 $position 运算符吗?

    我正在开发一个流星应用程序 并使用最新的流星包 我想重新定位 mongo 文档数组中的项目 为了实现这一目标 我 pull将其从数组中取出 然后 push它在特定的index位置根据MongoDB 文档 https docs mongodb
  • 如何在不使用 AutoMapper 的情况下手动映射 DTO?

    我正在学习 C NET Core 并尝试在不使用 AutoMapper 的情况下创建 DTO 映射 因为我正在独自开发一个小项目 并且想在使用额外的包之前了解基础知识 令人惊讶的是我无法在 stackoverflow com 上轻松找到答案
  • Xcode:TEST 与 DEBUG 预处理器宏

    使用单元测试创 建新项目时 Xcode 将测试方案的构建配置设置为 调试 与 运行 方案相同 我应该区分运行 Command R 和测试 Command U 方案吗 即 我是否应该创建一个名为 Test 的新构建配置 向其中添加预处理器宏
  • MySql备份与恢复

    试图了解人们如何进行完整备份 恢复过程 用户定义的数据库模式和数据可以通过 mysqldump 轻松备份 但是主表和数据呢 即 如果服务器完全崩溃 我该如何重建数据库 即包括 Mysql 中的所有设置 这只是转储 导入 informatio
  • jQuery.bind("删除")

    有没有办法在 DOM 元素被删除时运行事件处理程序 我没有在任何地方看到过这个记录 看起来这是可能的 因为 jQuery 能够在元素删除时删除数据和事件 Binding DOMNodeRemoved将允许您检测绑定元素内节点的删除 适用于
  • Android - R 类的问题

    我正在尝试在 Android 中编写一些基本的东西 listView 等 我的问题如下 1 我编写的任何资源 例如 指定 listView 或按钮内容的 xml 文件 都会在 R 类中注册 但当我尝试使用它时 Eclipse 将其标记为错误
  • 为什么在 React 示例中 useRef 初始化为 null?

    In the 官方 React 文档 https reactjs org docs hooks reference html useref以及 我看到的所有例子useRef像这样使用 const ref useRef null 我发现它无需
  • 在WPF中,如果窗口不在屏幕上,如何将其移动到屏幕上?

    如果我有一个窗口 如何确保该窗口永远不会隐藏在屏幕之外 这很重要 因为有时如果用户添加或删除监视器 如果我们记住了之前的位置 窗口可能会永久隐藏在屏幕之外 我在用WPF MVVM 这个答案已经在大规模的现实世界应用中得到了测试 从任何附加属