นำหน้าต่างไปไว้ด้านหน้าใน WPF


214

ฉันจะนำแอปพลิเคชัน 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ด้วยตัวจับเวลามันทำงานได้อย่างที่คาดหวังดังนั้นฉันไม่แน่ใจว่าทำไมมันไม่ทำงานในกรณีดั้งเดิมของฉัน

แก้ไข : ฉันกำลังทำสิ่งนี้ร่วมกับฮอตคีย์ทั่วโลก


คุณได้ตรวจสอบแล้วว่า MainWindow เป็นหน้าต่างที่คุณต้องการหรือไม่ จาก MSDN: MainWindow ถูกตั้งค่าโดยอัตโนมัติโดยอ้างอิงถึงวัตถุหน้าต่างแรกที่จะสร้างอินสแตนซ์ใน AppDomain
ทอดด์ไวท์

เป็นความคิดที่ดี แต่เป็น Window เดียวในแอปพลิเคชัน
Factor Mystic

คุณสามารถให้รหัสบริบทอีกเล็กน้อยได้หรือไม่
ทอดด์ไวท์

คำตอบ:


314
myWindow.Activate();

พยายามนำหน้าต่างไปที่พื้นหน้าและเปิดใช้งานหน้าต่าง

นั่นควรทำเคล็ดลับเว้นแต่ฉันจะเข้าใจผิดและคุณต้องการพฤติกรรมที่อยู่ด้านบนเสมอ ในกรณีนั้นคุณต้องการ:

myWindow.TopMost = true;

14
ฉันแค่ใช้ myWindow.Show () และบางครั้งมันก็ไม่ได้อยู่ด้านบน ฉันโทรไปที่ myWindow.Activate () ทันทีหลังจากนั้นและทำงานได้
Bermo

4
การเปิดใช้งานไม่ทำงานใน Windows XP ในบางครั้ง ฉันแนะนำ @Matthew Xavier คำตอบ
Lex Li

ค่อนข้างแปลกเนื่องจากโดยค่าเริ่มต้นแล้ว ShowActivated เปิดอยู่
greenoldman

1
คำตอบแรกนั้นดีขอบคุณสำหรับสิ่งนั้น! แต่บรรทัดที่สองของรหัสการใช้Topmostคุณสมบัติเป็นการปฏิบัติที่ไม่ถูกต้องเนื่องจากสามารถบดบังกล่องโต้ตอบป๊อปอัปอื่น ๆ และมีพฤติกรรมที่ไม่คาดคิด
Jonathan Perry

3
ที่จริงมันสามารถทำได้ด้วยสิ่งนี้: if (myWindow.WindowState == WindowState.Minimized) myWindow.WindowState = WindowState.Normal;แปลกมันจะรักษาหน้าต่าง maxized ใด ๆ และไม่เปลี่ยนพวกเขากลับสู่สถานะปกติ
r41n

168

ฉันได้พบวิธีแก้ปัญหาที่นำหน้าต่างขึ้นไปด้านบน แต่มันทำงานเป็นหน้าต่างปกติ:

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

1
คำใบ้ยอดเยี่ยม! TopMost ทำให้ความมหัศจรรย์เกิดขึ้นบน Windows 7 หากหน้าต่างเปิดอยู่แล้ว แต่อยู่ใต้หน้าต่างอื่น
gsb

นี่ก็เป็นการหลอกลวงสำหรับฉันเช่นกัน ขอบคุณ gsb สำหรับความคิดเห็นเพิ่มเติมเกี่ยวกับสิ่งที่ดูเหมือนจะเป็นการใช้งานแปลก ๆ ของ TopMost!
Jen

1
ขอบคุณ - การแก้ไขสั้นและหวาน
code4life

2
ในกรณีของฉัน Window.Activate () และ Window.Focus () เพียงพอ การตั้งค่า Window.TopMost ไม่จำเป็น
รุนแรง

6
Window.Focus()อย่าใช้ สิ่งนี้จะดึงโฟกัสออกจากสิ่งที่ผู้ใช้กำลังพิมพ์ในกล่องข้อความซึ่งทำให้ผู้ใช้ปลายทางหงุดหงิดอย่างเมามัน รหัสข้างต้นใช้งานได้ดีโดยไม่มีมัน
Contango

32

ในกรณีที่คุณต้องการให้หน้าต่างอยู่ข้างหน้าในครั้งแรกที่โหลดแล้วคุณควรใช้สิ่งต่อไปนี้:

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

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

1
หากคุณพัฒนาสิ่งที่คล้ายกับ Launchy ( launchy.net ) ใน C # คุณควรสังเกตว่าคำตอบนี้แทบจะไร้ประโยชน์
Lex Li

21

ในการทำให้นี่เป็น copy-paste ที่รวดเร็ว -
ใช้คลาส ' 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


6
+1 นี่คือคำตอบเดียวที่มีประโยชน์สำหรับฉัน ฉันมีแอปพลิเคชั่นที่มีเจ้านายหนึ่งคนและหน้าต่างทาสที่ลอยอยู่หลายแห่ง เมื่อเปิดใช้งานสิ่งเหล่านี้หน้าต่างอื่นทั้งหมดควรถูกนำไปด้านหน้าเช่นกัน แต่ไม่เปิดใช้งาน / รับโฟกัสตามคำตอบส่วนใหญ่แนะนำว่า: เป็นหายนะเนื่องจากทำให้หน้าต่างที่คลิกไม่สามารถคลิกได้เนื่องจากจู่ ๆ อีกหน้าต่างหนึ่งก็ได้รับโฟกัส
stijn

เหตุผลใดที่ไม่ได้ใช้process.MainWindowHandle?
Sriram Sakthivel

ในกรณีของฉันฉันไม่ต้องการที่หน้าต่างหลัก แต่ไม่เห็นด้วยมีวิธีการอื่น ๆ hWndได้รับ FWIW HwndSourceวัตถุทำงานได้ดี
tobriand

21

ฉันรู้ว่าคำถามนี้ค่อนข้างเก่า แต่ฉันเพิ่งเจอสถานการณ์ที่แม่นยำนี้และต้องการแบ่งปันโซลูชันที่ฉันนำไปใช้

ดังที่ได้กล่าวไว้ในความคิดเห็นในหน้านี้โซลูชั่นที่เสนอหลายตัวไม่สามารถใช้กับ XP ได้ซึ่งฉันต้องให้การสนับสนุนในสถานการณ์ของฉัน ในขณะที่ฉันเห็นด้วยกับความเชื่อมั่นของ @Matthew Xavier ว่าโดยทั่วไปนี่เป็นวิธีปฏิบัติ UX ที่ไม่ดี แต่ก็มีหลายครั้งที่ UX นั้นเป็นไปได้ทั้งหมด

วิธีแก้ปัญหาในการนำหน้าต่าง WPF ขึ้นไปด้านบนนั้นให้ฉันโดยใช้รหัสเดียวกับที่ฉันใช้เพื่อให้ฮอตคีย์ทั่วโลก บทความบล็อกโดย Joseph Cooneyมีลิงค์ไปยังตัวอย่างโค้ดของเขาที่มีรหัสต้นฉบับ

ฉันล้างข้อมูลและแก้ไขโค้ดเล็กน้อยแล้วนำไปใช้เป็นวิธีการเพิ่มเติมไปยัง System.Windows.Window ฉันได้ทดสอบสิ่งนี้กับ 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
    }
}

ฉันหวังว่ารหัสนี้จะช่วยให้ผู้อื่นที่ประสบปัญหานี้


เฮ้ Lookie นั่น! ฉันดิ้นรนกับสิ่งนี้มาหลายเดือนแล้ว! สิ่งนี้ใช้ได้กับทั้งสถานการณ์ของฉัน ! น่ากลัว (Windows 7 x64)
mdiehl13

ที่จริงแล้วดูเหมือนจะทำงานได้ก็ต่อเมื่อฉันทำเช่นนี้: App.mainWindow.Show (); SystemWindows.GlobalActivate (App.mainwindow); // เมื่อฉันลบ. show () ครั้งแรกมันไม่ได้นำมาไว้ด้านหน้า
mdiehl13

+1 สำหรับ SetWindowPos () ฉันกำลังมองหาวิธีที่จะนำเฉพาะหน้าต่างของฉันไปข้างหน้าโดยไม่รบกวนแอปอื่น ๆ หรือขโมยโฟกัส this.Activate () ขโมยโฟกัส
prettyvoid

สิ่งนี้ทำเพื่อฉันและในกรณีของฉันจุดทั้งหมดคือการขโมยโฟกัสเนื่องจากเกิดขึ้นเมื่อผู้ใช้โต้ตอบกับองค์ประกอบบางอย่าง ขอบคุณมากดูเหมือนว่าจะทำงานสอดคล้องกัน! แค่โทรมาthis.Activate()ดูเหมือนจะทำงานบางครั้ง
ปีเตอร์

13

หากผู้ใช้โต้ตอบกับแอปพลิเคชันอื่นอาจไม่สามารถพาคุณไปข้างหน้าได้ ตามกฎทั่วไปกระบวนการสามารถคาดหวังได้เฉพาะการตั้งค่าหน้าต่างพื้นหน้าหากกระบวนการนั้นเป็นกระบวนการพื้นหน้าอยู่แล้ว (Microsoft เอกสารข้อ จำกัด ในรายการSetForegroundWindow () MSDN) นี่เป็นเพราะ:

  1. ผู้ใช้ "เป็นเจ้าของ" เบื้องหน้า ตัวอย่างเช่นมันจะน่ารำคาญอย่างยิ่งหากโปรแกรมอื่นขโมยส่วนหน้าในขณะที่ผู้ใช้พิมพ์อย่างน้อยขัดจังหวะเวิร์กโฟลว์ของเธอและอาจทำให้เกิดผลกระทบที่ไม่ตั้งใจเนื่องจากการกดแป้นพิมพ์ของเธอมีความหมายสำหรับโปรแกรมประยุกต์หนึ่ง .
  2. ลองนึกภาพว่าแต่ละโปรแกรมสองโปรแกรมตรวจสอบเพื่อดูว่าหน้าต่างนั้นเป็นส่วนหน้าหรือไม่และพยายามตั้งให้เป็นส่วนหน้าหากไม่มี ทันทีที่มีการเรียกใช้โปรแกรมที่สองคอมพิวเตอร์จะแสดงผลไร้ประโยชน์เมื่อพื้นหน้าเด้งระหว่างสองที่ทุกงานสลับ

จุดดี. จุดประสงค์ของรหัสคือร่วมกับฮอตคีย์ทั่วโลกและแอปพลิเคชั่นอื่น ๆ ก็ทำได้เช่นกัน
ปัจจัย Mystic

ต้องใช้ PInvoke ใน C # เพื่อเลียนแบบสิ่งที่อธิบายไว้ในบทความนี้codeproject.com/Tips/76427/…
Lex Li

แล้วทำไมการแสดงออกถึงการผสมผสานข้อผิดพลาดป๊อปอัพปรากฏอยู่เมื่อฉันเปลี่ยนไปที่สตูดิโอภาพในบางครั้ง? : - /
Simon_Weaver

ไซม่อนฉันสงสัยว่าป๊อปอัปข้อผิดพลาดที่คุณเห็นคือหน้าต่าง "สูงสุด" (การตัดสินใจออกแบบที่ฉันไม่อนุมัติ) มีความแตกต่างระหว่างหน้าต่างพื้นหน้า (ซึ่งรับอินพุตผู้ใช้) และหน้าต่าง "สูงสุด" ในการสั่งซื้อ Z หน้าต่างใด ๆ สามารถทำให้ตัวเอง "สูงสุด" ซึ่งวางอยู่บนหน้าต่างที่ไม่ใช่บนสุดทั้งหมด แต่ไม่ได้ให้ความสำคัญกับแป้นพิมพ์ของหน้าต่าง ฯลฯ วิธีที่จะกลายเป็นหน้าต่างเบื้องหน้าไม่
Matthew Xavier

เคล็ดลับล้มเหลวสำหรับหน้าต่างพิเศษบางอย่าง Visual Studio และหน้าต่างพรอมต์คำสั่งต้องมีสิ่งที่ป้องกันไม่ให้หน้าต่างอื่นกลายเป็นหน้าต่างเบื้องหน้า
Lex Li

9

ฉันรู้ว่านี่เป็นคำตอบที่ล่าช้าอาจเป็นประโยชน์สำหรับนักวิจัย

 if (!WindowName.IsVisible)
 {
     WindowName.Show();
     WindowName.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}">

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 จะเปลี่ยนหน้าต่างไปยังหน้าจอได้อย่างไรถ้ามันปิดหน้าจอ .


5
"รหัสระดับเอ็นเตอร์ไพรส์" catch (Exception) { }และไม่กี่บรรทัดต่อมา ขวาใช่ ... และจะใช้รหัสที่ไม่ได้แสดงให้เห็นแม้ในคำตอบที่เหมือนหรือ_dialogService ShiftWindowOntoScreenHelperรวมทั้งขอให้สร้างหน้าต่างในมุมมองโมเดล (ซึ่งโดยทั่วไปจะแบ่งรูปแบบ MVVM ทั้งหมด) ...
Kryptos

@Kryptos นี่คือรหัสระดับองค์กร ฉันพิมพ์มันออกมาจากหน่วยความจำและเทคนิคที่แน่นอนนี้ใช้ใน บริษัท FTSE100 ขนาดใหญ่ ชีวิตจริงนั้นค่อนข้างเก่ากว่าเมื่อเทียบกับรูปแบบการออกแบบที่สมบูรณ์แบบที่เราทุกคนตั้งเป้าหมายไว้
Contango

ฉันไม่ชอบความจริงที่ว่าเราเก็บอินสแตนซ์ของหน้าต่างในตัวแบบมุมมองด้วยตัวเองตามที่ Kryptos พูดถึงที่ทำลายจุดรวมของ mvvm บางทีมันอาจทำได้ใน codebehind แทนไหม
Igor Meszaros

1
@Igor Meszaros เห็นด้วย ตอนนี้ฉันมีประสบการณ์มากขึ้นถ้าฉันต้องทำมันอีกครั้งฉันจะเพิ่มพฤติกรรมและควบคุมโดยใช้สิ่งFunc<>ที่เชื่อมโยงกับ ViewModel
Contango

7

ฉันมีปัญหาคล้ายกันกับแอ็พพลิเคชัน WPF ที่ถูกเรียกใช้จากแอ็พพลิเคชัน Access ผ่านอ็อบเจ็กต์ Shell

โซลูชันของฉันอยู่ด้านล่าง - ทำงานใน XP และ Win7 x64 พร้อมแอปที่รวบรวมไปยังเป้าหมาย x86

ฉันค่อนข้างจะทำสิ่งนี้มากกว่าจำลองแท็บ alt

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

4

เนื่องจากนี่เป็นหัวข้อที่น่าสนใจ ... นี่คือสิ่งที่ใช้ได้กับฉัน ฉันได้รับข้อผิดพลาดหากฉันไม่ได้ทำเช่นนี้เพราะ 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 () จะไม่ทำงานจนกว่าหน้าต่างจะเปิดใช้งานอยู่ ();


2

ฉันก็คิดหาวิธีแก้ไข ฉันกำลังโทรจากเบ็ดคีย์บอร์ดที่ใช้ในการติดตั้งฮอตคีย์ การโทรทำงานได้ตามที่คาดไว้ถ้าฉันวางไว้ใน 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();
}

แค่สนใจ: คุณลอง Window.Activate (ตามที่มอร์เทนแนะนำ) และคำแนะนำอื่น ๆ หรือไม่? พวกเขาดูแฮ็คน้อยกว่า kludge ที่ยอมรับนี้
Simon D.

นี้ได้ค่อนข้างชั่วขณะที่ผ่านมา แต่ใช่ในเวลาที่ฉันได้ลอง
Mystic ปัจจัย

สิ่งนี้ใช้ไม่ได้กับ Windows XP ของฉัน ฉันแนะนำ @Matthew Xavier คำตอบ
Lex Li

2

หากต้องการแสดงหน้าต่างใด ๆ ที่เปิดอยู่ในปัจจุบันให้อิมพอร์ต 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);
                }

"ชื่อแอพของคุณโดยไม่มีตัวอักษร" Oof, hacky hacky hacky ทำไมไม่ใช้IndexOfอย่างถูกต้องแทน?
การแข่งขัน Lightness ใน Orbit

1

ปัญหาอาจเป็นได้ว่าเธรดที่เรียกรหัสของคุณจาก hook ไม่ได้ถูกเตรียมใช้งานโดยรันไทม์ดังนั้นการเรียกใช้วิธีการรันไทม์ไม่ทำงาน

บางทีคุณอาจลองใช้ Invoke เพื่อจัดระเบียบรหัสของคุณไปยังเธรด UI เพื่อเรียกรหัสของคุณที่นำหน้าต่างไปสู่เบื้องหน้า


1

รหัสเหล่านี้จะทำงานได้ดีตลอดเวลา

ในขั้นแรกให้ตั้งตัวจัดการเหตุการณ์ที่เปิดใช้งานใน 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();
}

ขั้นตอนเหล่านี้จะทำงานได้ดีและจะนำหน้าต่างอื่น ๆ ทั้งหมดไปไว้ในหน้าต่างผู้ปกครอง


0

หากคุณพยายามซ่อนหน้าต่างเช่นคุณย่อขนาดหน้าต่างให้เล็กที่สุดฉันพบว่าใช้งาน

    this.Hide();

จะซ่อนอย่างถูกต้องจากนั้นเพียงใช้

    this.Show();

จะแสดงหน้าต่างเป็นไอเท็มยอดนิยมที่สุดอีกครั้ง


0

แค่ต้องการเพิ่มโซลูชันอื่นให้กับคำถามนี้ การติดตั้งใช้งานนี้สำหรับสถานการณ์ของฉันซึ่ง 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();
    }
}

0

อย่าลืมใส่รหัสที่แสดงหน้าต่างนั้นในตัวจัดการ PreviewMouseDoubleClick เนื่องจากหน้าต่างที่ใช้งานอยู่จะสลับกลับไปที่หน้าต่างที่จัดการเหตุการณ์ เพียงวางไว้ในตัวจัดการเหตุการณ์ MouseDoubleClick หรือหยุดเดือดโดยตั้งค่า e.Handled เป็น True

ในกรณีของฉันฉันจัดการ PreviewMouseDoubleClick บน Listview และไม่ได้ตั้งค่า e.Handled = true จากนั้นจะยกแม่มดเหตุการณ์ MouseDoubleClick sat sat โฟกัสกลับไปที่หน้าต่างเดิม


-1

ฉันสร้างวิธีการขยายเพื่อให้นำมาใช้ใหม่ได้ง่าย

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

สวัสดีไมค์ คุณกำลังตอบคำถามนี้ค่อนข้างช้า คุณสามารถอธิบายในคำตอบของคุณได้ไหมว่าทำไมวิธีการนี้จึงแตกต่างกัน (และอาจจะดีกว่า) จากคำตอบที่ดีที่โพสต์ไว้แล้วสำหรับคำถามนี้
Noel Widmer

ปลายเพียงเท่าที่ฉันต้องการที่จะทำเช่นนี้และฉันเจอสิ่งนี้และต้องการที่จะแบ่งปันว่าฉันแก้ปัญหาได้อย่างไรเมื่อคนอื่น ๆ ต้องการใช้มัน
Mike

แน่นอนว่าฉันเลือกที่จะตรวจสอบโพสต์ของคุณและต้องการให้คุณรับรู้ เป็นการดีที่จะให้คำตอบใหม่หากคุณคิดว่ามันเป็นคุณูปการที่ดีต่อชุมชน
Noel Widmer

2
คำถามนี้เกี่ยวกับ WPF โดยเฉพาะ แต่โซลูชันของคุณสำหรับ WinForms
Brian Reichle
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.