Skip to content

Instantly share code, notes, and snippets.

@C0BR4cH
Created September 29, 2017 11:21
Show Gist options
  • Save C0BR4cH/8b2e6418980308f67ce34d2981b477a8 to your computer and use it in GitHub Desktop.
Save C0BR4cH/8b2e6418980308f67ce34d2981b477a8 to your computer and use it in GitHub Desktop.
C# WPF Borderless Window with custom Titlebar
/// <summary>
/// Source initialization
/// </summary>
/// <param name="sender">sender</param>
/// <param name="e">args</param>
void Window_SourceInitialized(object sender, System.EventArgs e)
{
// Get handler for WPF window and hook into it
IntPtr windowHandle = (new WindowInteropHelper(this)).Handle;
HwndSource.FromHwnd(windowHandle).AddHook(new HwndSourceHook(WindowProc));
}
/// <summary>
/// Used to catch the message for WM_GETMINMAXINFO and handle the correct sizes.
/// </summary>
/// <param name="hwnd">The window handle</param>
/// <param name="msg">The message ID</param>
/// <param name="wParam">The message's wParam value</param>
/// <param name="lParam">The message's lParam value</param>
/// <param name="handled">A value that indicates whether the message was handled. Set the value to <code>true</code> if the message was handled; otherwise, <code>false</code></param>
/// <returns>In this case only used to handle messages. Will only return <code>IntPtr.Zero</code></returns>
private static IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
// Message for WM_GETMINMAXINFO
case 0x0024:
WmGetMinMaxInfo(hwnd, lParam);
break;
}
return IntPtr.Zero;
}
private static void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
{
// Get cursor position
POINT lMousePosition;
GetCursorPos(out lMousePosition);
// Get pointer of primary screen
IntPtr lPrimaryScreen = MonitorFromPoint(new POINT(0, 0), MonitorOptions.MONITOR_DEFAULTTOPRIMARY);
// Check for Monitor availablility
MONITORINFO lPrimaryScreenInfo = new MONITORINFO();
if (!GetMonitorInfo(lPrimaryScreen, lPrimaryScreenInfo))
{
return;
}
// Get pointer of current screen
IntPtr lCurrentScreen = MonitorFromPoint(lMousePosition, MonitorOptions.MONITOR_DEFAULTTONEAREST);
// Get MinMaxInfo
MINMAXINFO lMmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
// Set correct position and sizes of screen / monitor
if (lPrimaryScreen.Equals(lCurrentScreen) == true)
{
lMmi.ptMaxPosition.X = lPrimaryScreenInfo.rcWork.Left;
lMmi.ptMaxPosition.Y = lPrimaryScreenInfo.rcWork.Top;
lMmi.ptMaxSize.X = lPrimaryScreenInfo.rcWork.Right - lPrimaryScreenInfo.rcWork.Left;
lMmi.ptMaxSize.Y = lPrimaryScreenInfo.rcWork.Bottom - lPrimaryScreenInfo.rcWork.Top;
}
else
{
lMmi.ptMaxPosition.X = lPrimaryScreenInfo.rcMonitor.Left;
lMmi.ptMaxPosition.Y = lPrimaryScreenInfo.rcMonitor.Top;
lMmi.ptMaxSize.X = lPrimaryScreenInfo.rcMonitor.Right - lPrimaryScreenInfo.rcMonitor.Left;
lMmi.ptMaxSize.Y = lPrimaryScreenInfo.rcMonitor.Bottom - lPrimaryScreenInfo.rcMonitor.Top;
}
// Set the values to memory
Marshal.StructureToPtr(lMmi, lParam, true);
}
/// <summary>
/// Switches between window stats Maximized and Normal
/// </summary>
private void SwitchWindowState()
{
switch (WindowState)
{
case WindowState.Normal:
{
WindowState = WindowState.Maximized;
break;
}
case WindowState.Maximized:
{
WindowState = WindowState.Normal;
break;
}
}
}
/// <summary>
/// Event on left mousebutton down on titlebar
/// </summary>
/// <param name="sender">sender</param>
/// <param name="e">args</param>
private void rctHeader_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// On doubleclick
if (e.ClickCount == 2)
{
// If allowed, switch window state
if ((ResizeMode == ResizeMode.CanResize) || (ResizeMode == ResizeMode.CanResizeWithGrip))
{
SwitchWindowState();
}
return;
}
// Allow restore on move if maximized
else if (WindowState == WindowState.Maximized)
{
doRestoreIfMove = true;
return;
}
// In every other case enable draging the window around
DragMove();
}
/// <summary>
/// Event on left mousebutton up on titlebar
/// </summary>
/// <param name="sender">sender</param>
/// <param name="e">args</param>
private void rctHeader_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
// Disable restore on move
doRestoreIfMove = false;
}
/// <summary>
/// Event on moving the mouse
/// </summary>
/// <param name="sender">sender</param>
/// <param name="e">args</param>
private void rctHeader_PreviewMouseMove(object sender, MouseEventArgs e)
{
// Only do, if restore on move is allowed
if (doRestoreIfMove)
{
doRestoreIfMove = false;
// Calculate values
double percentHorizontal = e.GetPosition(this).X / ActualWidth;
double targetHorizontal = RestoreBounds.Width * percentHorizontal;
double percentVertical = e.GetPosition(this).Y / ActualHeight;
double targetVertical = RestoreBounds.Height * percentVertical;
// Force set window state
WindowState = WindowState.Normal;
// Get current mouse position
POINT lMousePosition;
GetCursorPos(out lMousePosition);
// Set window location relative to current mouse cursor. This simulates the moving of the window
Left = lMousePosition.X - targetHorizontal;
Top = lMousePosition.Y - targetVertical;
DragMove();
}
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetCursorPos(out POINT lpPoint);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr MonitorFromPoint(POINT pt, MonitorOptions dwFlags);
enum MonitorOptions : uint
{
MONITOR_DEFAULTTONULL = 0x00000000,
MONITOR_DEFAULTTOPRIMARY = 0x00000001,
MONITOR_DEFAULTTONEAREST = 0x00000002
}
[DllImport("user32.dll")]
static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
X = x;
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)]
public struct RECT
{
public int Left, Top, Right, Bottom;
public RECT(int left, int top, int right, int bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment