Reputation: 105
I have a PC with 1-4 monitors. Every monitor may have a different resolution. I get screens(monitors) and their resolution with System.Windows.Forms.Screen.AllScreens. For every screen I open a new fullscreen window. I have a list of data items (which may take different height when displayed). I want to show this data items using ItemsControl. When all space(height) is used on 1st monitor it must go to second and so on...but only full data item must be on screen.
How can I do this? Or do i need to use only 1 window stretched to many screens?
Upvotes: 2
Views: 1348
Reputation: 423
I'm a noob, but i'll try to help. I'm working on a wpf fullscreen dual monitor app. (i want the taskbar to be visible).
<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>
Breakdown
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);
}
This handles my specific case: i've a secondary monitor, left of the primary one, smaller and a little lower than the first.
Now you can probably make some sense of the grid.
Breakdown
ScreensUtils could totally be a method. I plan on extending it a little, so I made it a class.
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);
}
}
This still needs a title bar. Either stretched across monitors or on primary only. Should be no problem for anyone(a simple gridrow or a grid + the already present content grids in a stack/dock panel. )
There is the problem of auto hidden task bars. (that space will appear empty when the taskbar hides).
For this there is:
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;
}
}
You use it like
var _taskbar = new TaskbarUtils;
if(_taskbar.AlwaysOnTop) {}
This is a print screen.
I've found found that Screen.AllScreens[index].WorkingArea.Bottom reports a messed up value. I use Screen.AllScreens[index].WorkingArea.Top + Screen.AllScreens[index].WorkingArea.Size.Height
The code I've provided sucks. So does ScreenUtils. It's done on the fly and with no consideration to other cases besides my own. But it's something. As I'll actually DoWork() on this, i'll update my answer.
You should be totally capable of adapting to your needs.
If you find something wrong or if you improve, please post your findings.
Best of luck!
PS: the taskbar class was taken from here
Upvotes: 2