I have a wpf application which is in maximized state always without showing taskbar.
Here is the code for Hiding and showing taskbar.
[DllImport("user32.dll")]
private static extern int FindWindow(string className, string windowText);
[DllImport("user32.dll")]
private static extern int ShowWindow(int hwnd, int command);
private const int SW_HIDE = 0;
private const int SW_SHOW = 1;
static int hwnd = FindWindow("Shell_TrayWnd", "");
public static new void Hide()
{
ShowWindow(hwnd, SW_HIDE);
}
public static new void Show()
{
ShowWindow(hwnd, SW_SHOW);
}
This is working fine on windows 7. But when application runs on Windows 10.. taskbar didnt show up again by calling show().
Here is the part where I am calling show()
#region Show Desktop
private void Desktop_MouseUp(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left )
{
this.WindowState = System.Windows.WindowState.Minimized;
Shell32.Shell objShel = new Shell32.Shell();
objShel.MinimizeAll();
Show();
}
}
#endregion
This works on the main display and is taken from here and converted to c#.
public static class Taskbar
{
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr FindWindow(
string lpClassName,
string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
private static extern int SetWindowPos(
IntPtr hWnd,
IntPtr hWndInsertAfter,
int x,
int y,
int cx,
int cy,
uint uFlags
);
[Flags]
private enum SetWindowPosFlags : uint
{
HideWindow = 128,
ShowWindow = 64
}
public static void Show()
{
var window = FindWindow("Shell_traywnd", "");
SetWindowPos(window, IntPtr.Zero, 0, 0, 0, 0, (uint) SetWindowPosFlags.ShowWindow);
}
public static void Hide()
{
var window = FindWindow("Shell_traywnd", "");
SetWindowPos(window, IntPtr.Zero, 0, 0, 0, 0, (uint)SetWindowPosFlags.HideWindow);
}
}
Related
Currently, I can open & close the windows touch keyboard,
I don't know how to detect the touch keyboard is popped up or not,
so I try to get windows style value,
when I click the button at the top-right corner, to hiding the touch keyboard, please reference this image
the windows style value is the same as I did not hide the touch keyboard,
Is there any way to detect this situation?
---Test code as below -------------------------------------------------------
namespace WindowsFormsApp7
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
CloseWindowsTouchKeyboard();
}
private void btnOpen_Click(object sender, EventArgs e)
{
OpenWindowsTouchKeyboard();
}
private void btnClose_Click(object sender, EventArgs e)
{
CloseWindowsTouchKeyboard();
}
private void btnGetWindowLong_Click(object sender, EventArgs e)
{
GetWindowLong();
}
private const string IPTIP_MAIN_WINDOW_CLASS_NAME = "IPTip_Main_Window";
private const int GWL_STYLE = -16;
private const int WM_SYSCOMMAND = 274;
private const uint SC_CLOSE = 61536;
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool PostMessage(IntPtr hWnd, int Msg, uint wParam, uint lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowLong(IntPtr hWnd, int nIndex);
public static void OpenWindowsTouchKeyboard()
{
string strFilePath = #"C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe";
if (!File.Exists(strFilePath))
{
return;
}
Process.Start(strFilePath);
}
public static void CloseWindowsTouchKeyboard()
{
IntPtr TouchhWnd;
TouchhWnd = FindWindow(IPTIP_MAIN_WINDOW_CLASS_NAME, null);
if (TouchhWnd == IntPtr.Zero)
{
return;
}
PostMessage(TouchhWnd, WM_SYSCOMMAND, SC_CLOSE, 0);
}
public static void GetWindowLong()
{
IntPtr tKeyboardHandle = FindWindow(IPTIP_MAIN_WINDOW_CLASS_NAME, null);
if (tKeyboardHandle != IntPtr.Zero)
{
uint iStyle = GetWindowLong(tKeyboardHandle, GWL_STYLE);
Console.WriteLine($"iStyle: {iStyle}");
}
}
}
}
You'll need to utilize ComImport to define interfaces manually:
[ComImport, Guid("228826af-02e1-4226-a9e0-99a855e455a6")]
class ImmersiveShellBroker { }
[ComImport, Guid("9767060c-9476-42e2-8f7b-2f10fd13765c")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IImmersiveShellBroker
{
void M();
IInputHostManagerBroker GetInputHostManagerBroker();
}
[ComImport, Guid("2166ee67-71df-4476-8394-0ced2ed05274")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IInputHostManagerBroker { void GetIhmLocation(out Bounds rect, out DisplayMode mode); }
[StructLayout(LayoutKind.Sequential)]
struct Bounds { public int Left, Top, Right, Bottom; }
enum DisplayMode { None = 0, Floating = 2, Docked = 3 }
Then you can use them to check the touch keyboard display mode like so,
IImmersiveShellBroker broker = (IImmersiveShellBroker)(new ImmersiveShellBroker());
IInputHostManagerBroker inputHost = broker.GetInputHostManagerBroker();
Marshal.ReleaseComObject(broker);
// if mode != DisplayMode.None then the keyboard is open
DisplayMode mode;
inputHost.GetIhmLocation(out _, out mode);
you can also cache the inputHost somewhere, but it's not necessary.
I have some C# code that allows the user to control a LabVIEW VI from a C# Windows Forms application.
As of right now, when the user clicks on the "open dialog" button, it opens the VI in another separate, LabVIEW-styled window. What I would like, if possible, is to have that window open as a child form inside the parent.
Everything else works as far as the LabVIEW/C# interface. I just would like to have everything self-contained in one aesthetically pleasing window.
Here's Form1.cs:
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace LabVIEW_DLL_Call
{
public partial class Form1 : Form
{
[DllImport("user32.dll", SetLastError = true)]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("SharedLib.dll", CallingConvention = CallingConvention.Cdecl)]
static extern long Launch();
[DllImport("SharedLib.dll", CallingConvention=CallingConvention.Cdecl)]
static extern long SetParams(ushort signalType, double frequency, double amplitude);
[DllImport("SharedLib.dll", CallingConvention=CallingConvention.Cdecl)]
static extern long GetData(double[] Array, long len);
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void btnLaunch_Click(object sender, EventArgs e)
{
Launch();
var hWnd = FindWindow("dialog.vi", null);
SetParent(hWnd, panel1.Handle);
}
private void btnSetParams_Click(object sender, EventArgs e)
{
SetParams((ushort)this.dropSignalType.SelectedIndex, (double)this.numFreq.Value, (double)this.numAmplitude.Value);
}
private void btnGetData_Click(object sender, EventArgs e)
{
int dataCount = 1000;
double[] results = new double[dataCount];
GetData(results, dataCount);
string txt = String.Join("\r\n", results);
this.textBox1.Text = txt;
}
}
}
Basically, what happens is that Form2 loads up within Form1, but it also generates the LabVIEW window. (And the second photo is one of the launch.vi)
That's a great start!
It could look something like this:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
private const int SWP_NOSIZE = 0x0001;
private async void btnLaunch_Click(object sender, EventArgs e)
{
bool foundIt = false;
DateTime timeOut = DateTime.Now.AddSeconds(10);
Launch();
do
{
await Task.Delay(250);
var hWnd = FindWindow(null, "dialog.vi");
if (!hWnd.Equals(IntPtr.Zero))
{
SetParent(hWnd, panel1.Handle);
SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE);
foundIt = true;
}
}
while (!foundIt && (DateTime.Now <= timeOut));
if (!foundIt)
{
MessageBox.Show("Failed to find the LabView window.");
}
}
I need to check what window the user currently has selected, and do stuff if they have a specific program selected.
I haven't used the GetForegroundWindow function before, and can't find any information on how to use it in this manner.
I simply need an if comparing the current window to see if its a specific program. However the GetForegroundWindow function doesn't give back a string or int it seems. So mainly I don't know how to find out the value of the program window I want to compare it to.
I currently have the code to get the current window:
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
IntPtr selectedWindow = GetForegroundWindow();
I need to be able to apply it as follows ideally:
If (selectedWindow!="SpecificProgram")
{
<Do this stuff>
}
I'm hoping the GetForegroundWindow value/object is unique to each program and doesn't function in some way that each specific program/window has different values each-time.
I'm also doing this as part of a windows form though I doubt it matters.
-Thanks for any help
Edit: This way works, and uses the tile of the current window, which makes it perfect for checking if the window is right easily:
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
private string GetActiveWindowTitle()
{
const int nChars = 256;
StringBuilder Buff = new StringBuilder(nChars);
IntPtr handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
and then I can just do:
if (GetActiveWindowTitle()=="Name of Window")
{
DoStuff.jpg
}
It has some code but it works:
#region Retrieve list of windows
[DllImport("user32")]
private static extern int GetWindowLongA(IntPtr hWnd, int index);
[DllImport("USER32.DLL")]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("USER32.DLL")]
private static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
private const int GWL_STYLE = -16;
private const ulong WS_VISIBLE = 0x10000000L;
private const ulong WS_BORDER = 0x00800000L;
private const ulong TARGETWINDOW = WS_BORDER | WS_VISIBLE;
internal class Window
{
public string Title;
public IntPtr Handle;
public override string ToString()
{
return Title;
}
}
private List<Window> windows;
private void GetWindows()
{
windows = new List<Window>();
EnumWindows(Callback, 0);
}
private bool Callback(IntPtr hwnd, int lParam)
{
if (this.Handle != hwnd && (GetWindowLongA(hwnd, GWL_STYLE) & TARGETWINDOW) == TARGETWINDOW)
{
StringBuilder sb = new StringBuilder(100);
GetWindowText(hwnd, sb, sb.Capacity);
Window t = new Window();
t.Handle = hwnd;
t.Title = sb.ToString();
windows.Add(t);
}
return true; //continue enumeration
}
#endregion
And to check user window:
IntPtr selectedWindow = GetForegroundWindow();
GetWindows();
for (i = 0; i < windows.Count; i++)
{
if(selectedWindow == windows[i].Handle && windows[i].Title == "Program Title X")
{
//Do stuff
break;
}
}
Valter
We want our application to run in full screen mode with no title bar on a Win CE 5.0 powered device. The application is being developed using .NET Compact Framework 3.5 (C#).
I have followed this tutorial, but I encountered an error. Here is my code:
namespace DatalogicDeviceControl
{
public partial class Form1 : Form
{
public const int SWP_ASYNCWINDOWPOS = 0x4000;
public const int SWP_DEFERERASE = 0x2000;
public const int SWP_DRAWFRAME = 0x0020;
public const int SWP_FRAMECHANGED = 0x0020;
public const int SWP_HIDEWINDOW = 0x0080;
public const int SWP_NOACTIVATE = 0x0010;
public const int SWP_NOCOPYBITS = 0x0100;
public const int SWP_NOMOVE = 0x0002;
public const int SWP_NOOWNERZORDER = 0x0200;
public const int SWP_NOREDRAW = 0x0008;
public const int SWP_NOREPOSITION = 0x0200;
public const int SWP_NOSENDCHANGING = 0x0400;
public const int SWP_NOSIZE = 0x0001;
public const int SWP_NOZORDER = 0x0004;
public const int SWP_SHOWWINDOW = 0x0040;
public const int HWND_TOP = 0;
public const int HWND_BOTTOM = 1;
public const int HWND_TOPMOST = -1;
public const int HWND_NOTOPMOST = -2;
public Form1()
{
InitializeComponent();
HideStartBar();
}
[DllImport("coredll.dll", EntryPoint = "FindWindowW", SetLastError = true)]
private static extern IntPtr FindWindowCE(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
public void HideStartBar()
{
IntPtr handle;
try
{
// Find the handle to the Start Bar
handle = FindWindowCE("HHTaskBar", null);
// If the handle is found then hide the start bar
if (handle != IntPtr.Zero)
{
// Hide the start bar
SetWindowPos(handle, 0, 0, 0, 0, 0, SWP_HIDEWINDOW);
}
}
catch
{
MessageBox.Show("Could not hide Start Bar.");
}
}
}
}
I have encountered the following error:
The best overloaded method match for 'DatalogicDeviceControl.Form1.SetWindowPos(System.IntPtr, int, int, int, int, uint)' has some invalid arguments
#dzerow: Your answer is correct: Windows Mobile does not support the user32.dll library.
Use the coredll.dll library instead.
private const int SRCCOPY = 0x00CC0020;
private const string CORE_DLL = "coredll.dll";
private static IntPtr _taskBar;
private static IntPtr _sipButton;
private static string _deviceId, _deviceIp;
private static DateTime _lastUpdateCheck, _startTime;
[DllImport(CORE_DLL)]
public static extern int BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
[DllImport(CORE_DLL)]
public static extern bool CeRunAppAtEvent(string appName, int Event);
[DllImport(CORE_DLL, EntryPoint = "FindWindowW", SetLastError = true)]
public static extern IntPtr FindWindowCE(string lpClassName, string lpWindowName);
[DllImport(CORE_DLL)]
private static extern IntPtr GetDC(IntPtr hwnd);
[DllImport(CORE_DLL)]
public static extern bool MessageBeep(int uType);
[DllImport(CORE_DLL, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
Below is a lot of the code from an application I used to manage. Parts of it may be incomplete and there could be too much info. If something is missing, just comment and I will fill that in.
Basically, the routine I have written enables you to call ShowWindowsMenu(bool enable) to either enable or disable the HHTaskBar (task bar) and the MS_SIPBUTTON (soft input button).
public static void ShowWindowsMenu(bool enable) {
try {
if (enable) {
if (_taskBar != IntPtr.Zero) {
SetWindowPos(_taskBar, IntPtr.Zero, 0, 0, 240, 26, (int)WindowPosition.SWP_SHOWWINDOW); // display the start bar
}
} else {
_taskBar = FindWindowCE("HHTaskBar", null); // Find the handle to the Start Bar
if (_taskBar != IntPtr.Zero) { // If the handle is found then hide the start bar
SetWindowPos(_taskBar, IntPtr.Zero, 0, 0, 0, 0, (int)WindowPosition.SWP_HIDEWINDOW); // Hide the start bar
}
}
} catch (Exception err) {
ErrorWrapper(enable ? "Show Start" : "Hide Start", err);
}
try {
if (enable) {
if (_sipButton != IntPtr.Zero) { // If the handle is found then hide the start bar
SetWindowPos(_sipButton, IntPtr.Zero, 0, 0, 240, 26, (int)WindowPosition.SWP_SHOWWINDOW); // display the start bar
}
} else {
_sipButton = FindWindowCE("MS_SIPBUTTON", "MS_SIPBUTTON");
if (_sipButton != IntPtr.Zero) { // If the handle is found then hide the start bar
SetWindowPos(_sipButton, IntPtr.Zero, 0, 0, 0, 0, (int)WindowPosition.SWP_HIDEWINDOW); // Hide the start bar
}
}
} catch (Exception err) {
ErrorWrapper(enable ? "Show SIP" : "Hide SIP", err);
}
}
Be sure to turn these features back on when your program exits, or the user will have to reboot the device to get those re-enabled.
EDIT: I forgot the WindowPosition enumerated value I created:
public enum WindowPosition {
SWP_HIDEWINDOW = 0x0080,
SWP_SHOWWINDOW = 0x0040
}
Sorry about that.
Anything else?
Unfortunately Windows Mobile doesn't contain user32.dll, as well as many other normal Windows API DLLs. I had to P/Invoke into coredll.dll instead. For signatures, see PInvoke.net's section (at the bottom left) for "Smart Device Functions".
I've been using Rainlendar for some time and I noticed that it has an option to put the window "on desktop". It's like a bottomMost window (as against topmost).
How could I do this on a WPF app?
Thanks
My answer is in terms of the Win32 API, not specific to WPF (and probably requiring P/Invoke from C#):
Rainlendar has two options:
"On Desktop", it becomes a child of the Explorer desktop window ("Program Manager"). You could achieve this with the SetParent API.
"On Bottom" is what you describe - its windows stay at the bottom of the Z-order, just in front of the desktop. It's easy enough to put them there to begin with (see SetWindowPos) - the trick is to stop them coming to the front when clicked. I would suggest handling the WM_WINDOWPOSCHANGING message.
This is what I used so the window is always "on bottom":
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
...
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,
int Y, int cx, int cy, uint uFlags);
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOACTIVATE = 0x0010;
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
public static void SetBottom(Window window)
{
IntPtr hWnd = new WindowInteropHelper(window).Handle;
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
}
I was trying to do the same...
i've used a lot of ideas arround, but i was able to to it and prevent the flickering.
I managed to override WndProc, used one setwindowpos before to put it in the background, and another to prevent it from getting the focus...
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOACTIVATE = 0x0010;
const UInt32 SWP_NOZORDER = 0x0004;
const int WM_ACTIVATEAPP = 0x001C;
const int WM_ACTIVATE = 0x0006;
const int WM_SETFOCUS = 0x0007;
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
const int WM_WINDOWPOSCHANGING = 0x0046;
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,
int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr DeferWindowPos(IntPtr hWinPosInfo, IntPtr hWnd,
IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr BeginDeferWindowPos(int nNumWindows);
[DllImport("user32.dll")]
static extern bool EndDeferWindowPos(IntPtr hWinPosInfo);
private void Window_Loaded(object sender, RoutedEventArgs e)
{
IntPtr hWnd = new WindowInteropHelper(this).Handle;
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
IntPtr windowHandle = (new WindowInteropHelper(this)).Handle;
HwndSource src = HwndSource.FromHwnd(windowHandle);
src.AddHook(new HwndSourceHook(WndProc));
}
private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_SETFOCUS)
{
IntPtr hWnd = new WindowInteropHelper(this).Handle;
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
handled = true;
}
return IntPtr.Zero;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
IntPtr windowHandle = (new WindowInteropHelper(this)).Handle;
HwndSource src = HwndSource.FromHwnd(windowHandle);
src.RemoveHook(new HwndSourceHook(this.WndProc));
}
The OnDesktop version that Im using:
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
public static void SetOnDesktop(Window window)
{
IntPtr hWnd = new WindowInteropHelper(window).Handle;
IntPtr hWndProgMan = FindWindow("Progman", "Program Manager");
SetParent(hWnd, hWndProgMan);
}
I was having some trouble finding the Program Manager window, but Kimmo, the creator from Rainlendar gave me a link to the code:
http://www.ipi.fi/~rainy/legacy.html
If anybody needs more detail just look in library/rainwindow.cpp for the function SetWindowZPos.
The attached property version of #HrejWaltz's answer:
Update (12/28/2016)
public class WindowSinker
{
#region Properties
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOACTIVATE = 0x0010;
const UInt32 SWP_NOZORDER = 0x0004;
const int WM_ACTIVATEAPP = 0x001C;
const int WM_ACTIVATE = 0x0006;
const int WM_SETFOCUS = 0x0007;
const int WM_WINDOWPOSCHANGING = 0x0046;
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
Window Window = null;
#endregion
#region WindowSinker
public WindowSinker(Window Window)
{
this.Window = Window;
}
#endregion
#region Methods
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr DeferWindowPos(IntPtr hWinPosInfo, IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr BeginDeferWindowPos(int nNumWindows);
[DllImport("user32.dll")]
static extern bool EndDeferWindowPos(IntPtr hWinPosInfo);
void OnClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
var Handle = (new WindowInteropHelper(Window)).Handle;
var Source = HwndSource.FromHwnd(Handle);
Source.RemoveHook(new HwndSourceHook(WndProc));
}
void OnLoaded(object sender, RoutedEventArgs e)
{
var Hwnd = new WindowInteropHelper(Window).Handle;
SetWindowPos(Hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
var Handle = (new WindowInteropHelper(Window)).Handle;
var Source = HwndSource.FromHwnd(Handle);
Source.AddHook(new HwndSourceHook(WndProc));
}
IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_SETFOCUS)
{
hWnd = new WindowInteropHelper(Window).Handle;
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
handled = true;
}
return IntPtr.Zero;
}
public void Sink()
{
Window.Loaded += OnLoaded;
Window.Closing += OnClosing;
}
public void Unsink()
{
Window.Loaded -= OnLoaded;
Window.Closing -= OnClosing;
}
#endregion
}
public static class WindowExtensions
{
#region Always On Bottom
public static readonly DependencyProperty SinkerProperty = DependencyProperty.RegisterAttached("Sinker", typeof(WindowSinker), typeof(WindowExtensions), new UIPropertyMetadata(null));
public static WindowSinker GetSinker(DependencyObject obj)
{
return (WindowSinker)obj.GetValue(SinkerProperty);
}
public static void SetSinker(DependencyObject obj, WindowSinker value)
{
obj.SetValue(SinkerProperty, value);
}
public static readonly DependencyProperty AlwaysOnBottomProperty = DependencyProperty.RegisterAttached("AlwaysOnBottom", typeof(bool), typeof(WindowExtensions), new UIPropertyMetadata(false, OnAlwaysOnBottomChanged));
public static bool GetAlwaysOnBottom(DependencyObject obj)
{
return (bool)obj.GetValue(AlwaysOnBottomProperty);
}
public static void SetAlwaysOnBottom(DependencyObject obj, bool value)
{
obj.SetValue(AlwaysOnBottomProperty, value);
}
static void OnAlwaysOnBottomChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var Window = sender as Window;
if (Window != null)
{
if ((bool)e.NewValue)
{
var Sinker = new WindowSinker(Window);
Sinker.Sink();
SetSinker(Window, Sinker);
}
else
{
var Sinker = GetSinker(Window);
Sinker.Unsink();
SetSinker(Window, null);
}
}
}
#endregion
}
Based on the answers of HrejWaltz and James M, I want to provide a modified solution that intercepts and modifies incoming WM_WINDOWPOSCHANGING messages by setting the SWP_NOZORDER flag instead of calling SetWindowPos everytime a WM_SETFOCUS message is received.
The class offers attached properties to directly add to a WPF Window using WindowSinker.AlwaysOnBottom="True".
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
public class WindowSinker
{
#region Windows API
// ReSharper disable InconsistentNaming
private const int WM_WINDOWPOSCHANGING = 0x0046;
private const uint SWP_NOSIZE = 0x0001;
private const uint SWP_NOMOVE = 0x0002;
private const uint SWP_NOZORDER = 0x0004;
private const uint SWP_NOACTIVATE = 0x0010;
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public uint flags;
}
private static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
// ReSharper restore InconsistentNaming
#endregion
#region WindowSinker
private readonly Window window;
private bool disposed;
public WindowSinker(Window window)
{
this.window = window;
if (window.IsLoaded)
{
OnWindowLoaded(window, null);
}
else
{
window.Loaded += OnWindowLoaded;
}
window.Closing += OnWindowClosing;
}
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
window.Loaded -= OnWindowLoaded;
window.Closing -= OnWindowClosing;
disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~WindowSinker()
{
Dispose(false);
}
#endregion
#region Event Handlers
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy,
uint uFlags);
private void OnWindowLoaded(object sender, RoutedEventArgs e)
{
SetWindowPos(new WindowInteropHelper(window).Handle, HWND_BOTTOM, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
var source = HwndSource.FromHwnd(new WindowInteropHelper(window).Handle);
source?.AddHook(WndProc);
}
private void OnWindowClosing(object sender, CancelEventArgs e)
{
var source = HwndSource.FromHwnd(new WindowInteropHelper(window).Handle);
source?.RemoveHook(WndProc);
}
private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_WINDOWPOSCHANGING)
{
var windowPos = Marshal.PtrToStructure<WINDOWPOS>(lParam);
windowPos.flags |= SWP_NOZORDER;
Marshal.StructureToPtr(windowPos, lParam, false);
}
return IntPtr.Zero;
}
#endregion
#region Attached Properties
private static readonly DependencyProperty SinkerProperty = DependencyProperty.RegisterAttached(
"Sinker",
typeof(WindowSinker),
typeof(WindowSinker),
null);
public static readonly DependencyProperty AlwaysOnBottomProperty = DependencyProperty.RegisterAttached(
"AlwaysOnBottom",
typeof(bool),
typeof(WindowSinker),
new UIPropertyMetadata(false, OnAlwaysOnBottomChanged));
public static WindowSinker GetSinker(DependencyObject d)
{
return (WindowSinker) d.GetValue(SinkerProperty);
}
private static void SetSinker(DependencyObject d, WindowSinker value)
{
d.SetValue(SinkerProperty, value);
}
public static bool GetAlwaysOnBottom(DependencyObject d)
{
return (bool) d.GetValue(AlwaysOnBottomProperty);
}
public static void SetAlwaysOnBottom(DependencyObject d, bool value)
{
d.SetValue(AlwaysOnBottomProperty, value);
}
private static void OnAlwaysOnBottomChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (sender is Window window)
{
if ((bool) e.NewValue)
{
SetSinker(window, new WindowSinker(window));
}
else
{
GetSinker(window)?.Dispose();
SetSinker(window, null);
}
}
}
#endregion
}