Вывести окно на передний план в WPF


как я могу вывести свое приложение WPF на переднюю часть рабочего стола? До сих пор я пробовал:

SwitchToThisWindow(new WindowInteropHelper(Application.Current.MainWindow).Handle, true);

SetWindowPos(new WindowInteropHelper(Application.Current.MainWindow).Handle, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

SetForegroundWindow(new WindowInteropHelper(Application.Current.MainWindow).Handle);

ни одно из которых не делает работу (Marshal.GetLastWin32Error() говорит, что эти операции завершены успешно, и атрибуты P / Invoke для каждого определения имеют SetLastError=true).

если я создаю новое пустое приложение WPF и вызываю SwitchToThisWindow с таймером он работает точно так, как ожидалось, поэтому я не уверен, почему он не работает в моем оригинале случай.

Edit: я делаю это в сочетании с глобальной горячей клавише.

18 181

18 ответов:

myWindow.Activate();

пытается вывести окно на передний план и активирует его.

Это должно сделать трюк, если я не понял неправильно, и вы хотите всегда на вершине поведения. В таком случае вы хотите:

myWindow.TopMost = true;

Я нашел решение, которое выводит окно наверх, но оно ведет себя как обычное окно:

if (!Window.IsVisible)
{
    Window.Show();
}

if (Window.WindowState == WindowState.Minimized)
{
    Window.WindowState = WindowState.Normal;
}

Window.Activate();
Window.Topmost = true;  // important
Window.Topmost = false; // important
Window.Focus();         // important

Если вам нужно, чтобы окно было впереди при первой загрузке, вы должны использовать следующее:

private void Window_ContentRendered(object sender, EventArgs e)
{
    this.Topmost = false;
}

private void Window_Initialized(object sender, EventArgs e)
{
    this.Topmost = true;
}

чтобы сделать это быстрое копирование-вставить один -
Используйте этот класс'DoOnProcess метод перемещения главного окна процесса на передний план (но не для кражи фокуса из других окон)

public class MoveToForeground
{
    [DllImportAttribute("User32.dll")]
    private static extern int FindWindow(String ClassName, String WindowName);

    const int SWP_NOMOVE        = 0x0002;
    const int SWP_NOSIZE        = 0x0001;            
    const int SWP_SHOWWINDOW    = 0x0040;
    const int SWP_NOACTIVATE    = 0x0010;
    [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
    public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);

    public static void DoOnProcess(string processName)
    {
        var allProcs = Process.GetProcessesByName(processName);
        if (allProcs.Length > 0)
        {
            Process proc = allProcs[0];
            int hWnd = FindWindow(null, proc.MainWindowTitle.ToString());
            // Change behavior by settings the wFlags params. See http://msdn.microsoft.com/en-us/library/ms633545(VS.85).aspx
            SetWindowPos(new IntPtr(hWnd), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE);
        }
    }
}

HTH

Я знаю, что этот вопрос довольно старый, но я только что столкнулся с этим точным сценарием и хотел поделиться решением, которое я реализовал.

Как уже упоминалось в комментариях на этой странице, некоторые из предложенных решений не работают на XP, которые мне нужно поддерживать в моем сценарии. Хотя я согласен с мнением @Matthew Xavier, что в целом это плохая практика UX, бывают случаи, когда это полностью правдоподобный UX.

решение для приведения окна WPF к верхняя часть была фактически предоставлена мне тем же кодом, который я использую для предоставления глобальной горячей клавиши. статья в блоге Джозефа Куни содержит ссылка на его примеры кода, который содержит исходный код.

я очистил и немного изменил код, и реализовал его как метод расширения для системы.Окна.Окно. Я проверил это на XP 32 бит и Win7 64 бит, оба из которых работают правильно.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Interop;
using System.Runtime.InteropServices;

namespace System.Windows
{
    public static class SystemWindows
    {
        #region Constants

        const UInt32 SWP_NOSIZE = 0x0001;
        const UInt32 SWP_NOMOVE = 0x0002;
        const UInt32 SWP_SHOWWINDOW = 0x0040;

        #endregion

        /// <summary>
        /// Activate a window from anywhere by attaching to the foreground window
        /// </summary>
        public static void GlobalActivate(this Window w)
        {
            //Get the process ID for this window's thread
            var interopHelper = new WindowInteropHelper(w);
            var thisWindowThreadId = GetWindowThreadProcessId(interopHelper.Handle, IntPtr.Zero);

            //Get the process ID for the foreground window's thread
            var currentForegroundWindow = GetForegroundWindow();
            var currentForegroundWindowThreadId = GetWindowThreadProcessId(currentForegroundWindow, IntPtr.Zero);

            //Attach this window's thread to the current window's thread
            AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, true);

            //Set the window position
            SetWindowPos(interopHelper.Handle, new IntPtr(0), 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);

            //Detach this window's thread from the current window's thread
            AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, false);

            //Show and activate the window
            if (w.WindowState == WindowState.Minimized) w.WindowState = WindowState.Normal;
            w.Show();
            w.Activate();
        }

        #region Imports

        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

        [DllImport("user32.dll")]
        private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

        [DllImport("user32.dll")]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        #endregion
    }
}

Я надеюсь, что этот код поможет другим, кто столкнуться с этой проблемой.

Если пользователь взаимодействует с другим приложением, это не может быть возможно, чтобы привести ваш на фронт. Как правило, процесс может ожидать установки окна переднего плана только в том случае, если этот процесс уже является процессом переднего плана. (Microsoft документирует ограничения в SetForegroundWindow() запись MSDN.) Это потому, что:

  1. пользователь "владеет" передним планом. Например, было бы крайне неприятно, если бы другая программа украла передний план в то время как пользователь печатает, по крайней мере, прерывая ее рабочий процесс и, возможно, вызывая непреднамеренные последствия, поскольку ее нажатия клавиш, предназначенные для одного приложения, неправильно интерпретируются нарушителем, пока она не заметит изменение.
  2. представьте, что каждая из двух программ проверяет, является ли ее окно передним планом, и пытается установить его на передний план, если это не так. Как только вторая программа запущена, компьютер становится бесполезным, так как передний план отскакивает между двумя каждый переключатель задач.

У меня была аналогичная проблема с приложением WPF, которое вызывается из приложения Access через объект оболочки.

мое решение ниже-работает в XP и Win7 x64 с приложением, скомпилированным в x86 target.

Я бы предпочел сделать это, чем имитировать alt-tab.

void Window_Loaded(object sender, RoutedEventArgs e)
{
    // make sure the window is normal or maximised
    // this was the core of the problem for me;
    // even though the default was "Normal", starting it via shell minimised it
    this.WindowState = WindowState.Normal;

    // only required for some scenarios
    this.Activate();
}

Я знаю, что это поздний ответ, может быть полезно для исследователей

 if (!WindowName.IsVisible)
 {
     WindowName.Show();
     WindowName.Activate();
 }

почему некоторые ответы на этой странице не правы!

  • любой ответ, который использует window.Focus() - это неправильно.

    • почему? Если появляется уведомление,window.Focus() будет захватить фокус от того, что пользователь набирает в то время. Это безумно расстраивает конечных пользователей, особенно если всплывающие окна происходят довольно часто.
  • любой ответ, который использует window.Activate() is неправильный.

    • почему? Это также сделает видимыми любые родительские окна.
  • любой ответ, который не window.ShowActivated = false - это неправильно.
    • почему? Он будет захватывать фокус от другого окна, когда появляется сообщение, которое очень раздражает!
  • любой ответ, который не использует Visibility.Visible Скрыть/показать окно-это неправильно.
    • почему? Если мы используем Citrix, если окно не свернуто, когда оно есть закрытый, он оставит странное черное прямоугольное удержание на экране. Таким образом, мы не можем использовать window.Show() и window.Hide().

по сути:

  • окно не должно захватывать фокус от любого другого окна, когда оно активируется;
  • окно не должно активировать свой родитель, когда он показан;
  • окно должно быть совместимо с Citrix.

решение MVVM

этот код 100% совместимость с Citrix (без пустых областей экрана). Он тестируется как с обычным WPF, так и с DevExpress.

этот ответ предназначен для любого случая использования, когда мы хотим небольшое окно уведомления, которое всегда находится перед другими окнами (если пользователь выбирает это в настройках).

если этот ответ кажется более сложным, чем другие, это потому, что это надежный код уровня предприятия. Некоторые из других ответов на этой странице просты, но не на самом деле работа.

XAML-вложенное свойство

добавьте это вложенное свойство к любому UserControl в окне. Присоединенное свойство будет:

  • подождите, пока Loaded событие запускается (в противном случае он не может искать визуальное дерево, чтобы найти родительское окно).
  • добавить обработчик событий, который гарантирует, что окно видимым или нет.

в любой момент, Вы можете установить окно, чтобы быть впереди или нет, перевернув значение из присоединенного имущества.

<UserControl x:Class="..."
         ...
         attachedProperties:EnsureWindowInForeground.EnsureWindowInForeground=
             "{Binding EnsureWindowInForeground, Mode=OneWay}">

C# - Вспомогательный Метод

public static class HideAndShowWindowHelper
{
    /// <summary>
    ///     Intent: Ensure that small notification window is on top of other windows.
    /// </summary>
    /// <param name="window"></param>
    public static void ShiftWindowIntoForeground(Window window)
    {
        try
        {
            // Prevent the window from grabbing focus away from other windows the first time is created.
            window.ShowActivated = false;

            // Do not use .Show() and .Hide() - not compatible with Citrix!
            if (window.Visibility != Visibility.Visible)
            {
                window.Visibility = Visibility.Visible;
            }

            // We can't allow the window to be maximized, as there is no de-maximize button!
            if (window.WindowState == WindowState.Maximized)
            {
                window.WindowState = WindowState.Normal;
            }

            window.Topmost = true;
        }
        catch (Exception)
        {
            // Gulp. Avoids "Cannot set visibility while window is closing".
        }
    }

    /// <summary>
    ///     Intent: Ensure that small notification window can be hidden by other windows.
    /// </summary>
    /// <param name="window"></param>
    public static void ShiftWindowIntoBackground(Window window)
    {
        try
        {
            // Prevent the window from grabbing focus away from other windows the first time is created.
            window.ShowActivated = false;

            // Do not use .Show() and .Hide() - not compatible with Citrix!
            if (window.Visibility != Visibility.Collapsed)
            {
                window.Visibility = Visibility.Collapsed;
            }

            // We can't allow the window to be maximized, as there is no de-maximize button!
            if (window.WindowState == WindowState.Maximized)
            {
                window.WindowState = WindowState.Normal;
            }

            window.Topmost = false;
        }
        catch (Exception)
        {
            // Gulp. Avoids "Cannot set visibility while window is closing".
        }
    }
}

использование

для того, чтобы использовать это, вам нужно создать окно в вашем ViewModel:

private ToastView _toastViewWindow;
private void ShowWindow()
{
    if (_toastViewWindow == null)
    {
        _toastViewWindow = new ToastView();
        _dialogService.Show<ToastView>(this, this, _toastViewWindow, true);
    }
    ShiftWindowOntoScreenHelper.ShiftWindowOntoScreen(_toastViewWindow);
    HideAndShowWindowHelper.ShiftWindowIntoForeground(_toastViewWindow);
}

private void HideWindow()
{
    if (_toastViewWindow != null)
    {
        HideAndShowWindowHelper.ShiftWindowIntoBackground(_toastViewWindow);
    }
}

Дополнительные ссылки

советы о том, как убедиться, что окно уведомлений всегда смещается обратно на видимый экран, см. В моем ответе:в WPF, как переместить окно на экран, если он находится вне экрана?.

Ну, так это такая горячая тема... вот что работает для меня. Я получил ошибки, если я не сделал этого таким образом, потому что Activate() будет ошибка на вас, если вы не можете видеть окно.

Xaml:

<Window .... 
        Topmost="True" 
        .... 
        ContentRendered="mainWindow_ContentRendered"> .... </Window>

Codebehind:

private void mainWindow_ContentRendered(object sender, EventArgs e)
{
    this.Topmost = false;
    this.Activate();
    _UsernameTextBox.Focus();
}

Это был единственный способ для меня, чтобы получить окно, чтобы показать на вершине. Затем активируйте его, чтобы вы могли ввести в поле, не устанавливая фокус с помощью мыши. управление.Фокус() не будет работать, если окно не является активным ();

Ну я придумал обойти. Я делаю вызов с клавиатуры крюк, используемый для реализации горячей клавиши. Вызов работает, как ожидалось, если я положил его в BackgroundWorker с паузой. Это Клудж, но я понятия не имею, почему он не работал изначально.

void hotkey_execute()
{
    IntPtr handle = new WindowInteropHelper(Application.Current.MainWindow).Handle;
    BackgroundWorker bg = new BackgroundWorker();
    bg.DoWork += new DoWorkEventHandler(delegate
        {
            Thread.Sleep(10);
            SwitchToThisWindow(handle, true);
        });
    bg.RunWorkerAsync();
}

чтобы показать любое открытое в данный момент окно, импортируйте эти DLL:

public partial class Form1 : Form
{
    [DllImportAttribute("User32.dll")]
    private static extern int FindWindow(String ClassName, String WindowName);
    [DllImportAttribute("User32.dll")]
    private static extern int SetForegroundWindow(int hWnd);

и в программе мы ищем приложение с указанным названием (написать название без первой буквы (индекс > 0))

  foreach (Process proc in Process.GetProcesses())
                {
                    tx = proc.MainWindowTitle.ToString();
                    if (tx.IndexOf("Title of Your app WITHOUT FIRST LETTER") > 0)
                    {
                        tx = proc.MainWindowTitle;
                        hWnd = proc.Handle.ToInt32(); break;
                    }
                }
                hWnd = FindWindow(null, tx);
                if (hWnd > 0)
                {
                    SetForegroundWindow(hWnd);
                }

проблема может быть в том, что поток, вызывающий ваш код из крючка, не был инициализирован средой выполнения, поэтому вызывающие методы среды выполнения не работают.

возможно, вы могли бы попробовать сделать вызов, чтобы маршалировать свой код на поток пользовательского интерфейса, чтобы вызвать свой код, который выводит окно на передний план.

эти коды будут работать нормально все время.

сначала установите активированный обработчик событий в XAML:

Activated="Window_Activated"

добавить строку ниже в блок конструктора главного окна:

public MainWindow()
{
    InitializeComponent();
    this.LocationChanged += (sender, e) => this.Window_Activated(sender, e);
}

и внутри активированного обработчика событий скопируйте этот код:

private void Window_Activated(object sender, EventArgs e)
{
    if (Application.Current.Windows.Count > 1)
    {
        foreach (Window win in Application.Current.Windows)
            try
            {
                if (!win.Equals(this))
                {
                    if (!win.IsVisible)
                    {
                        win.ShowDialog();
                    }

                    if (win.WindowState == WindowState.Minimized)
                    {
                        win.WindowState = WindowState.Normal;
                    }

                    win.Activate();
                    win.Topmost = true;
                    win.Topmost = false;
                    win.Focus();
                }
            }
            catch { }
    }
    else
        this.Focus();
}

эти шаги будут отлично работать и приведут к появлению всех других окон в окне родителей.

Если вы пытаетесь скрыть окно, например вы свернули окно, я обнаружил, что с помощью

    this.Hide();

скроет его правильно, а затем просто с помощью

    this.Show();

затем снова отобразит окно как самый верхний элемент.

просто хотел добавить еще одно решение этого вопроса. Эта реализация работает для моего сценария, где CaliBurn отвечает за отображение главного окна.

protected override void OnStartup(object sender, StartupEventArgs e)
{
    DisplayRootViewFor<IMainWindowViewModel>();

    Application.MainWindow.Topmost = true;
    Application.MainWindow.Activate();
    Application.MainWindow.Activated += OnMainWindowActivated;
}

private static void OnMainWindowActivated(object sender, EventArgs e)
{
    var window = sender as Window;
    if (window != null)
    {
        window.Activated -= OnMainWindowActivated;
        window.Topmost = false;
        window.Focus();
    }
}

Не забудьте поместить код, который показывает это окно внутри обработчика PreviewMouseDoubleClick, поскольку активное окно переключится обратно в окно, которое обрабатывало событие. Просто поместите его в обработчик событий MouseDoubleClick или прекратите пузыриться, установив e.Handled в True.

в моем случае я обрабатывал PreviewMouseDoubleClick на Listview и не устанавливал e.Handled = true, тогда он поднял событие MouseDoubleClick witch sat focus обратно в исходное окно.

Я создал метод расширения, чтобы сделать для легкого повторного использования.

using System.Windows.Forms;
    namespace YourNamespace{
        public static class WindowsFormExtensions {
            public static void PutOnTop(this Form form) {
                form.Show();
                form.Activate();
            }// END PutOnTop()       
        }// END class
    }// END namespace

вызовите конструктор формы

namespace YourNamespace{
       public partial class FormName : Form {
       public FormName(){
            this.PutOnTop();
            InitalizeComponents();
        }// END Constructor
    } // END Form            
}// END namespace