BUG: Window Size is Limited to Primary Screen Size

Developer
Feb 11, 2014 at 2:10 PM
Edited Feb 12, 2014 at 10:46 AM
If you have a multi-monitor setup, you cannot resize the Elysium Window to a size larger than your primary monitor screen resolution. I downloaded the latest Elysium source code and the problem seems to be fixed but as there is so much low level P/Invoke stuff going on, its difficult to see how the bug was fixed for the currently released version of Elysium.

UPDATE

Ok, so I fixed the problem which is VERY hack-ish but will get the job done until the next version of Elysium hopefully fixes this. You will need to create a new Window class based on the Elysium Window class and add the code below:
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;

public class MyWindow : Elysium.Controls.Window
{
    #region HACK TO ALLOW RESIZING BIGGER THAN PRIMARY SCREEN RESOLUTION. REMOVE UPON UPGRADE OF ELYSIUM

        private IntPtr handle;
        private Assembly assembly = Assembly.GetAssembly(typeof(Elysium.Theme));
        private Type monitorType;
        private Type taskbarType;
        private BindingFlags bindingFlags = BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.GetField;

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);

            this.handle = new WindowInteropHelper(this).Handle;
            this.assembly = Assembly.GetAssembly(typeof(Elysium.Theme));
            this.monitorType = this.assembly.GetType("Elysium.Native.Monitor");
            this.taskbarType = this.assembly.GetType("Elysium.Native.Taskbar");

            HwndSource.FromHwnd(handle).AddHook(MaximizedSizeFixWindowProc);
        }

        private IntPtr MaximizedSizeFixWindowProc(
            IntPtr hwnd,
            int msg,
            IntPtr wParam,
            IntPtr lParam,
            ref bool handled)
        {
            switch (msg)
            {
                case NativeMethods.WM_GETMINMAXINFO:
                    // Handle the message and mark it as handled,
                    // so other callbacks do not touch it
                    WmGetMinMaxInfo(hwnd, lParam);
                    handled = true;
                    break;
            }

            return (IntPtr)0;
        }

        private void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
        {
            var structure = (NativeMethods.MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(NativeMethods.MINMAXINFO));

            // var monitor = new Monitor(_handle);
            // monitor.Invalidate();
            // Taskbar.Invalidate();
            object monitor = this.monitorType.GetConstructors(this.bindingFlags).First().Invoke(new object[] { this.handle });
            this.monitorType.GetMethod("Invalidate", this.bindingFlags).Invoke(monitor, new object[] { });
            this.taskbarType.GetMethod("Invalidate", this.bindingFlags).Invoke(null, new object[] { });

            // var bounds = monitor.Bounds;
            // var workArea = monitor.WorkArea;
            object bounds = this.monitorType.GetProperty("Bounds", this.bindingFlags).GetValue(monitor);
            object workArea = this.monitorType.GetProperty("WorkArea", this.bindingFlags).GetValue(monitor);

            int boundsLeft = (int)bounds.GetType().GetField("left", this.bindingFlags).GetValue(bounds);
            int boundsTop = (int)bounds.GetType().GetField("top", this.bindingFlags).GetValue(bounds);

            // int workAreaLeft = (int)workArea.GetType().GetField("left", bindingFlags).GetValue(bounds);
            // int workAreaRight = (int)workArea.GetType().GetField("right", bindingFlags).GetValue(bounds);
            int workAreaLeft = 0;
            int workAreaRight = (int)SystemParameters.VirtualScreenWidth;
            int workAreaTop = (int)workArea.GetType().GetField("top", this.bindingFlags).GetValue(bounds);
            int workAreaBottom = (int)workArea.GetType().GetField("bottom", this.bindingFlags).GetValue(bounds);

            int taskbarPosition = (int)taskbarType.GetProperty("Position", this.bindingFlags).GetValue(null);
            bool taskbarAutoHide = (bool)taskbarType.GetProperty("AutoHide", this.bindingFlags).GetValue(null);

            structure.MaxPosition.X = Math.Abs(boundsLeft) + taskbarPosition == 0 && taskbarAutoHide ? 1 : 0;
            structure.MaxPosition.Y = Math.Abs(boundsTop) + taskbarPosition == 1 && taskbarAutoHide ? 1 : 0;
            structure.MaxSize.X = structure.MaxTrackSize.X = Math.Abs(workAreaRight - workAreaLeft) - (taskbarPosition == 2 && taskbarAutoHide ? 1 : 0);
            structure.MaxSize.Y = structure.MaxTrackSize.Y = Math.Abs(workAreaBottom - workAreaTop) - (taskbarPosition == 3 && taskbarAutoHide ? 1 : 0);

            var source = PresentationSource.FromVisual(this);
            if (source != null && source.CompositionTarget != null)
            {
                if (IsNonNegative(MinWidth))
                {
                    structure.MinTrackSize.X = (int)Math.Ceiling(MinWidth * source.CompositionTarget.TransformFromDevice.M11);
                }
                if (IsNonNegative(MinHeight))
                {
                    structure.MinTrackSize.Y = (int)Math.Ceiling(MinHeight * source.CompositionTarget.TransformFromDevice.M22);
                }
                if (IsNonNegative(MaxWidth))
                {
                    structure.MaxSize.X = structure.MaxTrackSize.X = Math.Min(structure.MaxSize.X, (int)Math.Ceiling(MaxWidth * source.CompositionTarget.TransformFromDevice.M11));
                }
                if (IsNonNegative(MaxHeight))
                {
                    structure.MaxSize.Y = structure.MaxTrackSize.Y = Math.Min(structure.MaxSize.Y, (int)Math.Ceiling(MaxHeight * source.CompositionTarget.TransformFromDevice.M22));
                }
            }

            Marshal.StructureToPtr(structure, lParam, true);
        }

        private static bool IsNonNegative(double value)
        {
            return !double.IsNaN(value) && !double.IsInfinity(value) && value > 0d;
        }

        private static class NativeMethods
        {
            #region Constants
            public const int WM_GETMINMAXINFO = 0x0024;
            private const int MONITOR_DEFAULTTONULL = 0;
            private const int MONITOR_DEFAULTTOPRIMARY = 1;
            private const int MONITOR_DEFAULTTONEAREST = 2;
            #endregion

            #region Structs

            [Serializable, StructLayout(LayoutKind.Sequential)]
            public struct POINT
            {
                public int X;
                public int Y;
            }
            /// </remarks>
            [StructLayout(LayoutKind.Sequential)]
            public struct RECT
            {
                public int Left;
                public int Top;
                public int Right;
                public int Bottom;
            }
            [StructLayout(LayoutKind.Sequential)]
            public struct MINMAXINFO
            {
                public POINT Reserved;
                public POINT MaxSize;
                public POINT MaxPosition;
                public POINT MinTrackSize;
                public POINT MaxTrackSize;
            }
            [StructLayout(LayoutKind.Sequential)]
            public struct WINDOWINFO
            {
                public uint Size;
                public RECT Window;
                public RECT Client;
                public uint Style;
                public uint ExStyle;
                public uint WindowStatus;
                public uint WindowBordersWidth;
                public uint WindowBordersHeight;
                public ushort WindowType;
                public ushort CreatorVersion;
            }
            [StructLayout(LayoutKind.Sequential)]
            public struct MONITORINFO
            {
                public int Size;
                public RECT Monitor;
                public RECT WorkArea;
                public uint Flags;
            }

            #endregion

            #region Imported Methods

            [return: MarshalAs(UnmanagedType.Bool)]
            [DllImport("user32.dll", SetLastError = true)]
            private static extern bool GetWindowInfo(IntPtr hwnd, ref WINDOWINFO pwi);
            [DllImport("user32.dll")]
            private static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);

            #endregion
        }

        #endregion
}