C# 窗口“在桌面上”
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/365094/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Window "on desktop"
提问by Artur Carvalho
I've been using Rainlendarfor 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).
我已经使用Rainlendar一段时间了,我注意到它有一个选项可以将窗口放在“桌面上”。它就像一个最底部的窗口(相对于最顶部)。
How could I do this on a WPF app?
我怎么能在 WPF 应用程序上做到这一点?
Thanks
谢谢
采纳答案by Hugh Allen
My answer is in terms of the Win32 API, not specific to WPF (and probably requiring P/Invoke from C#):
我的答案是针对 Win32 API,而不是特定于 WPF(并且可能需要来自 C# 的 P/Invoke):
Rainlendar has two options:
Rainlendar 有两种选择:
- "On Desktop", it becomes a child of the Explorer desktop window ("Program Manager"). You could achieve this with the SetParentAPI.
- "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_WINDOWPOSCHANGINGmessage.
- “在桌面上”,它成为资源管理器桌面窗口(“程序管理器”)的子级。您可以使用SetParentAPI实现这一点。
- “在底部”就是你所描述的——它的窗口停留在 Z 顺序的底部,就在桌面的前面。开始时将它们放在那里很容易(请参阅SetWindowPos) - 诀窍是在单击时阻止它们出现在前面。我建议处理WM_WINDOWPOSCHANGING消息。
回答by Artur Carvalho
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);
}
回答by Artur Carvalho
The OnDesktop version that Im using:
我使用的 OnDesktop 版本:
[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:
我在查找程序管理器窗口时遇到了一些麻烦,但是来自 Rainlendar 的创建者 Kimmo 给了我一个代码链接:
http://www.ipi.fi/~rainy/legacy.html
http://www.ipi.fi/~rainy/legacy.html
If anybody needs more detail just look in library/rainwindow.cpp for the function SetWindowZPos.
如果有人需要更多详细信息,请查看 library/rainwindow.cpp 中的 SetWindowZPos 函数。
回答by Maurice Flanagan
WarningThe accepted answer suggests that you call SetParent to create a child of the Desktop. If you do this, you cause the Win32 Window Manager to synchronize the input queue of the Desktop to your child window, this is a bad thing- Raymond Chen explains why.Basically, if your window hangs or blocks (say with a MessageBox) you will lock up your desktop.
警告接受的答案建议您调用 SetParent 来创建桌面的子项。如果你这样做,你会导致 Win32 窗口管理器将桌面的输入队列同步到你的子窗口,这是一件坏事- Raymond Chen 解释了原因。基本上,如果您的窗口挂起或阻塞(例如使用 MessageBox),您将锁定桌面。
回答by HrejWaltz
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...
我设法覆盖了 WndProc,之前使用了一个 setwindowpos 将它放在后台,另一个用来防止它获得焦点......
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));
}
回答by James M
The attached property version of @HrejWaltz's answer:
@HrejWaltz 回答的附加属性版本:
Update (12/28/2016)
更新 (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
}
回答by kroimon
Based on the answers of HrejWaltzand 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.
基于HrejWaltz和James M的回答,我想提供一个修改的解决方案,WM_WINDOWPOSCHANGING
通过设置SWP_NOZORDER
标志而不是SetWindowPos
每次WM_SETFOCUS
收到消息时调用来拦截和修改传入的消息。
The class offers attached properties to directly add to a WPF Window using WindowSinker.AlwaysOnBottom="True"
.
该类提供附加属性以使用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
}