web-dev-qa-db-de.com

Bringen Sie in WPF ein Fenster nach vorne

Wie kann ich meine WPF-Anwendung auf die Vorderseite des Desktops bringen? Bisher habe ich versucht:

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

Keiner von ihnen erledigt den Job (Marshal.GetLastWin32Error() sagt, dass diese Vorgänge erfolgreich abgeschlossen wurden und die P/Invoke-Attribute für jede Definition SetLastError=true haben).

Wenn ich eine neue leere WPF-Anwendung erstelle und SwitchToThisWindow mit einem Timer aufrufe, funktioniert es genau wie erwartet. Ich bin mir also nicht sicher, warum es in meinem ursprünglichen Fall nicht funktioniert.

Edit : Ich mache das in Verbindung mit einem globalen Hotkey.

188
Factor Mystic

Nun, ich habe eine Arbeit gefunden. Ich mache den Anruf über einen Tastaturhaken, mit dem ein Hotkey implementiert wird. Der Anruf funktioniert wie erwartet, wenn ich ihn mit einer Pause in einen BackgroundWorker stecke. Es ist ein Klatsch, aber ich habe keine Ahnung, warum es ursprünglich nicht funktioniert hat.

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
Factor Mystic
myWindow.Activate();

Versucht, das Fenster in den Vordergrund zu bringen, und aktiviert es.

Das sollte den Trick tun, es sei denn, ich missverstehe und Sie wollen Always On Top Verhalten. In diesem Fall möchten Sie:

myWindow.TopMost = true;
277

Ich habe eine Lösung gefunden, die das Fenster nach oben bringt, aber es verhält sich wie ein normales Fenster:

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
151
Jader Dias

Wenn Sie das Fenster beim ersten Laden vor dem Fenster haben möchten, sollten Sie Folgendes verwenden:

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

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

Um dies schnell zum Kopieren und Einfügen zu machen -
Verwenden Sie die DoOnProcess-Methode dieser Klasse, um das Hauptfenster des Prozesses in den Vordergrund zu verschieben (aber nicht um den Fokus von anderen Fenstern zu stehlen) 

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

19

Ich weiß, dass diese Frage ziemlich alt ist, aber ich bin gerade auf dieses genaue Szenario gestoßen und wollte die Lösung, die ich implementiert habe, mitteilen.

Wie in den Kommentaren auf dieser Seite erwähnt, funktionieren einige der vorgeschlagenen Lösungen nicht unter XP, die ich in meinem Szenario unterstützen muss. Obwohl ich mit dem Gefühl von @Matthew Xavier einverstanden bin, dass dies im Allgemeinen eine schlechte UX-Übung ist, gibt es manchmal Zeiten, in denen es eine durchaus plausible UX ist.

Die Lösung, ein WPF-Fenster nach oben zu bringen, wurde mir tatsächlich mit demselben Code bereitgestellt, den ich für die Bereitstellung des globalen Hotkeys verwende. Ein Blogartikel von Joseph Cooney enthält einen Link zu seinen Codebeispielen der den Originalcode enthält.

Ich habe den Code ein wenig aufgeräumt und modifiziert und als Erweiterungsmethode für System.Windows.Window implementiert. Ich habe dies auf XP 32 Bit und Win7 64 Bit getestet, die beide korrekt funktionieren.

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

Ich hoffe, dieser Code hilft anderen, die auf dieses Problem stoßen.

17
Zodman

Wenn der Benutzer mit einer anderen Anwendung interagiert, können Sie Ihre Anwendung möglicherweise nicht in den Vordergrund stellen. In der Regel kann ein Prozess nur erwarten, das Vordergrundfenster zu setzen, wenn dieser Prozess bereits der Vordergrundprozess ist. (Microsoft dokumentiert die Einschränkungen im SetForegroundWindow () MSDN-Eintrag.) Dies hat folgende Gründe:

  1. Der Benutzer "besitzt" den Vordergrund. Es wäre zum Beispiel äußerst ärgerlich, wenn ein anderes Programm den Vordergrund gestohlen hat, während der Benutzer gerade seinen Workflow unterbricht, und möglicherweise unbeabsichtigte Folgen hat, da die für eine Anwendung vorgesehenen Tastatureingaben vom Täter falsch interpretiert werden, bis sie die Änderung bemerkt .
  2. Stellen Sie sich vor, dass jedes der beiden Programme prüft, ob sein Fenster der Vordergrund ist, und versucht, es in den Vordergrund zu stellen, wenn dies nicht der Fall ist. Sobald das zweite Programm ausgeführt wird, wird der Computer unbrauchbar, da der Vordergrund bei jedem Taskwechsel zwischen den beiden Programmen springt.
13
Matthew Xavier

Ich hatte ein ähnliches Problem mit einer WPF-Anwendung, die von einer Access-Anwendung über das Shell-Objekt aufgerufen wird.

Meine Lösung ist unten - funktioniert in XP und Win7 x64 mit einer auf x86-Ziel kompilierten App.

Ich würde das lieber tun, als einen Alt-Tab zu simulieren.

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

Ich weiß, dass dies eine späte Antwort ist, vielleicht hilfreich für Forscher

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

Warum sind einige der Antworten auf dieser Seite falsch?

  • Jede Antwort, die window.Focus() verwendet, ist falsch. 

    • Warum? Wenn eine Benachrichtigungsmeldung erscheint, nimmt window.Focus() den Fokus von dem ab, was der Benutzer gerade eingibt. Dies ist wahnsinnig frustrierend für Endbenutzer, insbesondere wenn die Popups recht häufig auftreten.
  • Jede Antwort, die window.Activate() verwendet, ist falsch.

    • Warum? Alle übergeordneten Fenster werden ebenfalls sichtbar.
  • Jede Antwort, die window.ShowActivated = false weglässt, ist falsch .
    • Warum? Es wird den Fokus von einem anderen Fenster wegnehmen, wenn die Nachricht erscheint, was sehr ärgerlich ist!
  • Jede Antwort, die nicht Visibility.Visible zum Ausblenden/Anzeigen des Fensters verwendet, ist falsch .
    • Warum? Wenn wir Citrix verwenden und das Fenster beim Schließen nicht minimiert wird, bleibt auf dem Bildschirm ein seltsamer schwarzer rechteckiger Griff. Daher können wir window.Show() und window.Hide() nicht verwenden.

Im Wesentlichen:

  • Das Fenster sollte den Fokus nicht von einem anderen Fenster wegnehmen, wenn es aktiviert wird.
  • Das Fenster sollte sein übergeordnetes Element nicht aktivieren, wenn es angezeigt wird.
  • Das Fenster sollte mit Citrix kompatibel sein.

MVVM-Lösung

Dieser Code ist zu 100% mit Citrix kompatibel (keine leeren Bereiche des Bildschirms). Es wird sowohl mit normalem WPF als auch mit DevExpress getestet.

Diese Antwort ist für jeden Anwendungsfall gedacht, in dem wir ein kleines Benachrichtigungsfenster haben möchten, das sich immer vor anderen Fenstern befindet (wenn der Benutzer dies in den Voreinstellungen auswählt).

Wenn diese Antwort komplexer erscheint als die anderen, liegt dies daran, dass es robuster Code auf Unternehmensebene ist. Einige der anderen Antworten auf dieser Seite sind einfach, funktionieren aber nicht.

XAML - angefügte Eigenschaft

Fügen Sie diese angefügte Eigenschaft einer beliebigen UserControl innerhalb des Fensters hinzu. Die angefügte Eigenschaft wird:

  • Warten Sie, bis das Loaded-Ereignis ausgelöst wird (andernfalls kann der visuelle Baum nicht nach dem übergeordneten Fenster gesucht werden).
  • Fügen Sie einen Ereignishandler hinzu, der sicherstellt, dass das Fenster sichtbar ist oder nicht.

Sie können das Fenster jederzeit an der Vorderseite einstellen oder nicht, indem Sie den Wert der angefügten Eigenschaft umdrehen.

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

Helfer-Methode

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".
        }
    }
}

Verwendungszweck

Um dies zu verwenden, müssen Sie das Fenster in Ihrem ViewModel erstellen:

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

Zusätzliche Links

Tipps, wie Sie sicherstellen können, dass ein Benachrichtigungsfenster immer wieder auf dem sichtbaren Bildschirm angezeigt wird, finden Sie in meiner Antwort: Wie verschiebt man in WPF ein Fenster auf den Bildschirm, wenn es außerhalb des Bildschirms ist? .

5
Contango

Nun, da dies ein so heißes Thema ist ... hier ist was für mich funktioniert. Ich habe Fehler bekommen, wenn ich es nicht so gemacht habe, weil Activate () einen Fehler ausgibt, wenn Sie das Fenster nicht sehen.

Xaml:

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

Codebehind:

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

Dies war die einzige Möglichkeit, das Fenster oben zu zeigen. Aktivieren Sie es dann, damit Sie das Feld eingeben können, ohne den Fokus mit der Maus einstellen zu müssen. control.Focus () funktioniert nur, wenn das Fenster Active () ist.

4
Omzig

Um ein aktuell geöffnetes Fenster anzuzeigen, importieren Sie diese 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);

und im Programm Wir suchen nach App mit angegebenem Titel (Titel ohne Anfangsbuchstaben schreiben (Index> 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);
                }
2
rahmud

Das Problem könnte sein, dass der Thread, der Ihren Code über den Hook aufruft, nicht von der Laufzeit initialisiert wurde, sodass aufrufende Laufzeitmethoden nicht funktionieren.

Vielleicht können Sie versuchen, eine Invoke auszuführen, um Ihren Code an den UI-Thread zu marshallen und Ihren Code aufzurufen, der das Fenster in den Vordergrund bringt.

1
joshperry

Diese Codes funktionieren immer einwandfrei.

Legen Sie zunächst den aktivierten Ereignishandler in XAML fest:

Activated="Window_Activated"

Fügen Sie Ihrem Hauptfenster-Konstruktorblock die folgende Zeile hinzu:

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

Kopieren Sie diese Codes in den aktivierten Ereignishandler:

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

Diese Schritte funktionieren gut und bringen alle anderen Fenster in das Fenster der Eltern.

1
Matrix

Wenn Sie versuchen, das Fenster auszublenden, beispielsweise das Fenster zu minimieren, habe ich das mit gefunden 

    this.Hide();

wird es richtig verstecken, dann einfach mit 

    this.Show();

das Fenster wird dann wieder als oberstes Element angezeigt.

0
Chris

Ich wollte nur eine andere Lösung zu dieser Frage hinzufügen. Diese Implementierung funktioniert für mein Szenario, in dem CaliBurn für die Anzeige des Hauptfensters verantwortlich ist.

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();
    }
}
0
d.moncada

Denken Sie daran, den Code, der dieses Fenster anzeigt, nicht in einen PreviewMouseDoubleClick-Handler einzufügen, da das aktive Fenster zum Fenster zurückkehrt, das das Ereignis behandelt hat. Fügen Sie es einfach in den MouseDoubleClick-Ereignishandler ein oder beenden Sie das Bubbling, indem Sie e.handled auf True setzen. 

In meinem Fall behandelte ich den PreviewMouseDoubleClick für eine Listview und setzte nicht e.Handled = true. Dann wurde das MouseDoubleClick-Ereignis ausgelöst, das den Fokus wieder auf das ursprüngliche Fenster setzte.

0
Michel P.

Ich baute eine Erweiterungsmethode, um die Wiederverwendung zu erleichtern.

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

Rufen Sie den Form Constructor auf 

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