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 have a wpf application and I manage max sizing with WmGetMinMaxInfo:
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;
mmi.ptMaxTrackSize.y = mmi.ptMaxSize.y;
}
Marshal.StructureToPtr(mmi, lParam, true);
}
My window are set to :
WindowStyle="None"
ResizeMode="CanResizeWithGrip"
AllowsTransparency="True"
I have an wierd issue when I minimized the app at maximized state and when click for return at the taskbar icon to come back on the maximized window app.
It don't take the full screen, It's seems to take the primary screen resolution.
I got that case when I don't have the same resolution between primary and secondary screen (Obviously my secondary are bigger than the first one).
Do you have any ideas where is the issue ? Windows features ?
There is any work around ?
SOLUTION:
I have solved my issue with the idea of Keithernet to not recalculate when the windowState are minimized. Thank !
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;
if (Application.Current.MainWindow.WindowState != WindowState.Minimized)
{
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;
mmi.ptMaxTrackSize.y = mmi.ptMaxSize.y;
lastGoodMaxTrackSize = mmi.ptMaxTrackSize;
lastGoodMaxPosition = mmi.ptMaxPosition;
lastGoodMaxSize = mmi.ptMaxSize;
}
else
{
mmi.ptMaxPosition = lastGoodMaxPosition;
mmi.ptMaxSize = lastGoodMaxSize;
mmi.ptMaxTrackSize = lastGoodMaxTrackSize;
}
mmi = AdjustWorkingAreaForAutoHide(monitorContainingApplication, mmi);
}
Marshal.StructureToPtr(mmi, lParam, true);
}
I have a custom window control that I wrote many years ago. It is still being used actively. Windows are handled properly no matter which display they are on and what the individual DPI is.
Here is my implementation of WmGetMinMaxInfo.
I hope this is helpful.
private void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
{
var mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
var hMonitor = Win32.MonitorFromWindow(hwnd, 1);
if (hMonitor != IntPtr.Zero)
{
var monitorInfo = new Win32.MONITORINFO();
Win32.GetMonitorInfo(hMonitor, monitorInfo);
var rcWorkArea = monitorInfo.rcWork;
var 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.ptMinTrackSize.x = (int)(minWidth * hidpiX);
mmi.ptMinTrackSize.y = (int)(minHeight * hidpiY);
var window = (Window)HwndSource.FromHwnd(hwnd)?.RootVisual;
if (window != null)
{
if (window.WindowState != WindowState.Minimized)
{
mmi.ptMaxTrackSize.x = Win32.GetSystemMetrics(Win32.SystemMetric.SM_CXVIRTUALSCREEN);
mmi.ptMaxTrackSize.y = Win32.GetSystemMetrics(Win32.SystemMetric.SM_CYVIRTUALSCREEN);
lastGoodMaxTrackSize = mmi.ptMaxTrackSize;
}
else if (lastGoodMaxTrackSize.x > 0 && lastGoodMaxTrackSize.y > 0)
{
mmi.ptMaxTrackSize = lastGoodMaxTrackSize;
}
}
mmi = AdjustWorkingAreaForAutoHide(hMonitor, mmi);
}
Marshal.StructureToPtr(mmi, lParam, true);
}
For hidpiX and hidpiY, I get those in a handler for the OnSourceInitialized event.
private void OnSourceInitialized(object sender, EventArgs e)
{
...
var source = PresentationSource.FromVisual((Visual)sender);
if (source == null) return;
// Get the X and Y HiDPI factors from the media matrix
if (source.CompositionTarget != null)
{
hidpiX = source.CompositionTarget.TransformToDevice.M11;
hidpiY = source.CompositionTarget.TransformToDevice.M22;
}
...
}
I want to create a button and a container with rounded corners.
I'm using the Region to paint the corners, code attached below.
However the corners doesn't seem smooth, is there any way to fix this, any help would be appreciated.
Image attached below as im not allowed to upload images yet.
[DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
private static extern IntPtr CreateRoundRectRgn(
int nLeftRect,
int nTopRect,
int nRightRect,
int nBottomRect,
int nWidthEllipse,
int nHeightEllipse
);
public Login()
{
InitializeComponent();
this.Region = System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, Width, Height, 30, 30));
this.logo.Image = Properties.Resources.logo;
this.btn_login.Region = System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, this.btn_login.Width, this.btn_login.Height, 10, 10));
}
Once the function is not implemented using the normal WinForm function.
Therefore we must implement it using win32Api.
The code is created referring to this and this.
First, you have to draw a round rectangle.
public static GraphicsPath RoundedRect(Rectangle bounds, int radius)
{
int diameter = radius * 2;
Size size = new Size(diameter, diameter);
Rectangle arc = new Rectangle(bounds.Location, size);
GraphicsPath path = new GraphicsPath();
if (radius == 0)
{
path.AddRectangle(bounds);
return path;
}
// top left arc
path.AddArc(arc, 180, 90);
// top right arc
arc.X = bounds.Right - diameter;
path.AddArc(arc, 270, 90);
// bottom right arc
arc.Y = bounds.Bottom - diameter;
path.AddArc(arc, 0, 90);
// bottom left arc
arc.X = bounds.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
public static void FillRoundedRectangle(Graphics graphics, Brush brush, Rectangle bounds, int cornerRadius)
{
if (graphics == null)
throw new ArgumentNullException("graphics");
if (brush == null)
throw new ArgumentNullException("brush");
using (GraphicsPath path = RoundedRect(bounds, cornerRadius))
{
graphics.FillPath(brush, path);
}
}
And let's add it to the drawing call.
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics graphics = e.Graphics;
Rectangle gradientRectangle = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
Brush b = new LinearGradientBrush(gradientRectangle, Color.DarkSlateBlue, Color.MediumPurple, 0.0f);
graphics.SmoothingMode = SmoothingMode.HighQuality;
FillRoundedRectangle(graphics, b, gradientRectangle, 35);
}
Then we can draw the same form as the picture above.
Second, draw a form using Per Pixel Alpha Blend.
public void SetBitmap(Bitmap bitmap)
{
SetBitmap(bitmap, 255);
}
public void SetBitmap(Bitmap bitmap, byte opacity)
{
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new ApplicationException("The bitmap must be 32ppp with alpha-channel.");
IntPtr screenDc = Win32.GetDC(IntPtr.Zero);
IntPtr memDc = Win32.CreateCompatibleDC(screenDc);
IntPtr hBitmap = IntPtr.Zero;
IntPtr oldBitmap = IntPtr.Zero;
try
{
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
oldBitmap = Win32.SelectObject(memDc, hBitmap);
Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height);
Win32.Point pointSource = new Win32.Point(0, 0);
Win32.Point topPos = new Win32.Point(Left, Top);
Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION();
blend.BlendOp = Win32.AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = opacity;
blend.AlphaFormat = Win32.AC_SRC_ALPHA;
Win32.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA);
}
finally
{
Win32.ReleaseDC(IntPtr.Zero, screenDc);
if (hBitmap != IntPtr.Zero)
{
Win32.SelectObject(memDc, oldBitmap);
Win32.DeleteObject(hBitmap);
}
Win32.DeleteDC(memDc);
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00080000;
return cp;
}
}
Finally, call SetBitmap when loading a form.
private void Form1_Load(object sender, EventArgs e)
{
Bitmap myBitmap = new Bitmap(this.Width, this.Height);
Graphics graphics = Graphics.FromImage(myBitmap);
Rectangle gradientRectangle = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
Brush b = new LinearGradientBrush(gradientRectangle, Color.DarkSlateBlue, Color.MediumPurple, 0.0f);
graphics.SmoothingMode = SmoothingMode.HighQuality;
FillRoundedRectangle(graphics, b, gradientRectangle, 35);
SetBitmap(myBitmap);
}
When you finish the above tasks, you can finally get Smooth Round Corners in WinForm Applications.
Full code of Form
public class RoundedForm : Form
{
private Timer drawTimer = new Timer();
public NanoRoundedForm()
{
this.FormBorderStyle = FormBorderStyle.None;
}
protected override void OnLoad(EventArgs e)
{
if (!DesignMode)
{
drawTimer.Interval = 1000 / 60;
drawTimer.Tick += DrawForm;
drawTimer.Start();
}
base.OnLoad(e);
}
private void DrawForm(object pSender, EventArgs pE)
{
using (Bitmap backImage = new Bitmap(this.Width, this.Height))
{
using (Graphics graphics = Graphics.FromImage(backImage))
{
Rectangle gradientRectangle = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
using (Brush b = new LinearGradientBrush(gradientRectangle, Color.DarkSlateBlue, Color.MediumPurple, 0.0f))
{
graphics.SmoothingMode = SmoothingMode.HighQuality;
RoundedRectangle.FillRoundedRectangle(graphics, b, gradientRectangle, 35);
foreach (Control ctrl in this.Controls)
{
using (Bitmap bmp = new Bitmap(ctrl.Width, ctrl.Height))
{
Rectangle rect = new Rectangle(0, 0, ctrl.Width, ctrl.Height);
ctrl.DrawToBitmap(bmp, rect);
graphics.DrawImage(bmp, ctrl.Location);
}
}
PerPixelAlphaBlend.SetBitmap(backImage, Left, Top, Handle);
}
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
if (DesignMode)
{
Graphics graphics = e.Graphics;
Rectangle gradientRectangle = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
Brush b = new LinearGradientBrush(gradientRectangle, Color.DarkSlateBlue, Color.MediumPurple, 0.0f);
graphics.SmoothingMode = SmoothingMode.HighQuality;
RoundedRectangle.FillRoundedRectangle(graphics, b, gradientRectangle, 35);
}
base.OnPaint(e);
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
if (!DesignMode)
{
cp.ExStyle |= 0x00080000;
}
return cp;
}
}
}
public static class RoundedRectangle
{
public static GraphicsPath RoundedRect(Rectangle bounds, int radius)
{
int diameter = radius * 2;
Size size = new Size(diameter, diameter);
Rectangle arc = new Rectangle(bounds.Location, size);
GraphicsPath path = new GraphicsPath();
if (radius == 0)
{
path.AddRectangle(bounds);
return path;
}
// top left arc
path.AddArc(arc, 180, 90);
// top right arc
arc.X = bounds.Right - diameter;
path.AddArc(arc, 270, 90);
// bottom right arc
arc.Y = bounds.Bottom - diameter;
path.AddArc(arc, 0, 90);
// bottom left arc
arc.X = bounds.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
public static void FillRoundedRectangle(Graphics graphics, Brush brush, Rectangle bounds, int cornerRadius)
{
if (graphics == null)
throw new ArgumentNullException("graphics");
if (brush == null)
throw new ArgumentNullException("brush");
using (GraphicsPath path = RoundedRect(bounds, cornerRadius))
{
graphics.FillPath(brush, path);
}
}
}
internal static class PerPixelAlphaBlend
{
public static void SetBitmap(Bitmap bitmap, int left, int top, IntPtr handle)
{
SetBitmap(bitmap, 255, left, top, handle);
}
public static void SetBitmap(Bitmap bitmap, byte opacity, int left, int top, IntPtr handle)
{
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new ApplicationException("The bitmap must be 32ppp with alpha-channel.");
IntPtr screenDc = Win32.GetDC(IntPtr.Zero);
IntPtr memDc = Win32.CreateCompatibleDC(screenDc);
IntPtr hBitmap = IntPtr.Zero;
IntPtr oldBitmap = IntPtr.Zero;
try
{
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
oldBitmap = Win32.SelectObject(memDc, hBitmap);
Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height);
Win32.Point pointSource = new Win32.Point(0, 0);
Win32.Point topPos = new Win32.Point(left, top);
Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION();
blend.BlendOp = Win32.AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = opacity;
blend.AlphaFormat = Win32.AC_SRC_ALPHA;
Win32.UpdateLayeredWindow(handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA);
}
finally
{
Win32.ReleaseDC(IntPtr.Zero, screenDc);
if (hBitmap != IntPtr.Zero)
{
Win32.SelectObject(memDc, oldBitmap);
Win32.DeleteObject(hBitmap);
}
Win32.DeleteDC(memDc);
}
}
}
internal class Win32
{
public enum Bool
{
False = 0,
True
};
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public Int32 x;
public Int32 y;
public Point(Int32 x, Int32 y) { this.x = x; this.y = y; }
}
[StructLayout(LayoutKind.Sequential)]
public struct Size
{
public Int32 cx;
public Int32 cy;
public Size(Int32 cx, Int32 cy) { this.cx = cx; this.cy = cy; }
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ARGB
{
public byte Blue;
public byte Green;
public byte Red;
public byte Alpha;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BLENDFUNCTION
{
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}
public const Int32 ULW_COLORKEY = 0x00000001;
public const Int32 ULW_ALPHA = 0x00000002;
public const Int32 ULW_OPAQUE = 0x00000004;
public const byte AC_SRC_OVER = 0x00;
public const byte AC_SRC_ALPHA = 0x01;
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll", ExactSpelling = true)]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool DeleteObject(IntPtr hObject);
}
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.
I have a Windows 8.1 wpf application that can change its mode: kiosk or maintenance. For the kiosk mode I have a code:
window.WindowStyle = WindowStyle.None;
window.WindowState = WindowState.Maximized;
window.Topmost = true;
It work very good, but when I change the mode from the maintenance to the kiosk mode I have a problem: if window was maximized before (window.WindowState == WindowState.Maximized) a taskbar stay visible.
I tried resolve this problem: I calculated monitor width and height using User32.dll:
public MainWindow()
{
InitializeComponent();
this.SourceInitialized += new EventHandler(win_SourceInitialized);
}
void win_SourceInitialized(object sender, EventArgs e)
{
System.IntPtr handle = (new WindowInteropHelper(this)).Handle;
HwndSource.FromHwnd(handle).AddHook(new HwndSourceHook(WindowProc));
}
private static System.IntPtr WindowProc(System.IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case 0x0024:
WmGetMinMaxInfo(hwnd, lParam);
handled = true;
break;
}
return (IntPtr)0;
}
private static void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam)
{
MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
// Adjust the maximized size and position to fit the work area of the correct monitor
int MONITOR_DEFAULTTONEAREST = 0x00000002;
System.IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (monitor != System.IntPtr.Zero)
{
MONITORINFO monitorInfo = new MONITORINFO();
GetMonitorInfo(monitor, monitorInfo);
RECT rcMonitorArea = monitorInfo.rcMonitor;
mmi.ptMaxPosition.x = rcMonitorArea.left;
mmi.ptMaxPosition.y = rcMonitorArea.top;
mmi.ptMaxSize.x = Math.Abs(rcMonitorArea.right - rcMonitorArea.left);
mmi.ptMaxSize.y = Math.Abs(rcMonitorArea.bottom - rcMonitorArea.top);
}
Marshal.StructureToPtr(mmi, lParam, true);
}
[DllImport("user32.dll")]
internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);
[DllImport("user32.dll")]
static extern bool GetCursorPos(ref Point lpPoint);
[DllImport("User32.dll")]
internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);
But it don't work after maximizes window in maitenance mode whatever. I understand that WindowState is WindowState.Maximized already and event of change window size do not happen. But I don't know how I can resolve it.
Of course, I can change the size of my window before maximizing:
window.WindowStyle = WindowStyle.None;
window.WindowState = WindowState.Normal;
window.WindowState = WindowState.Maximized;
window.Topmost = true;
It work, but I see flicker of the window. It's very bad for me.
I hope somebody had this bug and he resolved it happily.