Я уже некоторое время использую Rainlendar и заметил, что у него есть возможность поместить окно «на рабочий стол». Это похоже на самое нижнее окно (в отличие от самого верхнего).
Как я мог сделать это в приложении WPF?
Спасибо





Мой ответ относится к Win32 API, не относящемуся к WPF (и, вероятно, требующему P / Invoke из C#):
У Rainlendar есть два варианта:
Спасибо, Хью, у меня работает нижняя часть, сейчас попробую с SetParant. В основном «на рабочем столе» находится «снизу», не сворачивая, верно?
Я не уверен, что вы имеете в виду, говоря «без сворачивания», но если вы используете MS Spy ++ для изучения дерева окон (при запущенном Rainlendar), вы, надеюсь, поймете, что я имею в виду.
Это то, что я использовал, чтобы окно всегда было «внизу»:
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);
}
Версия 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);
}
У меня возникли проблемы с поиском окна диспетчера программ, но Киммо, создатель из Rainlendar, дал мне ссылку на код:
http://www.ipi.fi/~rainy/legacy.html
Если кому-то нужна дополнительная информация, просто посмотрите в библиотеке / rainwindow.cpp функцию SetWindowZPos.
Я пытался сделать то же самое ... Я использовал много идей вокруг, но я смог это сделать и предотвратить мерцание.
Мне удалось переопределить 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));
}
Для справки в будущем этот код будет работать только с приложениями WPF с using System.Windows.Interop;. См. Этот вопрос для получения дополнительной информации.
Прилагаемая версия свойства ответа @ HrejWaltz:
Обновление (28.12.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
}
можно добавить образец, как это использовать?
Основываясь на ответах HrejWaltz и Джеймс М, я хочу предоставить модифицированное решение, которое перехватывает и изменяет входящие сообщения WM_WINDOWPOSCHANGING, устанавливая флаг SWP_NOZORDER вместо вызова SetWindowPos каждый раз при получении сообщения WM_SETFOCUS.
Класс предлагает присоединенные свойства для непосредственного добавления в окно WPF с помощью 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
}