Окно «на рабочий стол»

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

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

Спасибо

Предупреждение Принятый ответ предполагает, что вы вызываете SetParent, чтобы создать дочерний элемент рабочего стола. Если вы это сделаете, вы заставите Win32 Window Manager синхронизировать входную очередь рабочего стола с вашим дочерним окном, это плохо - Раймонд Чен объясняет, почему. В основном, если ваше окно зависает или блокируется (скажем, с помощью MessageBox), вы заблокируете свой рабочий стол .
Maurice Flanagan 09.02.2009 17:45
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
14
1
13 168
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

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

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

  • «На рабочем столе» он становится дочерним по отношению к окну рабочего стола проводника («Диспетчер программ»). Вы можете добиться этого с помощью SetParent API.
  • «Снизу» - это то, что вы описываете - его окна остаются внизу Z-порядка, прямо перед рабочим столом. Достаточно просто поместить их туда для начала (см. SetWindowPos) - уловка в том, чтобы они не выходили вперед при нажатии. Я бы посоветовал обработать сообщение WM_WINDOWPOSCHANGING.

Спасибо, Хью, у меня работает нижняя часть, сейчас попробую с SetParant. В основном «на рабочем столе» находится «снизу», не сворачивая, верно?

Artur Carvalho 13.12.2008 17:34

Я не уверен, что вы имеете в виду, говоря «без сворачивания», но если вы используете MS Spy ++ для изучения дерева окон (при запущенном Rainlendar), вы, надеюсь, поймете, что я имею в виду.

Hugh Allen 14.12.2008 03:21

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

   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;. См. Этот вопрос для получения дополнительной информации.

javon27 12.04.2018 01:48

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

можно добавить образец, как это использовать?

vishal 08.02.2021 06:17

Основываясь на ответах 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
}

Другие вопросы по теме