C# WPF Auto Resize Window When Taskbar Height Changed - wpf

I have a WPF window which has these properties:
-ResizeMode=NoResize
-WindowStyle=None
I made every functionality of a normal window but i can't figure out how can i make window auto resize itself(when it's maximized) when taskbar's height changes. (Like Microsoft Visual Studio 2017 Window).
I can manually maximize my window but if I hide taskbar there is an empty space between my window and bottom of screen.
Is there any event fired when working area changes?

For your problem you can use SystemParameters.WorkArea.
Initially set the MaxHeight of your MainWindow.
MaxHeight="{Binding Height, Source={x:Static SystemParameters.WorkArea}}"
Register to the SystemParameters.StaticPropertyChanged in the codebehind of the MainWindow to receive changes and update your window size.
SystemParameters.StaticPropertyChanged += (sender, args) =>
{
if (args.PropertyName == nameof(SystemParameters.WorkArea))
{
this.Dispatcher.Invoke(() =>
{
MaxHeight = SystemParameters.WorkArea.Height;
Height = SystemParameters.WorkArea.Height;
WindowState = WindowState.Normal; // Updates the windows new sizes
WindowState = WindowState.Maximized;
});
}
};

Handle the WM_GETMINMAXINFO message for your window, and do whatever re-sizing you need to do
public MainWindow()
{
InitializeComponent();
SourceInitialized += new EventHandler(win_SourceInitialized);
}
private void win_SourceInitialized(object sender, EventArgs e)
{
System.IntPtr handle = (new WinInterop.WindowInteropHelper(this)).Handle;
WinInterop.HwndSource.FromHwnd(handle).AddHook(new WinInterop.HwndSourceHook(WindowProc));
}
private const int WM_GETMINMAXINFO = 0x0024;
private static System.IntPtr WindowProc(
System.IntPtr hwnd,
int msg,
System.IntPtr wParam,
System.IntPtr lParam,
ref bool handled)
{
switch (msg)
{
case WM_GETMINMAXINFO: //https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-getminmaxinfo
WmGetMinMaxInfo(hwnd, lParam); // <------ Do what you need to here ---------->
handled = true;
break;
}
return (System.IntPtr)0;
}
Note that if you are a borderless, not-resizable window, you may also need to get the monitor info (via the Win32 GetMonitorInfo) and restrict your application to the work area of the monitor it is on. On our systems, windows does not correctly size the window for 1900x1200 monitors (it makes it too tall, so we have to set the MaxHeight based on the Monitor Info, and pay attention for that to change if the taskbar is resized by continuing to watch the WM_GETMINMAXINFO messages).
This blog can probably help with that if you have those issues as well:
https://blogs.msdn.microsoft.com/llobo/2006/08/01/maximizing-window-with-windowstylenone-considering-taskbar/

Related

Any way to access mouse Raw Input from WPF?

I'm currently using a global mouse hook to make an app appear if it the mouse cursor reaches the corner of the screen. I just read about the existence of Raw Input and from what I understand, this is a more robust method as a slowdown in my hook will not impact the overall system.
Problem is I can't find any examples anywhere about using Raw Input in WPF.
Closest I got was SlimDX with the following code:
Device.RegisterDevice(UsagePage.Generic, UsageId.Mouse,
DeviceFlags.None);
Device.MouseInput += new EventHandler<MouseInputEventArgs>(mouse_MouseInput);
But that does not seem to work in WPF, only winforms.
Those DeviceFlags.None need to be InputSink to capture input in the background. SharpDX flags there are actually a wrapper for the RAWINPUTDEVICEFLAGS (InputSink = 0x00000100).
In WPF, you want to 1) override OnSourceInitialized and 2) hook WndProc there. While the window pointer is available you need to define the RAWINPUTDEVICE's you want to watch, flag InputSink.
It will look something like
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
source.AddHook(WndProc);
var win = source.Handle;
RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[2];
rid[0].UsagePage = (HIDUsagePage)0x01;
rid[0].Usage = (HIDUsage)0x05; // adds game pad
rid[0].Flags = RawInputDeviceFlags.InputSink; // accept input in background
rid[0].WindowHandle = win;
rid[1].UsagePage = (HIDUsagePage)0x01;
rid[1].Usage = (HIDUsage)0x04; // adds joystick
rid[1].Flags = RawInputDeviceFlags.InputSink; // accept input in background
rid[1].WindowHandle = win;
if (RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0])) == false)
{
var err = Marshal.GetLastWin32Error();
if (err > 0)
{
throw new Win32Exception($"GetLastWin32Error: {err}");
}
else
{
throw new Win32Exception("RegisterRawInputDevices failed with error code 0. Parameter count mis-match?");
}
}
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case WM_INPUT:
{
System.Diagnostics.Debug.WriteLine("Received WndProc.WM_INPUT");
DoTheThing(lParam);
}
break;
}
return hwnd;
}

WPF: Non focusable window

I am developing WPF Touch Screen Keyboard.
I need to know how is it possible to make main window non focusable, so other windows will be receiving the input when I click on virtual keyboard buttons.
Simple applying "Focusable="False"" to the main window and all child controls doesn't work.
I think there is a clickable attribute you can set to false which stops the form receiving click messages.
Problem was solved by using Popup instead of Window, which not grab the focus, when you click on it.
From here: https://social.msdn.microsoft.com/Forums/vstudio/en-US/41ca3605-247c-4c5b-ac5d-74ce5abd7b92/making-a-window-invisible-to-mouse-events-ishittestvisiblefalse-not-working?forum=wpf
I've figured out how to do this. The key being the WS_EX_TRANSPARENT flag for the window's extended style.You can set the topmost property like you normally would, then this code takes care of making the window transparent to mouse clicks:
Code Snippet
public const int WS_EX_TRANSPARENT = 0x00000020;
public const int GWL_EXSTYLE = (-20);
[DllImport("user32.dll")]
public static extern int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
public static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
// Get this window's handle
IntPtr hwnd = new WindowInteropHelper(this).Handle;
// Change the extended window style to include WS_EX_TRANSPARENT
int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
}

WPF Window Drag/Move Boundary

just curious if you know of any way to setup a drag boundary for a window?
It would be nice to have these properties:
Me.MinLeft = 10
Me.MinTop = 10
Me.MaxLeft = 150
Me.MaxTop = 150
Those are made up properties, btw, which would be nice to have.
I know I could probably setup a timer to fire ever 10th of a second and check the left and top and then move it back if it's over. But it would be more elegant to have the window act like it hit a wall and can't go any farther, like moving to the edge of the screen or something similar.
Edit: There seems to be some confusion somewhere, the point I'm trying to make is in the paragraph above, dragging, not re-sizing.
Here is teh "magic" you need to create this functionality, all you have to do is set the Window_SourceInitialized method to the window's SourceInitialized event and insert you logic where the big comment is.
I combined this code from several sources, so there could be some syntax errors in it.
internal enum WM
{
WINDOWPOSCHANGING = 0x0046,
}
[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 Window_SourceInitialized(object sender, EventArgs ea)
{
HwndSource hwndSource = (HwndSource)HwndSource.FromVisual((Window)sender);
hwndSource.AddHook(DragHook);
}
private static IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handeled)
{
switch ((WM)msg)
{
case WM.WINDOWPOSCHANGING:
{
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;
// ***********************
// Here you check the values inside the pos structure
// if you want to override tehm just change the pos
// structure and set changedPos to true
// ***********************
if (!changedPos)
{
return IntPtr.Zero;
}
Marshal.StructureToPtr(pos, lParam, true);
handeled = true;
}
break;
}
return IntPtr.Zero;
}
As I have no doubt that Nir's answer will work spending a little time implementing it, I was able to do what I wanted a little bit more elegant with this code:
Private Sub myWindow_LocationChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.LocationChanged
Dim primaryBounds As System.Drawing.Rectangle = Windows.Forms.Screen.PrimaryScreen.Bounds
Dim windowBounds As System.Drawing.Rectangle = New System.Drawing.Rectangle(CInt(Me.Left), CInt(Me.Top), CInt(Me.Width), CInt(Me.Height))
If (windowBounds.Left < 0) Then
windowBounds = New System.Drawing.Rectangle(0, windowBounds.Top, windowBounds.Width, windowBounds.Height)
ElseIf (windowBounds.Right > primaryBounds.Right) Then
windowBounds = New System.Drawing.Rectangle(primaryBounds.Right - windowBounds.Width, windowBounds.Top, windowBounds.Width, windowBounds.Height)
End If
If (windowBounds.Top < 0) Then
windowBounds = New System.Drawing.Rectangle(windowBounds.Left, 0, windowBounds.Width, windowBounds.Height)
ElseIf (windowBounds.Bottom > primaryBounds.Bottom) Then
windowBounds = New System.Drawing.Rectangle(windowBounds.Left, primaryBounds.Bottom - windowBounds.Height, windowBounds.Width, windowBounds.Height)
End If
Me.Left = windowBounds.Left
Me.Top = windowBounds.Top
End Sub
This made the window being dragged stay within the primary screen (whole window), but you could easily change the bounds to whatever values you needed.
There are dependency properties for WPF's Window for this purpose.
Here they are:
Window.MaxWidth
Window.MaxHeight
These properties will constrain the size of the Window, just like the WinForm's Form.
Maybe you could handle PreviewMouseMove (either the event or override the corresponding protected method) and set e.Handled = true whenever the mouse movement would cause the window to move outside the region you want to constrain it to.
This seems like the most logical, WPF-like way of doing this.

How to remove minimize and maximize buttons from a resizable window?

WPF doesn't provide the ability to have a window that allows resize but doesn't have maximize or minimize buttons. I'd like to able to make such a window so I can have resizable dialog boxes.
I'm aware the solution will mean using pinvoke but I'm not sure what to call and how. A search of pinvoke.net didn't turn up any thing that jumped out at me as what I needed, mainly I'm sure because Windows Forms does provide the CanMinimize and CanMaximize properties on its windows.
Could someone point me towards or provide code (C# preferred) on how to do this?
I've stolen some code I found on the MSDN forums and made an extension method on the Window class, like this:
internal static class WindowExtensions
{
// from winuser.h
private const int GWL_STYLE = -16,
WS_MAXIMIZEBOX = 0x10000,
WS_MINIMIZEBOX = 0x20000;
[DllImport("user32.dll")]
extern private static int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
extern private static int SetWindowLong(IntPtr hwnd, int index, int value);
internal static void HideMinimizeAndMaximizeButtons(this Window window)
{
IntPtr hwnd = new System.Windows.Interop.WindowInteropHelper(window).Handle;
var currentStyle = GetWindowLong(hwnd, GWL_STYLE);
SetWindowLong(hwnd, GWL_STYLE, (currentStyle & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX));
}
}
The only other thing to remember is that for some reason this doesn't work from a window's constructor. I got around that by chucking this into the constructor:
this.SourceInitialized += (x, y) =>
{
this.HideMinimizeAndMaximizeButtons();
};
One way is to set your ResizeMode="NoResize". It will behave like this.
Don't know if this works for your req. visually.. This is
<Window x:Class="DataBinding.MyWindow" ...Title="MyWindow" Height="300" Width="300"
WindowStyle="ToolWindow" ResizeMode="CanResizeWithGrip">
If anyone use Devexpress window (DXWindow) accepted answer doesn't work. One ugly approach is
public partial class MyAwesomeWindow : DXWindow
{
public MyAwesomeWIndow()
{
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
// hides maximize button
Button button = (Button)DevExpress.Xpf.Core.Native.LayoutHelper.FindElementByName(this, DXWindow.ButtonParts.PART_Maximize.ToString());
button.IsHitTestVisible = false;
button.Opacity = 0;
// hides minimize button
button = (Button)DevExpress.Xpf.Core.Native.LayoutHelper.FindElementByName(this, DXWindow.ButtonParts.PART_Minimize.ToString());
button.IsHitTestVisible = false;
button.Opacity = 0;
// hides close button
button = (Button)DevExpress.Xpf.Core.Native.LayoutHelper.FindElementByName(this, DXWindow.ButtonParts.PART_CloseButton.ToString());
button.IsHitTestVisible = false;
button.Opacity = 0;
}
}
Here's a solution I'm using. Note that maximize button is still displayed.
Markup:
<Window x:Class="Example"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Example"
StateChanged="Window_StateChanged">
Code behind:
// Disable maximizing this window
private void Window_StateChanged(object sender, EventArgs e)
{
if (this.WindowState == WindowState.Maximized)
this.WindowState = WindowState.Normal;
}
This variant of the solution proposed by #MattHamilton can (and must) be called in the constructor of the Window. The trick is to subscribe a delegate to the SourceInitialized event within the extension method.
private const int GWL_STYLE = -16, WS_MAXIMIZEBOX = 0x10000, WS_MINIMIZEBOX = 0x20000;
[DllImport("user32.dll")]
extern private static int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
extern private static int SetWindowLong(IntPtr hwnd, int index, int value);
/// <summary>
/// Hides the Minimize and Maximize buttons in a Window. Must be called in the constructor.
/// </summary>
/// <param name="window">The Window whose Minimize/Maximize buttons will be hidden.</param>
public static void HideMinimizeAndMaximizeButtons(this Window window)
{
window.SourceInitialized += (s, e) => {
IntPtr hwnd = new System.Windows.Interop.WindowInteropHelper(window).Handle;
int currentStyle = GetWindowLong(hwnd, GWL_STYLE);
SetWindowLong(hwnd, GWL_STYLE, currentStyle & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX);
};
}
You can set the ResizeMode="NoResize" of the window if you want to remove Minimize and Maximize button
Just use
WindowStyle="ToolWindow"
It hides the maximize and minimize buttons, but the window can still be resized by dragging the window borders and minimize using the hide button in the bottom right corner of the taskbar.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.window.windowstyle?view=windowsdesktop-6.0

How can I open a window's system menu by code?

I have a C# WinForms borderless window, for which I override WndProc and handle the WM_NCHITTEST message. For an area of that form, my hit test function returns HTSYSMENU. Double-clicking that area successfully closes the form, but right-clicking it does not show the window's system menu, nor does it show up when right-clicking the window's name in the taskbar.
This form uses these styles:
this.SetStyle( ControlStyles.AllPaintingInWmPaint, true );
this.SetStyle( ControlStyles.UserPaint, true );
this.SetStyle( ControlStyles.OptimizedDoubleBuffer, true );
this.SetStyle( ControlStyles.ResizeRedraw, true );
And has these non-default property values:
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.KeyPreview = true;
this.MaximizeBox = false;
this.MinimizeBox = false;
I've tried handling WM_NCRBUTTONDOWN and WM_NCRBUTTONUP, and send the WM_GETSYSMENU message, but it didn't work.
A borderless window, if I am not mistaken, is flagged such that it offers no system menu, and that it does not appear in the taskbar.
The fact that any given window does not have a border and does not appear in the taskbar is the result of the style flags set on the window. These particular Style flags can be set using the GetWindowLong and SetWindowLong API calls. However you have to be careful as certain styles just don't work together.
I have written a number of custom controls over the years and I am constantly coaxing windows to become something they weren't originally intended to be.
For example I have written my own dropdown control where I needed a window to behave as a popup and not to activate.
The following code will do that. Note that the code appears in the OnHandleCreated event handler. This is because the flags need to be changed just after the handle is setup which indicates that Windows has already set what it thinks the flags should be.
using System.Runtime.InteropServices;
protected override void OnHandleCreated(EventArgs e) {
uint dwWindowProperty;
User32.SetParent(this.Handle, IntPtr.Zero);
dwWindowProperty = User32.GetWindowLong( this.Handle, User32.GWL.EXSTYLE );
dwWindowProperty = dwWindowProperty | (uint)User32.WSEX.TOOLWINDOW | (uint)User32.WSEX.NOACTIVATE;
User32.SetWindowLong( this.Handle, User32.GWL.EXSTYLE, dwWindowProperty );
dwWindowProperty = User32.GetWindowLong( this.Handle, User32.GWL.STYLE );
dwWindowProperty = ( dwWindowProperty & ~(uint)User32.WS.CHILD ) | (uint)User32.WS.POPUP;
User32.SetWindowLong( this.Handle, User32.GWL.STYLE, dwWindowProperty );
base.OnHandleCreated (e);
}
//this is a fragment of my User32 library wrapper needed for the previous code segment.
class User32
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall )]
public static extern int SetWindowLong( IntPtr hWnd, User32.GWL gwlIndex, uint dwNewLong);
[DllImport("user32.dll", CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall )]
public static extern uint GetWindowLong( IntPtr hWnd, User32.GWL gwlIndex );
[FlagsAttribute]
public enum WS: uint {
POPUP = 0x80000000,
CHILD = 0x40000000,
}
public enum GWL {
STYLE = -16,
EXSTYLE = -20
}
[FlagsAttribute]
public enum WSEX: uint {
TOP = 0x0,
TOPMOST = 0x8,
TOOLWINDOW = 0x80,
NOACTIVATE = 0x08000000,
}
}
Unfortunately the SysMenu style cannot be set without using the Caption style, so I can't say if this is a problem in your implementation.
You can check out the original style list and the extend style list at these two links:
Window Styles
CreateWindowEx
I have the same properties in my application and Right click doesn't work either, so this is not your problem, it appears to be the way windows forms respond when they have no border.
If you set your border to the normal value, you will be able to have right click in the taskbar and such.
For right click on other controls, you'll need to set the ContextMenuStrip and provide your "menu". But I'm not sure if this works when you have it without border. I have been unable to make it work.
protected override void WndProc( ref System.Windows.Forms.Message m )
{ // RightClickMenu
if ( m.Msg == 0x313 )
{
this.contextMenuStrip1.Show(this, this.PointToClient(new Point(m.LParam.ToInt32())));
}}
This detects rightclick on the applications taskbar "area"..
maybe it will help ?

Resources