【问题标题】:How to force WPF startup window to specific screen?如何强制 WPF 启动窗口到特定屏幕?
【发布时间】:2012-11-05 10:27:06
【问题描述】:

我有一个 WPF 应用程序,它将通过专用窗口在投影仪上显示信息。 我想配置用于投影仪显示的屏幕以及用于主应用程序窗口的屏幕。

此代码将在指定屏幕上生成投影仪输出:

var screen = GetProjectorScreen();
_projectorWindow = new ProjectorWindow();
_projectorWindow.Left = screen.WorkingArea.Left;
_projectorWindow.Top = screen.WorkingArea.Top;
_projectorWindow.Owner = _parentWindow;
_projectorWindow.Show();


public static Screen GetProjectorScreen()
{
    var screens = Screen.AllScreens;
    if (screens.Length > 1 && Settings.Default.DisplayScreen < screens.Length)
    {
        return screens[Settings.Default.DisplayScreen];
    }
    return screens[0];
}

我尝试用启动表单做同样的技巧,但到目前为止没有成功。 我尝试在 MainWindow 构造函数中设置 Top 和 Left 属性,但没有成功。

通过设置 StartupUri:从 App.xaml.cs 启动启动窗口:

StartupUri = new Uri("Windows/MainWindow.xaml", UriKind.Relative);

还有其他方法可以启动启动表单吗? 我试图只调用构造函数,但这会导致崩溃,因为不再加载某些资源。

【问题讨论】:

  • 一个主要问题浮现在脑海中 - 您如何知道计算机上连接了投影仪?如果没有投影仪会怎样?

标签: c# wpf xaml window startup


【解决方案1】:

我让它工作了。在设置窗口位置之前,需要将 WindowState 设置为 Normal。并且在创建窗口之前,即在构造函数调用之后,该设置将根本不起作用。因此,我在 Windows_Loaded 事件中调用了显式设置。如果需要移动窗口,这可能会导致闪烁,但我可以接受。

SetScreen 方法也应该在用户手动更改屏幕设置后调用。

private void SetScreen()
{
    var mainScreen = ScreenHandler.GetMainScreen();
    var currentScreen = ScreenHandler.GetCurrentScreen(this);
    if (mainScreen.DeviceName != currentScreen.DeviceName)
    {
        this.WindowState = WindowState.Normal;
        this.Left = mainScreen.WorkingArea.Left;
        this.Top = mainScreen.WorkingArea.Top;
        this.Width = mainScreen.WorkingArea.Width;
        this.Height = mainScreen.WorkingArea.Height;
        this.WindowState = WindowState.Maximized;
    }
}

备份 ScreenHandler 实用程序非常简单:

public static class ScreenHandler
{
    public static Screen GetMainScreen()
    {
        return GetScreen(Settings.Default.MainScreenId);
    }

    public static Screen GetProjectorScreen()
    {
        return GetScreen(Settings.Default.ProjectorScreenId);
    }

    public static Screen GetCurrentScreen(Window window)
    {
        var parentArea = new Rectangle((int)window.Left, (int)window.Top, (int)window.Width, (int)window.Height);
        return Screen.FromRectangle(parentArea);
    }

    private static Screen GetScreen(int requestedScreen)
    {
        var screens = Screen.AllScreens;
        var mainScreen = 0;
        if (screens.Length > 1 && mainScreen < screens.Length)
        {
            return screens[requestedScreen];
        }
        return screens[0];
    }
}

【讨论】:

    【解决方案2】:

    已接受的答案不再适用于应用清单中具有每个显示器 DPI 的 Windows 10。

    这对我有用:

    public partial class MyWindow : Window
    {
        readonly Rectangle screenRectangle;
    
        public MyWindow( System.Windows.Forms.Screen screen )
        {
            screenRectangle = screen.WorkingArea;
            InitializeComponent();
        }
    
        [DllImport( "user32.dll", SetLastError = true )]
        static extern bool MoveWindow( IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint );
    
        protected override void OnSourceInitialized( EventArgs e )
        {
            base.OnSourceInitialized( e );
            var wih = new WindowInteropHelper( this );
            IntPtr hWnd = wih.Handle;
            MoveWindow( hWnd, screenRectangle.Left, screenRectangle.Top, screenRectangle.Width, screenRectangle.Height, false );
        }
    
        void Window_Loaded( object sender, RoutedEventArgs e )
        {
            WindowState = WindowState.Maximized;
        }
    }
    

    仅设置 Left/Top 不起作用。根据我的测试,每个显示器的 DPI 意识只有在窗口已经创建并放置在某个显示器上之后才会启动。在此之前,显然窗口的左/上属性与主监视器的 DPI 一起缩放。

    对于每个显示器 DPI 和显示器布局的某些组合,这会导致一个错误,将 Left/Top 属性设置为 System.Windows.Forms.Screen 矩形的像素值会导致窗口位于其他位置。

    上述解决方法仅适用于最大化,它并不总是设置正确的窗口大小。但至少它设置了正确的左上角,足以让最大化正常工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-21
      • 2012-07-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-11
      相关资源
      最近更新 更多