c# - eingabefenster - wpf modal dialog



Bringe in WPF ein Fenster nach vorne (12)

Warum einige der Antworten auf dieser Seite falsch sind!

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

    • Warum? Wenn eine Benachrichtigung window.Focus() wird, wird window.Focus() den Fokus weg von dem window.Focus() was der Benutzer gerade eingibt. Dies ist wahnsinnig frustrierend für Endbenutzer, besonders wenn die Popups ziemlich häufig auftreten.
  • Jede Antwort, die window.Activate() ist falsch.

    • Warum? Es wird auch alle übergeordneten Fenster sichtbar machen.
  • Jede Antwort, die window.ShowActivated = false auslässt, ist falsch.
    • Warum? Es wird den Fokus weg von einem anderen Fenster greifen, wenn die Nachricht erscheint, was sehr ärgerlich ist!
  • Jede Antwort, die Visibility.Visible nicht verwendet, um das Fenster zu verbergen / anzuzeigen, ist falsch.
    • Warum? Wenn wir Citrix verwenden, bleibt das Fenster beim Schließen des Fensters nicht kollabiert und hinterlässt auf dem Bildschirm einen merkwürdigen schwarzen rechteckigen Halt. Daher können wir window.Hide() und window.Hide() .

Im Wesentlichen:

  • Das Fenster sollte den Fokus nicht von einem anderen Fenster entfernen, wenn es aktiviert wird.
  • Das Fenster sollte sein Elternelement nicht aktivieren, wenn es angezeigt wird.
  • Das Fenster sollte mit Citrix kompatibel sein.

MVVM-Lösung

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

Diese Antwort ist für jeden Anwendungsfall gedacht, bei dem ein kleines Benachrichtigungsfenster immer vor anderen Fenstern angezeigt werden soll (wenn der Benutzer dies in den Einstellungen auswählt).

Wenn diese Antwort komplexer erscheint als die anderen, liegt das daran, dass es sich um einen robusten Code auf Unternehmensebene handelt. Einige der anderen Antworten auf dieser Seite sind einfach, funktionieren aber nicht.

XAML - Angehängte Eigenschaft

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

  • Warten Sie, bis das Loaded Ereignis ausgelöst wird (andernfalls kann es die visuelle Struktur nicht nachschlagen, um das übergeordnete Fenster zu finden).
  • Fügen Sie einen Ereignishandler hinzu, der sicherstellt, dass das Fenster sichtbar ist oder nicht.

Zu jedem Zeitpunkt können Sie das Fenster davor setzen oder nicht, indem Sie den Wert der angehängten Eigenschaft spiegeln.

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

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

Verwendung

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 den sichtbaren Bildschirm verschoben wird , finden Sie in meiner Antwort: Wie verschiebt man in WPF ein Fenster auf den Bildschirm, wenn es nicht auf dem Bildschirm angezeigt wird? .

Wie kann ich meine WPF-Anwendung auf den Desktop bringen? Bisher habe ich es 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);

Keine davon macht den Job ( Marshal.GetLastWin32Error() sagt, dass diese Operationen erfolgreich abgeschlossen wurden, und die P / Invoke-Attribute für jede Definition haben SetLastError=true ).

Wenn ich eine neue leere WPF-Anwendung erstellen und SwitchToThisWindow mit einem Zeitgeber aufrufen, funktioniert es genau wie erwartet, so dass ich nicht sicher bin, warum es in meinem ursprünglichen Fall nicht funktioniert.

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


Answer #1

Das Problem könnte sein, dass der Thread, der den Code vom Hook aufruft, nicht von der Laufzeit initialisiert wurde, so dass das Aufrufen von Laufzeitmethoden nicht funktioniert.

Vielleicht könnten Sie versuchen, eine Invoke durchzuführen, um Ihren Code im UI-Thread zu marshalieren, um Ihren Code aufzurufen, der das Fenster in den Vordergrund bringt.


Answer #2

Diese Codes funktionieren alle Male einwandfrei.

Setzen Sie zunächst den aktivierten Event-Handler in XAML:

Activated="Window_Activated"

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

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

Und innerhalb des aktivierten Event-Handlers kopieren Sie diese Codes:

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 ihrer Eltern.


Answer #3

Ich habe eine Erweiterungsmethode für die einfache Wiederverwendung erstellt.

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 Formularkonstruktor auf

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

Answer #4

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 App auf x86 Ziel kompiliert.

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

Answer #5

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

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

Answer #6

Ich wollte nur eine weitere Lösung für diese Frage hinzufügen. Diese Implementierung funktioniert für mein Szenario, in dem CaliBurn für die Anzeige des Hauptfensters zuständig 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();
    }
}

Answer #7

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

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 für mich, das Fenster oben zu zeigen. Aktivieren Sie es dann, damit Sie das Feld eingeben können, ohne den Fokus mit der Maus zu setzen. control.Focus () funktioniert nur, wenn das Fenster Active () ist;


Answer #8

Um ALLE derzeit geöffneten 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 suchen wir 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);
                }

Answer #9

Um dies zu einem schnellen Kopieren-Einfügen machen -
Verwenden Sie diese Klasse ' DoOnProcess Methode, um das Hauptfenster des Prozesses zu verschieben' in den Vordergrund (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


Answer #10

Wenn Sie versuchen, das Fenster zu verbergen, zum Beispiel minimieren Sie das Fenster, ich habe festgestellt, dass mit

    this.Hide();

wird es richtig verstecken, dann einfach benutzen

    this.Show();

Das Fenster wird dann erneut als das oberste Element angezeigt.


Answer #11

Wenn der Benutzer mit einer anderen Anwendung interagiert, kann es möglicherweise nicht möglich sein, Sie in den Vordergrund zu bringen. In der Regel kann ein Prozess nur erwarten, dass das Vordergrundfenster gesetzt wird, wenn dieser Prozess bereits der Vordergrundprozess ist. (Microsoft dokumentiert die Einschränkungen in dem MSDN-Eintrag SetForegroundWindow() ). Das liegt daran, dass:

  1. Der Benutzer "besitzt" den Vordergrund. Zum Beispiel wäre es extrem ärgerlich, wenn ein anderes Programm den Vordergrund stehlen würde, während der Benutzer tippt, zumindest ihren Arbeitsablauf unterbricht und möglicherweise unbeabsichtigte Konsequenzen verursacht, da ihre für eine Anwendung bestimmten Tastenanschläge von der Täterin falsch interpretiert werden, bis sie die Änderung bemerkt .
  2. Stellen Sie sich vor, dass jedes von zwei Programmen überprü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 nutzlos, da der Vordergrund bei jedem Taskwechsel zwischen den beiden springt.




pinvoke