【问题标题】:WPF display data on multi monitorsWPF 在多显示器上显示数据
【发布时间】:2019-09-22 01:14:48
【问题描述】:

我有一台带有 1-4 台显示器的 PC。每个显示器可能有不同的分辨率。我使用 System.Windows.Forms.Screen.AllScreens 获得屏幕(监视器)及其分辨率。对于每个屏幕,我都会打开一个新的全屏窗口。 我有一个数据项列表(显示时可能采用不同的高度)。我想使用 ItemsControl 显示这些数据项。当第一台显示器上使用所有空间(高度)时,它必须转到第二台,依此类推......但只有完整的数据项必须在屏幕上。

我该怎么做?还是我只需要使用 1 个窗口拉伸到多个屏幕?

【问题讨论】:

    标签: .net wpf multiple-monitors


    【解决方案1】:

    我是一个菜鸟,但我会尽力提供帮助。我正在开发一个 wpf 全屏双显示器应用程序。 (我希望任务栏可见)。

    XAML

    <Window x:Class="SlideWpf.SecondWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="500" Width="800" BorderThickness="0" AllowsTransparency="True" WindowStyle="None">
        <Window.Background>
            <SolidColorBrush Opacity="0"></SolidColorBrush>
        </Window.Background>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="55*" Name="GRowTop"/>
                <RowDefinition Height="409*" Name="GRowContent"/>
                <RowDefinition Height="36*" Name="GRowBottom"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="89*" Name="GColLeft"/>
                <ColumnDefinition Width="659*" Name="GColMid"/>
                <ColumnDefinition Width="52*" Name="GColRight"/>
            </Grid.ColumnDefinitions>
    
            <Border BorderThickness="2" BorderBrush="Red" Grid.Row="0" Grid.Column="1" Grid.RowSpan="2">
                <Border BorderThickness="10" BorderBrush="Black">
                    <Grid Name="g1" Background="AntiqueWhite"></Grid>
                </Border>
            </Border>
            <Border BorderThickness="2" BorderBrush="Red" Grid.Row="1" Grid.Column="0" Grid.RowSpan="2">
                <Border BorderThickness="10" BorderBrush="Black">
                    <Grid Name="g2"  Background="Beige"></Grid>
                </Border>
            </Border>
        </Grid>
    </Window>
    

    细分

    • 无边框形式
    • 允许透明度:true,通过solidcolorbrush 设置窗口透明度,因此该窗口中的控件不会继承不透明度。
    • 具有列和行的网格,将容纳第一个和第二个监视器空间。请注意,它们是双边框的。
    • 边框是临时的,所以我可以正确对齐它们(看看我哪里搞砸了)

    Xaml.cs

    if (ScreensUtils.Bounds(1).Left < ScreensUtils.Bounds(0).Left)
                {
                    Left = ScreensUtils.Location(1).X;
                    Top = Math.Min(ScreensUtils.Location(1).Y, ScreensUtils.Location(0).Y);
    
                    Width = ScreensUtils.Size(0).Width + ScreensUtils.Size(1).Width;
                    Height = Math.Max(ScreensUtils.Bounds(0).Bottom, ScreensUtils.Bounds(1).Top + ScreensUtils.Size(1).Height);
    
    
                    GRowTop.Height = new GridLength(ScreensUtils.Location(1).Y);
    
                    GColLeft.Width = new GridLength(ScreensUtils.Size(1).Width);
                    GColMid.Width = new GridLength(ScreensUtils.Size(0).Width);
    
                    GRowBottom.Height = new GridLength(ScreensUtils.Bounds(1).Top + ScreensUtils.Size(1).Height - ScreensUtils.Bounds(0).Bottom);
                }
    

    这处理了我的具体情况:我有一个辅助显示器,在主显示器的左侧,比第一个更小且略低。

    现在您大概可以理解网格了。

    细分

    ScreensUtils 完全可以是一种方法。我打算稍微扩展一下,所以我把它变成了一个类。

    class ScreensUtils
        {
            public static Size Size(int index)
            {
                return Screen.AllScreens[index].WorkingArea.Size;
            }
            public static Point Location(int index)
            {
                return Screen.AllScreens[index].WorkingArea.Location;
            }
            public static Rectangle Bounds(int index)
            {
                return new Rectangle( Screen.AllScreens[index].WorkingArea.Left, Screen.AllScreens[index].WorkingArea.Top, Screen.AllScreens[index].WorkingArea.Left + Screen.AllScreens[index].WorkingArea.Size.Width , Screen.AllScreens[index].WorkingArea.Top + Screen.AllScreens[index].WorkingArea.Size.Height);
            }
        }
    
    • 基本上我们得到第二台显示器的尺寸和位置。这 第一个也一样。
    • 我们比较和调整网格列和行的大小为 需要。
    • 我们可能还需要更改内容的位置 存放区,取决于存放区的大小、数量和位置 列。

    这仍然需要一个标题栏。要么跨显示器拉伸,要么仅在主要显示器上。对任何人来说都应该没问题(一个简单的网格网格+堆栈/停靠面板中已经存在的内容网格。)

    存在自动隐藏任务栏的问题。 (当任务栏隐藏时,该空间将显示为空)。

    为此有:

    class TaskbarUtils
    {
        public enum TaskbarPosition
        {
            Unknown = -1,
            Left,
            Top,
            Right,
            Bottom,
        }
    
        public sealed class Taskbar
        {
            private const string ClassName = "Shell_TrayWnd";
    
            public Rectangle Bounds
            {
                get;
                private set;
            }
            public TaskbarPosition Position
            {
                get;
                private set;
            }
            public Point Location
            {
                get
                {
                    return this.Bounds.Location;
                }
            }
            public Size Size
            {
                get
                {
                    return this.Bounds.Size;
                }
            }
            //Always returns false under Windows 7
            public bool AlwaysOnTop
            {
                get;
                private set;
            }
            public bool AutoHide
            {
                get;
                private set;
            }
    
            public Taskbar()
            {
                IntPtr taskbarHandle = User32.FindWindow(Taskbar.ClassName, null);
    
                APPBARDATA data = new APPBARDATA();
                data.cbSize = (uint)Marshal.SizeOf(typeof(APPBARDATA));
                data.hWnd = taskbarHandle;
                IntPtr result = Shell32.SHAppBarMessage(ABM.GetTaskbarPos, ref data);
                if (result == IntPtr.Zero)
                    throw new InvalidOperationException();
    
                this.Position = (TaskbarPosition)data.uEdge;
                this.Bounds = Rectangle.FromLTRB(data.rc.left, data.rc.top, data.rc.right, data.rc.bottom);
    
                data.cbSize = (uint)Marshal.SizeOf(typeof(APPBARDATA));
                result = Shell32.SHAppBarMessage(ABM.GetState, ref data);
                int state = result.ToInt32();
                this.AlwaysOnTop = (state & ABS.AlwaysOnTop) == ABS.AlwaysOnTop;
                this.AutoHide = (state & ABS.Autohide) == ABS.Autohide;
            }
        }
    
        public enum ABM : uint
        {
            New = 0x00000000,
            Remove = 0x00000001,
            QueryPos = 0x00000002,
            SetPos = 0x00000003,
            GetState = 0x00000004,
            GetTaskbarPos = 0x00000005,
            Activate = 0x00000006,
            GetAutoHideBar = 0x00000007,
            SetAutoHideBar = 0x00000008,
            WindowPosChanged = 0x00000009,
            SetState = 0x0000000A,
        }
    
        public enum ABE : uint
        {
            Left = 0,
            Top = 1,
            Right = 2,
            Bottom = 3
        }
    
        public static class ABS
        {
            public const int Autohide = 0x0000001;
            public const int AlwaysOnTop = 0x0000002;
        }
    
        public static class Shell32
        {
            [DllImport("shell32.dll", SetLastError = true)]
            public static extern IntPtr SHAppBarMessage(ABM dwMessage, [In] ref APPBARDATA pData);
        }
    
        public static class User32
        {
            [DllImport("user32.dll", SetLastError = true)]
            public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public struct APPBARDATA
        {
            public uint cbSize;
            public IntPtr hWnd;
            public uint uCallbackMessage;
            public ABE uEdge;
            public RECT rc;
            public int lParam;
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }
    }
    

    你使用它就像

    var _taskbar = new TaskbarUtils;
    if(_taskbar.AlwaysOnTop) {}
    

    输出

    这是一个打印屏幕。

    最后说明

    我发现 Screen.AllScreens[index].WorkingArea.Bottom 报告一个混乱的值。我用 Screen.AllScreens[index].WorkingArea.Top + Screen.AllScreens[index].WorkingArea.Size.Height

    我提供的代码很糟糕。 ScreenUtils 也是如此。它是即时完成的,除了我自己的情况外,没有考虑其他情况。但它是一些东西。因为我实际上会在这方面做 DoWork(),所以我会更新我的答案。

    你应该完全有能力适应你的需要。

    如果您发现问题或改进,请发布您的发现。

    祝你好运!

    PS:任务栏类取自here

    【讨论】:

      猜你喜欢
      • 2011-02-10
      • 2018-10-24
      • 1970-01-01
      • 2016-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多