2011-11-10 40 views
7

我遇到了自定義wpf啓動屏幕實現的問題。 問題是加載完成後應顯示MainWindow,它有時不會被帶到前面,即Activate()調用失敗。它可能發生的1/10次。應用程序在Windows7/64上運行。在啓動屏幕關閉後將主窗口置於前面

這裏是implmentation(完整的源sample

public partial class App : Application 
{ 
    private Splash _splash; 
    private SplashVM _viewModel; 

    protected override void OnStartup(StartupEventArgs e) 
    { 
     base.OnStartup(e); 

     // starts splash in separate GUI thread 
     StartSplash(); 

     // continues on loading main application in main gui thread 
     LoadMainAppFakeSteps(1000, 3); 

     // tells splash screen to start shutting down 
     Stop(); 

     // Creates mainwindow for application 
     // The problem is that the mainwindow sometimes fails to activate, 
     // even when user has not touched mouse or keyboard (i.e has not given any other programs or components focus) 
     MainWindow = new Shell(); 
     MainWindow.Show(); 
     MainWindow.Activate(); 
    } 

    private void StartSplash() 
    { 
     _viewModel = new SplashVM(); 
     var thread = new Thread(SplashThread); 
     thread.SetApartmentState(ApartmentState.STA); 
     thread.IsBackground = true; 
     thread.Start(_viewModel); 
    } 

    private void SplashThread(object vm) 
    { 
     _splash = new Splash(); 
     _splash.DataContext = vm; 
     _splash.Show(); 

     System.Windows.Threading.Dispatcher.Run(); 

     _splash = null; 
     _viewModel = null; 
    } 

    private void LoadMainAppFakeSteps(int stepDelayMs, int numSteps) 
    { 
     for (int i = 1; i <= numSteps; i++) 
     { 
      _viewModel.Text = i.ToString(); 
      Thread.Sleep(stepDelayMs); 
     } 
    } 

    private void Stop() 
    { 
     if (_splash == null) throw new InvalidOperationException("Not showing splash screen"); 

     _splash.Dispatcher.BeginInvokeShutdown(DispatcherPriority.Normal); 
    } 
} 

我嘗試這樣做:

MainWindow = new Shell(); 
MainWindow.Topmost = true; 
MainWindow.Show(); 
MainWindow.Activate(); 
MainWindow.Topmost = false; 

,並似乎工作,感謝您的建議

+1

在Windows XP上試了30次,我從未發現過這個問題。什麼操作系統你有這個問題? –

+1

我也不能重現這個問題。我正在使用Windows 7. –

+0

@Meleak Win7/64 – hkon

回答

3
MainWindow = new Shell(); 
MainWindow.Topmost = true; 
MainWindow.Show(); 
MainWindow.Activate(); 
MainWindow.Topmost = false; 
+0

運行exe,這似乎確實可以解決它,可能是通過強制竊取焦點。激活呼叫甚至不需要。 – stijn

4

Windows有一些內置的防止窗口竊取用戶當前活動線程的焦點。這意味着您只能從另一個窗口成功地獲得Activate()表單,該窗口當前有重點要提供。

例如,如果你的主應用程序窗口顯示一個對話框,windows很高興讓它具有焦點,因爲主應用程序只有焦點在同一個線程中,並可以通過它。

在啓動畫面的情況下,您的主窗體可能不是聚焦窗口,原因有幾個。無論是因爲你的啓動畫面有焦點還是因爲最終用戶開始加載你的應用程序,然後開始做其他事情(可能在玩他們的電子郵件或瀏覽器),這意味着你的應用程序根本沒有焦點。 (你可能看到任務欄中的窗口閃爍,試圖獲得焦點?)

在第一種情況下,您希望啓動屏幕執行主窗體的激活,然後關閉。 (正如先關閉閃屏然後嘗試激活主窗體一樣)。如果你搞亂了線程,你也可以使用一些win32 pinvoke來調用SetForegroundWindow()與窗口句柄。你可以通過線程傳遞窗口句柄。這將確保窗口適合您關注的請求,因爲當前重點表單正在提出請求。

這裏有偷焦點的對象,一個整潔的文章:

Windows 2000和Windows XP讓你把你的 應用程序的窗口到前面的唯一方法是,如果它是在 運行的線程是當時前景窗口的線程。所以,你必須 將應用程序的線程附加到前臺 窗口的線程,然後將應用程序的窗口放在前面。在 之後,您需要分離您的線程(這僅在用戶 將另一個應用程序的窗口作爲前臺活動窗口時纔會發生)。 來源:Force Window to Front

有人轉化這篇文章的代碼轉換爲C#在這個其他的StackOverflow職位:Steal Focus in C# on StackOverflow(看起來像的PInvoke調用一些定義缺失,但你可以用「PInvoke的」輕鬆找到這些谷歌和你想在Win32 API的名稱。)

1

除了hkon的回答,你應該檢查一下你的閃屏是通過最頂層=真偷焦點之前,在當前前臺窗口。

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

    public static bool IsForeground(this Window window) 
    { 
     var windowHandle = new WindowInteropHelper(window).Handle; 
     var foregroundWindow = GetForegroundWindow(); 
     return windowHandle == foregroundWindow; 
    } 
+0

是的,這將是有禮貌的事情=) – hkon