2011-09-08 5 views
28

Ho un'applicazione WPF utilizzando il modello MVVM che a volte devono mostrare un waitcursor quando si è occupato a fare qualcosa che l'utente deve aspettare. Grazie ad una combinazione di risposte in questa pagina: display Hourglass when application is busy, ho una soluzione che funziona quasi (anche se non è realmente MVVM nello spirito). Ogni volta che faccio qualcosa che richiede tempo nei miei ViewModels faccio questo:Come mostrare un waitcursor quando l'applicazione WPF è l'associazione dati occupato

using (UiServices.ShowWaitCursor()) 
{ 
.. do time-consuming logic 
this.SomeData = somedata; 
} 

(ShowWaitCursor() restituisce un IDisposable che mostra il waitcursor fino a quando non è in via di dismissione) L'ultima riga nel mio esempio è dove ho imposta alcune proprietà. Questa proprietà è associata al mio XAML, ad es. in questo modo:

<ItemsControl ItemsSource="{Binding SomeData}" /> 

Tuttavia, dal momento che questo potrebbe essere una lunga lista di oggetti e talvolta con DataTemplates complessi, ecc il legame reale e il rendering a volte prende una notevole quantità di tempo. Poiché questa associazione si svolge al di fuori della mia dichiarazione using, il waitcursor andrà via prima che l'attesa effettiva sia finita per l'utente.

Quindi la mia domanda è come fare un waitcursor in un'applicazione WPF MVVM che prende in considerazione l'associazione dati?

risposta

82

risposta di Isak non ha funzionato per me, perché non ha risolto il problema di come agire quando l'attesa attuale è finita per l'utente. Ho finito per fare questo: ogni volta che comincio a fare qualcosa di tempo, chiamo un metodo di supporto. Questo metodo di supporto cambia il cursore e quindi crea un DispatcherTimer che verrà chiamato quando l'applicazione è inattiva. Quando viene chiamato imposta il posteriore MouseCursor:

/// <summary> 
/// Contains helper methods for UI, so far just one for showing a waitcursor 
/// </summary> 
public static class UiServices 
{ 

    /// <summary> 
    /// A value indicating whether the UI is currently busy 
    /// </summary> 
    private static bool IsBusy; 

    /// <summary> 
    /// Sets the busystate as busy. 
    /// </summary> 
    public static void SetBusyState() 
    { 
      SetBusyState(true); 
    } 

    /// <summary> 
    /// Sets the busystate to busy or not busy. 
    /// </summary> 
    /// <param name="busy">if set to <c>true</c> the application is now busy.</param> 
    private static void SetBusyState(bool busy) 
    { 
      if (busy != IsBusy) 
      { 
       IsBusy = busy; 
       Mouse.OverrideCursor = busy ? Cursors.Wait : null; 

       if (IsBusy) 
       { 
        new DispatcherTimer(TimeSpan.FromSeconds(0), DispatcherPriority.ApplicationIdle, dispatcherTimer_Tick, Application.Current.Dispatcher); 
       } 
      } 
    } 

    /// <summary> 
    /// Handles the Tick event of the dispatcherTimer control. 
    /// </summary> 
    /// <param name="sender">The source of the event.</param> 
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> 
    private static void dispatcherTimer_Tick(object sender, EventArgs e) 
    { 
      var dispatcherTimer = sender as DispatcherTimer; 
      if (dispatcherTimer != null) 
      { 
       SetBusyState(false); 
       dispatcherTimer.Stop(); 
      } 
    } 
} 
+0

grande realizzazione! Questo funziona perfettamente per me quando il consumo di tempo sta accadendo in un'operazione vincolante dell'interfaccia utente di terze parti; quando non so davvero quando è finito. Grazie! –

+0

Vorrei cambiarlo per usare un conteggio piuttosto che un bool. In questo modo, più posti possono dire di aver bisogno del cursore di attesa e rimarrà finché non lo hanno abbandonato. –

+1

Bello! Grazie! – Dummy01

6

Quello che ho fatto in passato è definire le proprietà booleane nel viewmodel che indica che è in corso un lungo calcolo. Per esempio IsBusy che è impostato su true quando si lavora e falso quando è inattivo.

Poi nella vista mi legano a questo e visualizzare una barra di avanzamento o di trottola o simili, mentre questa proprietà è true. Personalmente non ho mai impostato il cursore usando questo approccio, ma non vedo perché non sarebbe possibile.

Se si desidera un controllo ancora maggiore e un semplice valore booleano non è sufficiente, è possibile utilizzare lo VisualStateManager che si guida da viewmodel. Con questo approccio puoi specificare in dettaglio come dovrebbe apparire l'interfaccia utente in base allo stato del modello di visualizzazione.

1

Oltre al contributo di Isak Savo, si potrebbe desiderare di avere uno sguardo a Brian Keating's blog per un campione di lavoro.

7

quindi non mi piace usare OverrideCursor perché ho avuto più finestre e volevo quelli che non sono stati attualmente in esecuzione qualcosa per avere il cursore normale.

Ecco la mia soluzione:

<Window.Style> 
    <Style TargetType="Window"> 
     <Style.Triggers> 
      <DataTrigger Binding="{Binding IsBusy}" Value="True"> 
       <Setter Property="Cursor" Value="Wait" /> 
      </DataTrigger> 
     </Style.Triggers> 
    </Style> 
</Window.Style> 
<Grid> 
    <Grid.Style> 
     <Style TargetType="Grid"> 
      <Style.Triggers> 
       <DataTrigger Binding="{Binding IsBusy}" Value="True"> 
        <Setter Property="IsHitTestVisible" Value="False" /> <!-- Ensures wait cursor is active everywhere in the window --> 
        <Setter Property="IsEnabled" Value="False" /> <!-- Makes everything appear disabled --> 
       </DataTrigger> 
      </Style.Triggers> 
     </Style> 
    </Grid.Style> 
    <!-- Window controls go here --> 
</Grid> 
Problemi correlati