I have created a borderless application in WPF, and it works pretty good. However, when I set the WindowState to full screen, the application takes up more space than my screen resolution, so there are some pixels outside the screen in all directions! (looks like some hard coded negative margins are added to hide the default border)
Any Ideas how to prevent this from happening?
My xaml:
<Window x:Class="MyApp.Shell"
WindowStyle="None"
BorderThickness="0"
AllowsTransparency="True"
Background="Transparent"
ResizeMode="CanResizeWithGrip"
WindowState="{Binding MainApplicationWindowState}"
...
Also, another problem I have seen is that the Windows toolbar / taskbar is covered in the fullsize state, so it looks like the "actual" screen height is used and not the "available" screen height, meaning screen height minus the windows toolbar / taskbar!
Anyone found a solution to these issues?
Thanks
I found this great solution
<Window WindowStyle="None" WindowStartupLocation="CenterScreen" WindowState="Maximized"
MaxWidth="{Binding Source={x:Static SystemParameters.WorkArea}, Path=Width}"
MaxHeight="{Binding Source={x:Static SystemParameters.WorkArea}, Path=Height}"
>
...
</Window>
But error for me still persists, windows is offset by few pixels on top and left... I tried to set Left and Top to 0 after I change window state but nothing happens.
I solved the problem this way:
XAML:
WindowStyle="None"
Left="0"
Top="0"
Width="{Binding WPFSettings.Width}"
Height="{Binding WPFSettings.Height}">
Visual Basic:
Public Class WPFSettings
Public ReadOnly Property Width() As Double
Get
Return System.Windows.SystemParameters.PrimaryScreenWidth
End Get
End Property
Public ReadOnly Property Height() As Double
Get
Return System.Windows.SystemParameters.PrimaryScreenHeight
End Get
End Property
End Class
It works pretty good.
Try this
Width="{Binding Source={x:Static SystemParameters.WorkArea}, Path=Width}"
Height="{Binding Source={x:Static SystemParameters.WorkArea}, Path=Height}"
A newer feature of .NET has solved this problem.
Leave your WindowStyle="SingleBorderWindow" or "ThreeDBorderWindow"
Leave ResizeMode="CanResize", then add this to the xaml inside the
<Window>
<WindowChrome.WindowChrome>
<WindowChrome GlassFrameThickness="0" CornerRadius="0" CaptionHeight="0"
UseAeroCaptionButtons="False" ResizeBorderThickness="7" />
</WindowChrome.WindowChrome>
</Window>
The window will not have any of the default window pane, but will still allow resizing and will not cover the task bar when maximized.
In my case the XAML tag of the Window had the Property SizeToContent="True" and all I had to do was to remove it.
In Xaml set the following binding on the Window.MaxHeight:
MaxHeight="{DynamicResource {x:Static SystemParameters.MaximizedPrimaryScreenHeightKey}}"
No need for an additional utility class.
I needed this to work across different size displays (like most people it sounds). So I simply did this...
<Window x:Class="MyApp.MainWindow"
ResizeMode="CanResize"
WindowStyle="SingleBorderWindow"
SizeChanged="Window_SizeChanged">
....
Code
....
</Window>
public void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (this.WindowState == WindowState.Maximized)
{
this.BorderThickness = new System.Windows.Thickness(8);
}
else
{
this.BorderThickness = new System.Windows.Thickness(0);
}
}
I am pretty late to the club, but I believe someone coming after me may benefit out of it.
I tried several solutions but most of them failed to work in multiple displays. So, I am sharing a solution that worked perfectly for me in a commercial application.
I registered SourceInitialized event. And in the callback for the event, I added the following code,
private void Window_SourceInitialized (object sender, EventArgs e)
{
WindowSizing.WindowInitialized(this);
}
While following is the code for WindowSizing class,
public static class WindowSizing
{
const int MONITOR_DEFAULTTONEAREST = 0x00000002;
#region DLLImports
[DllImport("shell32", CallingConvention = CallingConvention.StdCall)]
public static extern int SHAppBarMessage (int dwMessage, ref APPBARDATA pData);
[DllImport("user32", SetLastError = true)]
static extern IntPtr FindWindow (string lpClassName, string lpWindowName);
[DllImport("user32")]
internal static extern bool GetMonitorInfo (IntPtr hMonitor, MONITORINFO lpmi);
[DllImport("user32")]
internal static extern IntPtr MonitorFromWindow (IntPtr handle, int flags);
#endregion
private static MINMAXINFO AdjustWorkingAreaForAutoHide (IntPtr monitorContainingApplication, MINMAXINFO mmi)
{
IntPtr hwnd = FindWindow("Shell_TrayWnd", null);
if ( hwnd == null )
return mmi;
IntPtr monitorWithTaskbarOnIt = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if ( !monitorContainingApplication.Equals(monitorWithTaskbarOnIt) )
return mmi;
APPBARDATA abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
abd.hWnd = hwnd;
SHAppBarMessage((int) ABMsg.ABM_GETTASKBARPOS, ref abd);
int uEdge = GetEdge(abd.rc);
bool autoHide = System.Convert.ToBoolean(SHAppBarMessage((int) ABMsg.ABM_GETSTATE, ref abd));
if ( !autoHide )
return mmi;
switch ( uEdge )
{
case (int) ABEdge.ABE_LEFT:
mmi.ptMaxPosition.x += 2;
mmi.ptMaxTrackSize.x -= 2;
mmi.ptMaxSize.x -= 2;
break;
case (int) ABEdge.ABE_RIGHT:
mmi.ptMaxSize.x -= 2;
mmi.ptMaxTrackSize.x -= 2;
break;
case (int) ABEdge.ABE_TOP:
mmi.ptMaxPosition.y += 2;
mmi.ptMaxTrackSize.y -= 2;
mmi.ptMaxSize.y -= 2;
break;
case (int) ABEdge.ABE_BOTTOM:
mmi.ptMaxSize.y -= 2;
mmi.ptMaxTrackSize.y -= 2;
break;
default:
return mmi;
}
return mmi;
}
private static int GetEdge (RECT rc)
{
int uEdge = -1;
if ( rc.top == rc.left && rc.bottom > rc.right )
uEdge = (int) ABEdge.ABE_LEFT;
else if ( rc.top == rc.left && rc.bottom < rc.right )
uEdge = (int) ABEdge.ABE_TOP;
else if ( rc.top > rc.left )
uEdge = (int) ABEdge.ABE_BOTTOM;
else
uEdge = (int) ABEdge.ABE_RIGHT;
return uEdge;
}
public static void WindowInitialized (Window window)
{
IntPtr handle = (new System.Windows.Interop.WindowInteropHelper(window)).Handle;
System.Windows.Interop.HwndSource.FromHwnd(handle).AddHook(new System.Windows.Interop.HwndSourceHook(WindowProc));
}
private static IntPtr WindowProc (System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)
{
switch ( msg )
{
case 0x0024:
WmGetMinMaxInfo(hwnd, lParam);
handled = true;
break;
}
return (IntPtr) 0;
}
private static void WmGetMinMaxInfo (IntPtr hwnd, IntPtr lParam)
{
MINMAXINFO mmi = (MINMAXINFO) Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
IntPtr monitorContainingApplication = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if ( monitorContainingApplication != System.IntPtr.Zero )
{
MONITORINFO monitorInfo = new MONITORINFO();
GetMonitorInfo(monitorContainingApplication, monitorInfo);
RECT rcWorkArea = monitorInfo.rcWork;
RECT rcMonitorArea = monitorInfo.rcMonitor;
mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
mmi.ptMaxTrackSize.x = mmi.ptMaxSize.x; //maximum drag X size for the window
mmi.ptMaxTrackSize.y = mmi.ptMaxSize.y; //maximum drag Y size for the window
mmi.ptMinTrackSize.x = 800; //minimum drag X size for the window
mmi.ptMinTrackSize.y = 600; //minimum drag Y size for the window
mmi = AdjustWorkingAreaForAutoHide(monitorContainingApplication, mmi); //need to adjust sizing if taskbar is set to autohide
}
Marshal.StructureToPtr(mmi, lParam, true);
}
public enum ABEdge
{
ABE_LEFT = 0,
ABE_TOP = 1,
ABE_RIGHT = 2,
ABE_BOTTOM = 3
}
public enum ABMsg
{
ABM_NEW = 0,
ABM_REMOVE = 1,
ABM_QUERYPOS = 2,
ABM_SETPOS = 3,
ABM_GETSTATE = 4,
ABM_GETTASKBARPOS = 5,
ABM_ACTIVATE = 6,
ABM_GETAUTOHIDEBAR = 7,
ABM_SETAUTOHIDEBAR = 8,
ABM_WINDOWPOSCHANGED = 9,
ABM_SETSTATE = 10
}
[StructLayout(LayoutKind.Sequential)]
public struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public bool lParam;
}
[StructLayout(LayoutKind.Sequential)]
public struct MINMAXINFO
{
public POINT ptReserved;
public POINT ptMaxSize;
public POINT ptMaxPosition;
public POINT ptMinTrackSize;
public POINT ptMaxTrackSize;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class MONITORINFO
{
public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));
public RECT rcMonitor = new RECT();
public RECT rcWork = new RECT();
public int dwFlags = 0;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
public POINT (int x, int y)
{
this.x = x;
this.y = y;
}
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
}
This works like a magic for many scenarios, like maximizing the window with no border covers the task bar as well. This solution keeps the taskbar uncovered as well.
Regards,
Umar
I use a trigger on the window:
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},Path=WindowState}" Value="{x:Static WindowState.Maximized}">
<Setter Property="BorderThickness" Value="8"/>
</DataTrigger>
</Style.Triggers>
this trigger create a Border when the windowstate is Maximized. I think it's useful.
Related
I added this class :
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests
{
class WindowUtility
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
const uint SWP_NOSIZE = 0x0001;
const uint SWP_NOZORDER = 0x0004;
private static Size GetScreenSize() => new Size(GetSystemMetrics(0), GetSystemMetrics(1));
private struct Size
{
public int Width { get; set; }
public int Height { get; set; }
public Size(int width, int height)
{
Width = width;
Height = height;
}
}
[DllImport("User32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
private static extern int GetSystemMetrics(int nIndex);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(HandleRef hWnd, out Rect lpRect);
[StructLayout(LayoutKind.Sequential)]
private struct Rect
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
}
private static Size GetWindowSize(IntPtr window)
{
if (!GetWindowRect(new HandleRef(null, window), out Rect rect))
throw new Exception("Unable to get window rect!");
int width = rect.Right - rect.Left;
int height = rect.Bottom - rect.Top;
return new Size(width, height);
}
public static void MoveWindowToCenter()
{
IntPtr window = Process.GetCurrentProcess().MainWindowHandle;
if (window == IntPtr.Zero)
throw new Exception("Couldn't find a window to center!");
Size screenSize = GetScreenSize();
Size windowSize = GetWindowSize(window);
int x = (screenSize.Width - windowSize.Width) / 2;
int y = (screenSize.Height - windowSize.Height) / 2;
SetWindowPos(window, IntPtr.Zero, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
}
}
Using it in Program.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Tests
{
class Program
{
static void Main(string[] args)
{
WindowUtility.MoveWindowToCenter();
The problem is that it's showing for a second the window in some random position when running the application and then move it to the screen center.
Is there a way to make that when starting the application the window already will be in the center of the screen ?
I tried to use the accepted answer in this question in the link :
Show/Hide the console window of a C# console application
but then when i'm hiding the window then try to center it then to show it again when it's trying to center it can't find the window because it's hidden so it's throwing this message in the WindowUtility class :
"Couldn't find a window to center!"
I know you have your answer but this is I created for your question.
internal class WindowUtility
{
// P/Invoke declarations.
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
const int MONITOR_DEFAULTTOPRIMARY = 1;
[DllImport("user32.dll")]
static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
[StructLayout(LayoutKind.Sequential)]
struct MONITORINFO
{
public uint cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
public static MONITORINFO Default
{
get { var inst = new MONITORINFO(); inst.cbSize = (uint)Marshal.SizeOf(inst); return inst; }
}
}
[StructLayout(LayoutKind.Sequential)]
struct RECT
{
public int Left, Top, Right, Bottom;
}
[StructLayout(LayoutKind.Sequential)]
struct POINT
{
public int x, y;
}
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
const uint SW_RESTORE = 9;
[StructLayout(LayoutKind.Sequential)]
struct WINDOWPLACEMENT
{
public uint Length;
public uint Flags;
public uint ShowCmd;
public POINT MinPosition;
public POINT MaxPosition;
public RECT NormalPosition;
public static WINDOWPLACEMENT Default
{
get
{
var instance = new WINDOWPLACEMENT();
instance.Length = (uint)Marshal.SizeOf(instance);
return instance;
}
}
}
internal enum AnchorWindow
{
None = 0x0,
Top = 0x1,
Bottom = 0x2,
Left = 0x4,
Right = 0x8,
Center = 0x10,
Fill = 0x20
}
internal static void SetConsoleWindowPosition(AnchorWindow position)
{
// Get this console window's hWnd (window handle).
IntPtr hWnd = GetConsoleWindow();
// Get information about the monitor (display) that the window is (mostly) displayed on.
// The .rcWork field contains the monitor's work area, i.e., the usable space excluding
// the taskbar (and "application desktop toolbars" - see https://msdn.microsoft.com/en-us/library/windows/desktop/ms724947(v=vs.85).aspx)
var mi = MONITORINFO.Default;
GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTOPRIMARY), ref mi);
// Get information about this window's current placement.
var wp = WINDOWPLACEMENT.Default;
GetWindowPlacement(hWnd, ref wp);
// Calculate the window's new position: lower left corner.
// !! Inexplicably, on W10, work-area coordinates (0,0) appear to be (7,7) pixels
// !! away from the true edge of the screen / taskbar.
int fudgeOffset = 7;
int _left = 0, _top = 0;
switch (position)
{
case AnchorWindow.Left|AnchorWindow.Top:
wp.NormalPosition = new RECT()
{
Left = -fudgeOffset,
Top = mi.rcWork.Top,
Right = (wp.NormalPosition.Right - wp.NormalPosition.Left) - fudgeOffset,
Bottom = (wp.NormalPosition.Bottom - wp.NormalPosition.Top)
};
break;
case AnchorWindow.Right| AnchorWindow.Top:
wp.NormalPosition = new RECT()
{
Left = mi.rcWork.Right - wp.NormalPosition.Right + wp.NormalPosition.Left + fudgeOffset,
Top = mi.rcWork.Top,
Right = mi.rcWork.Right + fudgeOffset,
Bottom = (wp.NormalPosition.Bottom - wp.NormalPosition.Top)
};
break;
case AnchorWindow.Left | AnchorWindow.Bottom:
wp.NormalPosition = new RECT()
{
Left = -fudgeOffset,
Top = mi.rcWork.Bottom - (wp.NormalPosition.Bottom - wp.NormalPosition.Top),
Right = (wp.NormalPosition.Right - wp.NormalPosition.Left) - fudgeOffset,
Bottom = fudgeOffset + mi.rcWork.Bottom
};
break;
case AnchorWindow.Right | AnchorWindow.Bottom:
wp.NormalPosition = new RECT()
{
Left = mi.rcWork.Right - wp.NormalPosition.Right + wp.NormalPosition.Left + fudgeOffset,
Top = mi.rcWork.Bottom - (wp.NormalPosition.Bottom - wp.NormalPosition.Top),
Right = mi.rcWork.Right + fudgeOffset,
Bottom = fudgeOffset + mi.rcWork.Bottom
};
break;
case AnchorWindow.Center|AnchorWindow.Top:
_left = mi.rcWork.Right / 2 - (wp.NormalPosition.Right - wp.NormalPosition.Left) / 2;
wp.NormalPosition = new RECT()
{
Left = _left,
Top = mi.rcWork.Top,
Right = mi.rcWork.Right + fudgeOffset - _left,
Bottom = (wp.NormalPosition.Bottom - wp.NormalPosition.Top)
};
break;
case AnchorWindow.Center | AnchorWindow.Bottom:
_left = mi.rcWork.Right / 2 - (wp.NormalPosition.Right - wp.NormalPosition.Left) / 2;
wp.NormalPosition = new RECT()
{
Left = _left,
Top = mi.rcWork.Bottom - (wp.NormalPosition.Bottom - wp.NormalPosition.Top),
Right = mi.rcWork.Right + fudgeOffset - _left,
Bottom = fudgeOffset + mi.rcWork.Bottom
};
break;
case AnchorWindow.Center:
_left = mi.rcWork.Right / 2 - (wp.NormalPosition.Right - wp.NormalPosition.Left) / 2;
_top = mi.rcWork.Bottom / 2 - (wp.NormalPosition.Bottom - wp.NormalPosition.Top) / 2;
wp.NormalPosition = new RECT()
{
Left = _left,
Top = _top,
Right = mi.rcWork.Right + fudgeOffset - _left,
Bottom = mi.rcWork.Bottom + fudgeOffset - _top
};
break;
case AnchorWindow.Fill:
wp.NormalPosition = new RECT()
{
Left = -fudgeOffset,
Top = mi.rcWork.Top,
Right = mi.rcWork.Right + fudgeOffset,
Bottom = mi.rcWork.Bottom + fudgeOffset
};
break;
default:
return;
}
// Place the window at the new position.
SetWindowPlacement(hWnd, ref wp);
}
}
You can use it like this.
WindowUtility.SetConsoleWindowPosition(WindowUtility.AnchorWindow.Left | WindowUtility.AnchorWindow.Top);
// or
WindowUtility.SetConsoleWindowPosition(WindowUtility.AnchorWindow.Center);
// or
WindowUtility.SetConsoleWindowPosition(WindowUtility.AnchorWindow.Fill);
I've made a custom TextBox class to have a text box with custom border in my application, based on this other SO post. If I set any of the new custom properties in the form designer, they appear momentarily, until I change the control focus, and when I run the application, the new border settings are not displayed. I did update my form's InitializeComponent method, so that the text box initializes a new BorderedTextBox instead of TextBox. Does anyone know what's wrong here?
public class BorderedTextBox : TextBox
{
private Color _borderColor = Color.Black;
private int _borderWidth = 2;
private int _borderRadius = 5;
public BorderedTextBox() : base()
{
InitializeComponent();
this.Paint += this.BorderedTextBox_Paint;
}
public BorderedTextBox(int width, int radius, Color color) : base()
{
this._borderWidth = Math.Max(1, width);
this._borderColor = color;
this._borderRadius = Math.Max(0, radius);
InitializeComponent();
this.Paint += this.BorderedTextBox_Paint;
}
public Color BorderColor
{
get => this._borderColor;
set
{
this._borderColor = value;
DrawTextBox();
}
}
public int BorderWidth
{
get => this._borderWidth;
set
{
if (value > 0)
{
this._borderWidth = Math.Min(value, 10);
DrawTextBox();
}
}
}
public int BorderRadius
{
get => this._borderRadius;
set
{ // Setting a radius of 0 produces square corners...
if (value >= 0)
{
this._borderRadius = value;
this.DrawTextBox();
}
}
}
private void BorderedTextBox_Paint(object sender, PaintEventArgs e) => DrawTextBox(e.Graphics);
private void DrawTextBox() => this.DrawTextBox(this.CreateGraphics());
private void DrawTextBox(Graphics g)
{
Brush borderBrush = new SolidBrush(this.BorderColor);
Pen borderPen = new Pen(borderBrush, (float)this._borderWidth);
Rectangle rect = new Rectangle(
this.ClientRectangle.X,
this.ClientRectangle.Y,
this.ClientRectangle.Width - 1,
this.ClientRectangle.Height - 1);
// Clear text and border
g.Clear(this.BackColor);
// Drawing Border
g.DrawRoundedRectangle(
borderPen,
(0 == this._borderWidth % 2) ? rect.X + this._borderWidth / 2 : rect.X + 1 + this._borderWidth / 2,
rect.Y,
rect.Width - this._borderWidth,
(0 == this._borderWidth % 2) ? rect.Height - this._borderWidth / 2 : rect.Height - 1 - this._borderWidth / 2,
(float)this._borderRadius);
}
#region Component Designer generated code
/// <summary>Required designer variable.</summary>
private System.ComponentModel.IContainer components = null;
/// <summary>Clean up any resources being used.</summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
components.Dispose();
base.Dispose(disposing);
}
/// <summary>Required method for Designer support - Don't modify!</summary>
private void InitializeComponent() => components = new System.ComponentModel.Container();
#endregion
}
You need to override WndProc:
private const int WM_PAINT = 0x000F;
protected override void WndProc( ref Message m ) {
if(m.Msg == WM_PAINT ) {
base.WndProc( ref m );
Graphics gr = this.CreateGraphics();
//draw what you want
gr.Dispose();
return;
}
base.WndProc( ref m );
}
Works fine without any issues. It draws in client area though. A complete version drawing a custom border, textbox need to have border:
[DllImport( "user32.dll" )]
static extern IntPtr GetWindowDC( IntPtr hWnd );
[DllImport( "user32.dll" )]
static extern bool ReleaseDC( IntPtr hWnd, IntPtr hDC );
[DllImport( "gdi32.dll" )]
static extern bool FillRgn( IntPtr hdc, IntPtr hrgn, IntPtr hbr );
[DllImport( "gdi32.dll" )]
static extern IntPtr CreateRectRgn( int nLeftRect, int nTopRect, int nRightRect,
int nBottomRect );
[DllImport( "gdi32.dll" )]
static extern IntPtr CreateSolidBrush( uint crColor );
[DllImport( "gdi32.dll" )]
static extern bool DeleteObject( IntPtr hObject );
private const int WM_NCPAINT = 0x0085;
private const int WM_PAINT = 0x000F;
private const int RGN_DIFF = 0x4;
private int p_border = 2;
protected override void WndProc( ref Message m ) {
if(m.Msg == WM_PAINT ) {
base.WndProc( ref m );
IntPtr hdc = GetWindowDC( this.Handle ); //gr.GetHdc();
IntPtr rgn = CreateRectRgn( 0, 0, this.Width, this.Height );
IntPtr brush = CreateSolidBrush( 0xFF0000 ); //Blue : B G R
CombineRgn( rgn, rgn, CreateRectRgn( p_border, p_border, this.Width - p_border,
this.Height - p_border ), RGN_DIFF );
FillRgn( hdc, rgn, brush );
ReleaseDC( this.Handle, hdc );
DeleteObject( rgn );
DeleteObject( brush );
m.Result = IntPtr.Zero;
return;
}
if( m.Msg == WM_NCPAINT ) {
return;
}
base.WndProc( ref m );
}
Winforms TextBox is a legacy control I think even from way back before the .Net framework.
It doesn't support owner-drawing and as one can see on MSDN neither Paint not OnPaint are documented to work.
Yes, you can code them, and yes, they will have some effect. But TextBox doesn't play by the normal rules and will mess up your drawing without triggering a paint event.
Possibly you can hook yourself into the windows message queue (WndProc) but it is generally not recommended, especially for something like adorning it with a border.
Usually nesting a TextBox in a Panel and letting the Panel draw a nice Border is the simplest solution..
I have a high dpi setting on my monitor as the monitor is a fairly small 3840 x 2160 monitor.
This is causing issues with one of the applications I am writing as I am hosting a control in my main application. I developed it based off a nice example in the WPF-Samples.
On my screen, when the example is run in it's default state, the output looks like
I was able to account for the list control being the incorrect size by using
//MainWindow
PresentationSource source = PresentationSource.FromVisual(this); //Account for any scaling on the screen7
_listControl = new ControlHost(ControlHostElement.ActualWidth, ControlHostElement.ActualHeight, source.CompositionTarget.TransformToDevice.M11, source.CompositionTarget.TransformToDevice.M22);
...
//ControlHost
public ControlHost(double height, double width, double dpXScale, double dpYScale)
{
_hostHeight = (int) (height * dpXScale);
_hostWidth = (int) (width * dpYScale);
}
However, even when it is the correct size, the hosted UI is not scaled as the rest of the program is.
How could the program scale the hosted UI based on the DPI of the user's screen?
There are three main files that make up the this example.
ControlHost.cs
// // Copyright (c) Microsoft. All rights reserved.
// // Licensed under the MIT license. See LICENSE file in the project root for full license information.
#region Using directives
using System;
using System.Runtime.InteropServices;
using System.Windows.Interop;
#endregion
namespace WPFHostingWin32Control
{
public class ControlHost : HwndHost
{
internal const int
WsChild = 0x40000000,
WsVisible = 0x10000000,
LbsNotify = 0x00000001,
HostId = 0x00000002,
ListboxId = 0x00000001,
WsVscroll = 0x00200000,
WsBorder = 0x00800000;
private readonly int _hostHeight;
private readonly int _hostWidth;
private IntPtr _hwndHost;
public ControlHost(double height, double width, double dpXScale, double dpYScale)
{
_hostHeight = (int) (height * dpXScale);
_hostWidth = (int) (width * dpYScale);
}
public IntPtr HwndListBox { get; private set; }
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
HwndListBox = IntPtr.Zero;
_hwndHost = IntPtr.Zero;
_hwndHost = CreateWindowEx(0, "static", "",
WsChild | WsVisible,
0, 0,
_hostHeight, _hostWidth,
hwndParent.Handle,
(IntPtr) HostId,
IntPtr.Zero,
0);
HwndListBox = CreateWindowEx(0, "listbox", "",
WsChild | WsVisible | LbsNotify
| WsVscroll | WsBorder,
0, 0,
_hostHeight, _hostWidth,
_hwndHost,
(IntPtr) ListboxId,
IntPtr.Zero,
0);
return new HandleRef(this, _hwndHost);
}
protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
handled = false;
return IntPtr.Zero;
}
protected override void DestroyWindowCore(HandleRef hwnd)
{
DestroyWindow(hwnd.Handle);
}
//PInvoke declarations
[DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateWindowEx(int dwExStyle,
string lpszClassName,
string lpszWindowName,
int style,
int x, int y,
int width, int height,
IntPtr hwndParent,
IntPtr hMenu,
IntPtr hInst,
[MarshalAs(UnmanagedType.AsAny)] object pvParam);
[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
internal static extern bool DestroyWindow(IntPtr hwnd);
}
}
MainWindow.xaml
<Window x:Class="WPFHostingWin32Control.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFHostingWin32Control"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Loaded="On_UIReady">
<DockPanel Background="LightGreen">
<Border Name="ControlHostElement"
Width="200"
Height="200"
HorizontalAlignment="Right"
VerticalAlignment="Top"
BorderBrush="LightGray"
BorderThickness="3"
DockPanel.Dock="Right"/>
<StackPanel>
<Label HorizontalAlignment="Center"
Margin="0,10,0,0"
FontSize="14"
FontWeight="Bold">Control the Control</Label>
<TextBlock Margin="10,10,10,10" >Selected Text: <TextBlock Name="selectedText"/></TextBlock>
<TextBlock Margin="10,10,10,10" >Number of Items: <TextBlock Name="numItems"/></TextBlock>
<Line X1="0" X2="200"
Stroke="LightYellow"
StrokeThickness="2"
HorizontalAlignment="Center"
Margin="0,20,0,0"/>
<Label HorizontalAlignment="Center"
Margin="10,10,10,10">Append an Item to the List</Label>
<StackPanel Orientation="Horizontal">
<Label HorizontalAlignment="Left"
Margin="10,10,10,10">Item Text</Label>
<TextBox HorizontalAlignment="Left"
Name="txtAppend"
Width="200"
Margin="10,10,10,10" />
</StackPanel>
<Button HorizontalAlignment="Left"
Click="AppendText"
Width="75"
Margin="10,10,10,10">Append</Button>
<Line X1="0" X2="200"
Stroke="LightYellow"
StrokeThickness="2"
HorizontalAlignment="Center"
Margin="0,20,0,0"/>
<Label HorizontalAlignment="Center"
Margin="10,10,10,10">Delete the Selected Item</Label>
<Button Click="DeleteText"
Width="125"
Margin="10,10,10,10"
HorizontalAlignment="Left">Delete</Button>
</StackPanel>
</DockPanel>
</Window>
MainWindow.cs
// // Copyright (c) Microsoft. All rights reserved.
// // Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
namespace WPFHostingWin32Control
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
internal const int
LbnSelchange = 0x00000001,
WmCommand = 0x00000111,
LbGetcursel = 0x00000188,
LbGettextlen = 0x0000018A,
LbAddstring = 0x00000180,
LbGettext = 0x00000189,
LbDeletestring = 0x00000182,
LbGetcount = 0x0000018B;
private Application _app;
private IntPtr _hwndListBox;
private int _itemCount;
private ControlHost _listControl;
private Window _myWindow;
private int _selectedItem;
public MainWindow()
{
InitializeComponent();
}
private void On_UIReady(object sender, EventArgs e)
{
_app = Application.Current;
_myWindow = _app.MainWindow;
_myWindow.SizeToContent = SizeToContent.WidthAndHeight;
PresentationSource source = PresentationSource.FromVisual(this); //Account for any scaling on the screen7
_listControl = new ControlHost(ControlHostElement.ActualWidth, ControlHostElement.ActualHeight, source.CompositionTarget.TransformToDevice.M11, source.CompositionTarget.TransformToDevice.M22);
ControlHostElement.Child = _listControl;
_listControl.MessageHook += ControlMsgFilter;
_hwndListBox = _listControl.HwndListBox;
for (var i = 1; i <= 100; i++) //populate listbox
{
var itemText = "Item" + i;
SendMessage(_hwndListBox, LbAddstring, IntPtr.Zero, itemText);
}
_itemCount = SendMessage(_hwndListBox, LbGetcount, IntPtr.Zero, IntPtr.Zero);
numItems.Text = "" + _itemCount;
}
private void AppendText(object sender, EventArgs args)
{
if (txtAppend.Text != string.Empty)
{
SendMessage(_hwndListBox, LbAddstring, IntPtr.Zero, txtAppend.Text);
}
_itemCount = SendMessage(_hwndListBox, LbGetcount, IntPtr.Zero, IntPtr.Zero);
numItems.Text = "" + _itemCount;
}
private void DeleteText(object sender, EventArgs args)
{
_selectedItem = SendMessage(_listControl.HwndListBox, LbGetcursel, IntPtr.Zero, IntPtr.Zero);
if (_selectedItem != -1) //check for selected item
{
SendMessage(_hwndListBox, LbDeletestring, (IntPtr) _selectedItem, IntPtr.Zero);
}
_itemCount = SendMessage(_hwndListBox, LbGetcount, IntPtr.Zero, IntPtr.Zero);
numItems.Text = "" + _itemCount;
}
private IntPtr ControlMsgFilter(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
int textLength;
handled = false;
if (msg == WmCommand)
{
switch ((uint) wParam.ToInt32() >> 16 & 0xFFFF) //extract the HIWORD
{
case LbnSelchange: //Get the item text and display it
_selectedItem = SendMessage(_listControl.HwndListBox, LbGetcursel, IntPtr.Zero, IntPtr.Zero);
textLength = SendMessage(_listControl.HwndListBox, LbGettextlen, IntPtr.Zero, IntPtr.Zero);
var itemText = new StringBuilder();
SendMessage(_hwndListBox, LbGettext, _selectedItem, itemText);
selectedText.Text = itemText.ToString();
handled = true;
break;
}
}
return IntPtr.Zero;
}
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern int SendMessage(IntPtr hwnd,
int msg,
IntPtr wParam,
IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern int SendMessage(IntPtr hwnd,
int msg,
int wParam,
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder lParam);
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern IntPtr SendMessage(IntPtr hwnd,
int msg,
IntPtr wParam,
string lParam);
}
}
Winforms does not handle high-DPI settings very well by default. As stated in the comments, you can try some of the available manifest settings to get it to resize. If that won't work for your situation then you will need to scale the control yourself. Most Winforms controls have a Scale method you could call when you adjust the height and width:
_listControl.Scale(dpXScale, dpYScale);
of course, since your actual code uses a custom control your mileage may vary.
Mouse stimulation using SendInput works perfectly on MainDisplay. However when I use SendInput for extended screen (e.g. Second screen placed to the left of the main display in my case. Issues is replicable irrespective of the extended display any place around main display but with different resolution then main display):
If I use SendInput on extended screen, the mouse position has offset in both X and Y position, ever so slightly ranging from 40 to 80 points in x and 10 to 20 points in Y based on if X (width) and Y(height) of extended screen is different to main display width/height)
Thanks in advance for any support as to why difference on extended screen
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetCursorPos(ref Win32Point pt);
[DllImport("user32.dll")]
internal static extern bool SetCursorPos(int X, int Y);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
internal enum SendInputEventType : int
{
InputMouse,
InputKeyboard
}
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint nInputs, ref Input pInputs, int cbSize);
public struct Input
{
public uint InputType;
public MouseInput MI;
}
public struct MouseInput
{
public int Dx;
public int Dy;
public uint MouseData;
public uint DwFlags;
public uint Time;
public IntPtr DwExtraInfo;
}
public enum MouseEventInfo
{
mouseEventfMove = 0x0001,
mouseEventfLeftdown = 0x0002,
mouseEventfLeftup = 0x0004,
mouseEventfRightdown = 0x0008,
mouseEventfRightup = 0x0010,
mouseEventfWheel = 0x0800,
mouseEventfAbsolute = 0x8000,
wheelDelta = 0x0078
}
static int CalculateAbsoluteCoordinateX(int x, System.Drawing.Rectangle currentBounds)
{
return ((currentBounds.X + x) * 65536) / (currentBounds.Width);
}
static int CalculateAbsoluteCoordinateY(int y, System.Drawing.Rectangle currentBounds)
{
return (((currentBounds.Y + y) * 65536) / currentBounds.Height);
}
// for me screen at index 0 (screen no 1) is main display. Screen id 2
//placed to the left of the main display as per resolution screen i.e.at
//index 1 (Screen.AllScreens[1]) is extended display and Bound.X is a -ve value
public static int ScreenId = 2;
public static System.Drawing.Rectangle CurrentBounds
{
get
{
return SysForms.Screen.AllScreens[ScreenId - 1].Bounds;
}
}
public static void ClickLeftMouseButton(int x, int y)
{
Input mouseInput = new Input();
mouseInput.InputType = SendInputEventType.InputMouse;
mouseInput.MI.Dx = CalculateAbsoluteCoordinateX(x, CurrentBounds);
mouseInput.MI.Dy = CalculateAbsoluteCoordinateY(y, CurrentBounds);
mouseInput.MI.MouseData = 0;
mouseInput.MI.DwFlags = MouseEventInfo.mouseEventfMove | MouseEventInfo.mouseEventfAbsolute;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
mouseInput.MI.DwFlags = MouseEventInfo.mouseEventfLeftdown;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
mouseInput.MI.DwFlags = MouseEventFlags.mouseEventfLeftup;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
}
//Below is code of the WPF MainWindow for testing. Two buttons with click event.
// For main display with screenid as 1 both setcursor position and sendinput
//work perfectly, as I get the MousePosition, but when I apply this to
//extended screen (currently with two screen, main display is screen 1 in my
//case and screen 2 is extended screen, they put the mouse at two different positions.
//I have my doubts the way I am using the extended screen Bounds.X, but
//haven't will able to fix the issue
int x = 600;
int y = 300;
private void btnSend_Click(object sender, RoutedEventArgs e)
{
SetCursorPos(SysForms.Screen.AllScreens[ScreenId - 1].Bounds.X + x, SysForms.Screen.AllScreens[screenId - 1].Bounds.Y + y);
}
private void btnSend1_Click(object sender, RoutedEventArgs e)
{
ClickLeftMouseButton(x, y);
}
Found the issue. While using SendInput, the conversion of x,y in absolute value must be done in relation to Main/Primary screen.
Thus the changes:
static int CalculateAbsoluteCoordinateX(int x, System.Drawing.Rectangle currentBounds)
{
return ((currentBounds.X + x) * 65536) / (SystemParameters.PrimaryScreenWidth);
}
static int CalculateAbsoluteCoordinateY(int y, System.Drawing.Rectangle currentBounds)
{
return (((currentBounds.Y + y) * 65536) / SystemParameters.PrimaryScreenHeight);
}
Firstly here is the XAML code of the window itself:
<!-- Window Main -->
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Name="Window_Main" x:Class="WPF_Test.General_Window"
Title="General_Window" Height="500" Width="1000" BorderThickness="30" ResizeMode="CanResizeWithGrip" AllowsTransparency="True" WindowStyle="None" StateChanged="Window_Main_StateChanged">
<!-- Effects -->
<Window.Effect>
<DropShadowEffect x:Name="Border_Effect" ShadowDepth="0" BlurRadius="30" Direction="0" Opacity="0.5"/>
</Window.Effect>
<!-- Border Main -->
<Border x:Name="Border_Main" BorderBrush="Gray" BorderThickness="1">
<!-- Panel Main -->
<DockPanel x:Name="Panel_Main">
<DockPanel x:Name="Panel_Title" Height="25" LastChildFill="False" Background="#FFEEEEEE" MouseDown="Panel_Title_MouseDown" VerticalAlignment="Top" DockPanel.Dock="Top">
</DockPanel>
<DockPanel x:Name="Panel_Secondary" LastChildFill="true" Background="#FFEEEEEE"/>
</DockPanel>
</Border>
Here is the C# code of the constructor for that window, where I clearly define the minimum sizes of the window, But they are still ignored when re-sizing:
public void Window_Init()
{
Window_Main.MinHeight = Window_Minimum_Height + Window_Thickness * 2;
Window_Main.MinWidth = Window_Minimum_Width + Window_Thickness * 2;
Window_Main.BorderThickness = new Thickness( Window_Thickness );
}
So when I'm re-sizing the window using the ResizeGrip (provided by the CanResizeWithGrip property) the window itself will to stop to re-size at the moment it will reach the minimum width and height. While the rendering area which contains the window entirely still can be re-sized to even smaller dimensions, so it looks like a cut-off image of a window on the desktop.
I've resolved the bug.
You can use the grip resizer or create your own resizing controls , Using OnMouseDown event to send a message to HWND to resize the window while it doesn't pass the minimum sizes you have previously defined for the window.
This code will limit the window size when maximizing as well to ensure that the window will never be outside of the screen or rendered over the task-bar.
This supposed to be at the bottom of your MainWindow.cs code:
private const int WM_SYSCOMMAND = 0X112;
private HwndSource hwndSource;
enum SWP : uint
{
NOSIZE = 0x0001 ,
NOMOVE = 0x0002 ,
NOZORDER = 0x0004 ,
NOREDRAW = 0x0008 ,
NOACTIVATE = 0x0010 ,
FRAMECHANGED = 0x0020 ,
SHOWWINDOW = 0x0040 ,
HIDEWINDOW = 0x0080 ,
NOCOPYBITS = 0x0100 ,
NOOWNERZORDER = 0x0200 ,
NOSENDCHANGING = 0x0400 ,
}
public override void OnApplyTemplate()
{
System.IntPtr handle = ( new WindowInteropHelper( this ) ).Handle;
HwndSource.FromHwnd( handle ).AddHook( new HwndSourceHook( WindowProc ) );
}
private System.IntPtr WindowProc( System.IntPtr hwnd , int msg , System.IntPtr wParam , System.IntPtr lParam , ref bool handled )
{
switch ( msg )
{
case 0x0024:
{
WmGetMinMaxInfo( hwnd , lParam );
handled = true;
break;
}
case 0x0046:
{
WINDOWPOS pos = ( WINDOWPOS )Marshal.PtrToStructure( lParam , typeof( WINDOWPOS ) );
if ( ( pos.flags & ( int )(SWP.NOMOVE) ) != 0 )
{
return IntPtr.Zero;
}
Window wnd = ( Window )HwndSource.FromHwnd( hwnd ).RootVisual;
if ( wnd == null )
{
return IntPtr.Zero;
}
bool changedPos = false;
if ( pos.cx < MinWidth ) { pos.cx = (int)MinWidth; changedPos = true; }
if ( pos.cy < MinHeight ) { pos.cy = ( int )MinHeight; changedPos = true; }
if ( !changedPos )
{
return IntPtr.Zero;
}
Marshal.StructureToPtr( pos , lParam , true );
handled = true;
break;
}
}
return ( System.IntPtr )0;
}
private void WmGetMinMaxInfo( System.IntPtr hwnd , System.IntPtr lParam )
{
MINMAXINFO mmi = ( MINMAXINFO )Marshal.PtrToStructure( lParam , typeof( MINMAXINFO ) );
int MONITOR_DEFAULTTONEAREST = 0x00000002;
System.IntPtr monitor = MonitorFromWindow( hwnd , MONITOR_DEFAULTTONEAREST );
if ( monitor != System.IntPtr.Zero )
{
MONITORINFO monitorInfo = new MONITORINFO();
GetMonitorInfo( monitor , monitorInfo );
RECT rcWorkArea = monitorInfo.rcWork;
RECT rcMonitorArea = monitorInfo.rcMonitor;
mmi.ptMaxPosition.x = Math.Abs( rcWorkArea.left - rcMonitorArea.left );
mmi.ptMaxPosition.y = Math.Abs( rcWorkArea.top - rcMonitorArea.top );
mmi.ptMaxSize.x = Math.Abs( rcWorkArea.right - rcWorkArea.left );
mmi.ptMaxSize.y = Math.Abs( rcWorkArea.bottom - rcWorkArea.top );
}
Marshal.StructureToPtr( mmi , lParam , true );
}
[StructLayout( LayoutKind.Sequential )]
public struct POINT
{
public int x;
public int y;
public POINT( int x , int y )
{
this.x = x;
this.y = y;
}
}
[StructLayout( LayoutKind.Sequential )]
public struct MINMAXINFO
{
public POINT ptReserved;
public POINT ptMaxSize;
public POINT ptMaxPosition;
public POINT ptMinTrackSize;
public POINT ptMaxTrackSize;
};
[StructLayout( LayoutKind.Sequential , CharSet = CharSet.Auto )]
public class MONITORINFO
{
public int cbSize = Marshal.SizeOf( typeof( MONITORINFO ) );
public RECT rcMonitor = new RECT();
public RECT rcWork = new RECT();
public int dwFlags = 0;
}
[StructLayout( LayoutKind.Sequential , Pack = 0 )]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public static readonly RECT Empty = new RECT();
public int Width
{
get { return Math.Abs( right - left ); }
}
public int Height
{
get { return bottom - top; }
}
public RECT( int left , int top , int right , int bottom )
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public RECT( RECT rcSrc )
{
this.left = rcSrc.left;
this.top = rcSrc.top;
this.right = rcSrc.right;
this.bottom = rcSrc.bottom;
}
public bool IsEmpty
{
get
{
return left >= right || top >= bottom;
}
}
public override string ToString()
{
if ( this == RECT.Empty ) { return "RECT {Empty}"; }
return "RECT { left : " + left + " / top : " + top + " / right : " + right + " / bottom : " + bottom + " }";
}
public override bool Equals( object obj )
{
if ( !( obj is Rect ) ) { return false; }
return ( this == ( RECT )obj );
}
public override int GetHashCode()
{
return left.GetHashCode() + top.GetHashCode() + right.GetHashCode() + bottom.GetHashCode();
}
public static bool operator ==( RECT rect1 , RECT rect2 )
{
return ( rect1.left == rect2.left && rect1.top == rect2.top && rect1.right == rect2.right && rect1.bottom == rect2.bottom );
}
public static bool operator !=( RECT rect1 , RECT rect2 )
{
return !( rect1 == rect2 );
}
}
[DllImport( "user32" )]
internal static extern bool GetMonitorInfo( IntPtr hMonitor , MONITORINFO lpmi );
[DllImport( "User32" )]
internal static extern IntPtr MonitorFromWindow( IntPtr handle , int flags );
[StructLayout( LayoutKind.Sequential )]
internal struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public int flags;
}
private void InitializeWindowSource( object sender , EventArgs e )
{
hwndSource = PresentationSource.FromVisual( ( Visual )sender ) as HwndSource;
}
public enum ResizeDirection
{
Left = 1 ,
Right = 2 ,
Top = 3 ,
TopLeft = 4 ,
TopRight = 5 ,
Bottom = 6 ,
BottomLeft = 7 ,
BottomRight = 8 ,
}
[DllImport( "user32" , CharSet = CharSet.Auto )]
private static extern IntPtr SendMessage( IntPtr hWnd , uint Msg , IntPtr wParam , IntPtr lParam );
private void ResizeWindow( ResizeDirection direction )
{
SendMessage( hwndSource.Handle , WM_SYSCOMMAND , ( IntPtr )( 61440 + direction ) , IntPtr.Zero );
}
And do not forget to add this line to your window's constructor, otherwise nothing shall work:
SourceInitialized += new EventHandler( InitializeWindowSource );