窗口“在桌面上”

时间:2008-12-13 10:45:02

标签: c# wpf

我已经使用Rainlendar一段时间了,我注意到它可以选择将窗口“放在桌面上”。它就像一个bottomMost窗口(与最顶层的窗口相对)。

我怎么能在WPF应用程序上执行此操作?

由于

7 个答案:

答案 0 :(得分:14)

我的答案是Win32 API,不是特定于WPF(可能需要来自C#的P / Invoke):

Rainlendar有两种选择:

  • “在桌面上”,它成为Explorer桌面窗口(“程序管理器”)的子项。您可以使用SetParent API实现此目的。
  • “On Bottom”就是您所描述的 - 它的窗口位于Z-order的底部,就在桌面前面。很容易将它们放在那里开始(参见SetWindowPos) - 诀窍是在点击时阻止它们到达前面。我建议处理WM_WINDOWPOSCHANGING消息。

答案 1 :(得分:9)

这是我使用的,所以窗口总是“在底部”:

   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);
}

答案 2 :(得分:6)

警告接受的答案表明您调用SetParent来创建桌面的子级。如果这样做,则会导致Win32窗口管理器将桌面的输入队列同步到子窗口,这是坏事 - Raymond Chen explains why.基本上,如果窗口挂起或阻塞(用MessageBox说)你会锁定你的桌面。

答案 3 :(得分:3)

我试图做同样的事...... 我已经使用了很多想法,但我能够做到并防止闪烁。

我设法覆盖了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));
    }

答案 4 :(得分:3)

我使用的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的创建者Kimmo给了我一个代码链接:

http://www.ipi.fi/~rainy/legacy.html

如果有人需要更多详细信息,请查看library / rainwindow.cpp以获取SetWindowZPos函数。

答案 5 :(得分:1)

基于HrejWaltzJames M的答案,我想提供一种修改后的解决方案,该方法通过设置WM_WINDOWPOSCHANGING标志而不是调用来拦截和修改传入的SWP_NOZORDER消息每次收到SetWindowPos消息时,WM_SETFOCUS

该类提供了附加的属性,可使用WindowSinker.AlwaysOnBottom="True"直接添加到WPF窗口。

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
}

答案 6 :(得分:0)

@ HrejWaltz的附属物版本答案:

更新(2016年12月28日)

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
}