Принесите окно в 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 с помощью таймера, он работает точно так, как ожидалось, поэтому я не уверен, почему он не работает в моем исходном случае.

Изменить. Я делаю это в сочетании с глобальной горячей клавишей.

Ответ 1

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

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

Ответ 2

myWindow.Activate();

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

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

myWindow.TopMost = true;

Ответ 3

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

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

Ответ 4

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

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

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

Ответ 5

Чтобы сделать это быстро скопируйте-вставьте один -
Используйте этот метод класса 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);
        }
    }
}

НТН

Ответ 6

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

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

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

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

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 thread
            var interopHelper = new WindowInteropHelper(w);
            var thisWindowThreadId = GetWindowThreadProcessId(interopHelper.Handle, IntPtr.Zero);

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

            //Attach this window thread to the current window 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 thread from the current window 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
    }
}

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

Ответ 7

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

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

Ответ 8

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

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

Я бы скорее сделал это, чем смоделировал 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();
}

Ответ 9

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

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

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

    • Почему? Он также сделает видимыми любые родительские окна.
  • Любой ответ, который опускает 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}">

С# - вспомогательный метод

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, как сдвинуть окно на экран, если оно выключено экран?.

Ответ 10

Ну, так как это такая горячая тема... вот что работает для меня. У меня появились ошибки, если я не сделал этого так, потому что 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();
}

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

Ответ 11

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

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

Ответ 12

Чтобы показать ANY в настоящее время открытое окно, импортируйте эти 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);

и в программе. Мы ищем приложение с указанным заголовком (записываем название без первой буквы (индекs > 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);
                }

Ответ 13

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

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

Ответ 14

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

Сначала установите активированный обработчик событий в 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();
}

Эти шаги будут хорошо работать и выведут на передний план все остальные окна в окно их родителей.

Ответ 15

Я построил метод расширения для упрощения повторного использования.

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

Ответ 16

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

    this.Hide();

спрячет его правильно, а затем просто используя

    this.Show();

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

Ответ 17

Просто хотел добавить еще одно решение этого вопроса. Эта реализация работает для моего сценария, где 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();
    }
}

Ответ 18

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

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