2011-09-08 17 views
28

Mam aplikację WPF używającą wzorca MVVM, która czasami musi pokazać kodeka, gdy jest zajęty robieniem czegoś, na co użytkownik musi czekać. Dzięki kombinacji odpowiedzi na tej stronie: display Hourglass when application is busy, mam rozwiązanie, które prawie działa (chociaż tak naprawdę nie jest to MVVM w duchu). Ilekroć coś zrobić czasochłonne w moich ViewModels zrobić to:Jak wyświetlić znacznik czasu, gdy aplikacja WPF jest zajęta Databinding

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

(ShowWaitCursor() zwraca IDisposable, który pokazuje waitcursor dopóki jest ono usuwane) Ostatni wiersz w moim przykładzie, gdzie ustaw niektóre właściwości. Ta właściwość jest powiązana z moim XAML, np. tak:

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

Jednakże, ponieważ to może być długa lista obiektów, a czasem ze złożonymi datatemplates itp rzeczywista wiążące i renderowania kiedyś zajmuje znaczną ilość czasu. Ponieważ to wiązanie ma miejsce poza moją instrukcją użycia, kelner zniknie przed faktycznym oczekiwaniem użytkownika.

Moje pytanie brzmi: jak wykonać przystawkę w aplikacji WPV MVVM, która bierze pod uwagę databinding?

Odpowiedz

82

Odpowiedź Isaka nie zadziałała, ponieważ nie rozwiązał problemu, jak zadziałać, gdy faktyczne oczekiwanie dobiegnie końca. Skończyło się na tym: Za każdym razem, gdy zaczynam robić coś czasochłonnego, wzywam metodę pomocnika. Ta metoda pomocnicza zmienia kursor, a następnie tworzy DispatcherTimer, który zostanie wywołany, gdy aplikacja jest bezczynna. Kiedy to się nazywa ustawia plecy 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

Świetna realizacja! Działa to doskonale dla mnie, gdy zużycie czasu dzieje się w operacji wiążącej UI innej firmy; kiedy nie wiem, kiedy to się robi. Dziękuję Ci! –

+0

Chciałbym to zmienić, aby użyć liczby, a nie boolu. W ten sposób wiele miejsc może powiedzieć, że potrzebują kursora oczekiwania i pozostaną, dopóki wszyscy nie zrezygnują. –

+1

Nice! Dziękuję Ci! – Dummy01

6

Co zrobiłem w przeszłości jest zdefiniowanie właściwości boolean w viewmodel, który wskazuje, że trwa długa kalkulacja. Na przykład IsBusy, która jest ustawiona na true, gdy działa, a false podczas bezczynności.

Następnie w widoku wiążę się z tym i wyświetlam pasek postępu lub pokrętło lub podobną, dopóki ta właściwość jest prawdziwa. Osobiście nigdy nie ustawiłem kursora przy użyciu tego podejścia, ale nie rozumiem, dlaczego nie byłoby to możliwe.

Jeśli chcesz uzyskać jeszcze większą kontrolę, a prosta wartość boolowska to za mało, możesz użyć modelu VisualStateManager, który pobierzesz z widoku. Dzięki takiemu podejściu możesz szczegółowo określić, w jaki sposób powinien wyglądać interfejs użytkownika w zależności od stanu modułu viewmodel.

7

Więc nie lubiłem używać OverrideCursor, ponieważ miałem wiele okien i chciałem tych, które obecnie nie wykonują czegoś, aby mieć normalny kursor strzałki.

Oto moje rozwiązanie:

<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> 
Powiązane problemy