Окно «на рабочем столе»

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

Как я могу сделать это в приложении WPF?

Спасибо

13.12.2008 10:45:01
7 ОТВЕТОВ
РЕШЕНИЕ

Мой ответ в терминах Win32 API, не относящихся к WPF (и, вероятно, требующих P / Invoke из C #):

У Rainlendar есть два варианта:

  • «На рабочем столе» становится дочерним элементом окна рабочего стола Проводника («Диспетчер программ»). Вы можете достичь этого с помощью SetParent API.
  • «На дне» - это то, что вы описываете - его окна остаются внизу Z-порядка, прямо перед рабочим столом. Достаточно просто поместить их туда для начала (см. SetWindowPos ) - хитрость заключается в том, чтобы не дать им выйти вперед при нажатии. Я бы предложил обработать сообщение WM_WINDOWPOSCHANGING .
15
13.12.2008 12:42:12
Спасибо, Хью, у меня работает "нижняя" часть, сейчас я собираюсь попробовать SetParant. В основном "на рабочем столе" находится "на дне" без минимизации верно?
Artur Carvalho 13.12.2008 14:34:57
Я не уверен, что вы получаете с "без минимизации", но если вы используете MS Spy ++ для проверки дерева окон (с запущенным Rainlendar), вы, надеюсь, поймете, что я имею в виду.
Hugh Allen 14.12.2008 00:21:35

Это то, что я использовал, чтобы окно всегда было «снизу»:

   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);
}
9
13.12.2008 14:36:27

Версия 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

Если кому-то нужно больше подробностей, просто посмотрите в library / rainwindow.cpp функцию SetWindowZPos.

3
27.12.2008 22:14:18

Предупреждение Принятый ответ предполагает, что вы вызываете SetParent, чтобы создать дочерний элемент рабочего стола. Если вы сделаете это, вы заставите Win32 Window Manager синхронизировать входную очередь рабочего стола с вашим дочерним окном, это плохо - Раймонд Чен объясняет почему. По сути, если ваше окно зависает или блокируется (скажем, с помощью MessageBox), вы заблокируете свой рабочий стол.

6
9.02.2009 14:45:56
1. «Рабочий стол», на который я ссылаюсь, не является корневым окном, возвращаемым функцией GetDesktopWindow (). 2. Даже если вы вызовете SetParent (myWindow, GetDesktopWindow ()), это не вызовет проблемы - оно просто сделает ваше окно окном «верхнего уровня», если оно еще не было. Я думаю, что вы должны прочитать эту статью Рэймонда Чена снова.
Hugh Allen 8.04.2009 01:38:24
Хью, ты действительно попробовал это - его легко воспроизвести. 1. Вызовите SetParent с «Менеджером программ» в качестве родителя. 2. Повесьте свое дочернее окно (сделайте сон, что угодно) 3. Обратите внимание, что рабочий стол теперь завис, вы не можете с ним взаимодействовать (ярлыки и т. Д.).
Maurice Flanagan 8.04.2009 16:41:14
ОК, если вы повесите дочернего элемента Progman, вы повесите Progman, но показ MessageBox не делает этого, и это то, что вы можете восстановить в любом случае (убить процесс). Вы перепутали проблему, связавшись со статьей, в которой описано что-то другое (зависание всей машины путем отключения корневого окна).
Hugh Allen 9.04.2009 01:54:41
(под «показом MessageBox» я, конечно, имею в виду, что дочернее окно вызывает MessageBox () с самим собой в качестве владельца, а не с Progman)
Hugh Allen 9.04.2009 02:10:06
Тем не менее, это в комментариях, поэтому кажется, что это больше не применяется. Jordan Russell 24 Feb 2004 6:25 PM In the current SPs for Windows 2000 and XP, it looks like they fixed it so that disabling the desktop window is no longer possible. (i.e. EnableWindow(GetDesktopWindow(), FALSE) has no effect), Да, я некрофил ... xD
beppe9000 19.01.2015 08:30:54

Я пытался сделать то же самое ... Я использовал много идей, но я смог это предотвратить и предотвратить мерцание.

Мне удалось переопределить 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));
    }
5
19.03.2013 12:12:53
Для дальнейшего использования этот код будет работать только с приложениями WPF с. using System.Windows.Interop; Для получения дополнительной информации см. Этот вопрос
javon27 11.04.2018 22:48:31

Прилагаемая версия свойства ответа @ 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
}
1
28.12.2016 22:18:34

Основываясь на ответах HrejWaltz и James M , я хочу предложить модифицированное решение, которое перехватывает и модифицирует входящие 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
}
2
5.07.2019 15:52:35