2011-11-10 17 views
7

Me he encontrado con un problema con una implementación de wpf splash-screen personalizada. El problema es que una vez que finaliza la carga y se muestra la ventana principal, a veces no se lleva al frente, es decir, falla la llamada Activate(). Sucede tal vez 1/10 veces. La aplicación se ejecuta en Windows7/64.Llevar la ventana principal al frente después de que la pantalla secundaria se apague

Aquí es el implmentation (fuente completo 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); 
    } 
} 

He intentado esto:

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

y parece que funciona, gracias a todas sus sugerencias

+1

Lo intenté 30 veces con Windows XP y nunca me di cuenta del problema. ¿En qué SO tienes este problema? –

+1

No puedo reproducir el problema también. Estoy usando Windows 7. –

+0

@Meleak Win7/64 – hkon

Respuesta

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

, parece que lo arregla, probablemente forzando el robo de foco. la llamada Activate ni siquiera es necesaria. – stijn

4

Windows tiene algunas de ellas construidas en protección para evitar que Windows robe el foco del hilo actualmente activo del usuario. Esto significa que solo puede con éxito Activate() un formulario de otra ventana que actualmente tiene el foco para dar.

Por ejemplo, si la ventana de la aplicación principal muestra un cuadro de diálogo, Windows está feliz de que tenga foco porque la aplicación principal solo tiene el foco en el mismo hilo y, por lo tanto, puede pasarlo.

En el caso de la pantalla de bienvenida, su formulario principal puede no ser la ventana enfocada por varias razones. Ya sea porque su pantalla de inicio tiene foco o porque el usuario final comenzó a cargar su aplicación y luego comenzó a hacer otra cosa (tal vez jugando con su correo electrónico o navegador) lo que significa que su aplicación no tiene foco en absoluto. (¿Puede ver el destello de la ventana en la barra de tareas tratando de enfocar?)

En el primer caso, desea que su pantalla de inicio active el formulario principal y luego cierre. (Como se propuso cerrar primero la pantalla de bienvenida y luego intentar activar la forma principal). Si está jugando con hilos, también puede recurrir al uso de win32 pinvoke para llamar al SetForegroundWindow() con el controlador de ventana. Puede pasar el controlador de ventana alrededor de los hilos. Esto asegurará que Windows sea compatible con su solicitud de atención, ya que un formulario enfocado actualmente está realizando la solicitud.

Aquí es un artículo de ordenada en el tema de robo de enfoque:

La única forma en que Windows 2000 y Windows XP permiten traer su ventana de aplicación hasta la parte delantera es si el hilo se está ejecutando en es el hilo de la ventana de primer plano en el momento. Entonces, tiene que adjuntar el hilo de su aplicación al hilo de la ventana del primer plano y luego llevar la ventana de su aplicación al frente. Después de , debe separar el hilo (esto solo ocurre si el usuario tiene la ventana de otra aplicación como ventana activa de primer plano). Fuente:Force Window to Front

Alguien convierte el código de este artículo a C# que en este otro post StackOverflow: Steal Focus in C# on StackOverflow (Parece que algunas definiciones para llamadas PInvoke faltan, pero se pueden encontrar éstos fácilmente en Google con "PInvoke" y el nombre de la API de Win32 que desee.)

1

Además de Håkon de respuesta que debe comprobar si su pantalla de presentación es la ventana actual en primer plano antes de robar el foco a través TopMost = true.

[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

Sí, eso sería lo correcto para hacer =) – hkon

Cuestiones relacionadas