2012-04-12 6 views
18

Szenario: Benutzer klickt auf eine Schaltfläche in der Ansicht Dies ruft einen Befehl auf dem ViewModel, DoProcessing Wie, und wo wird der Wait-Cursor gesetzt, unter Berücksichtigung der Verantwortlichkeiten von View und ViewModel?MVVM Wait Cursor wie wird der wait-Cursor beim Aufruf eines Befehls gesetzt?

Nur um klar zu sein, ich bin nur auf der Suche nach dem DEFAULT-Cursor in eine Sanduhr ändern, während der Befehl ausgeführt wird. Wenn der Befehl abgeschlossen ist, wechselt der Cursor zurück in einen Pfeil. (Es ist eine synchrone Operation, die ich suche, und ich möchte die UI blockieren).

Ich habe eine IsBusy-Eigenschaft auf dem ViewModel erstellt. Wie stelle ich sicher, dass sich der Mauszeiger der Anwendung ändert?

Antwort

24

ich bin mit erfolgreich in meiner Anwendung:

/// <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, System.Windows.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(); 
     } 
    } 
} 

Dies wurde von here genommen. Courtsey huttelihut.

Sie müssen die Methode SetBusyState jedes Mal aufrufen, wenn Sie denken, dass Sie einen zeitaufwendigen Vorgang ausführen möchten. z.B.

Dadurch wird der Cursor automatisch in den Wartecursor versetzt, wenn die Anwendung besetzt ist, und im Ruhezustand auf Normal zurückgestellt.

+0

Die Verbindung zu Huttelihut ist gut. – user1328350

+0

Wie würden Sie es mit einer Fortschrittsleiste verwenden? (Bsp .: Modernui Fortschrittsbalken) – sexta13

+0

@ sexta13 - Nicht sicher, wie das funktioniert. Kann versuchen, Ihnen zu helfen, wenn Sie einen Link zu einer Beispielanwendung posten und beschreiben können, wie sie funktioniert. –

2

Befehl auf dem View-Modell behandelt, so dass die vernünftige decission wäre Folowing zu tun:

1) Erstellen Sie einen Besetztanzeige Service und injizieren sie in das Ansichtsmodell (dies ermöglicht es Ihnen, die Cursor Logik ersetzen leicht mit irgendwelchen fiesen Animation)

2) In der Befehlshandler die Besetztanzeige Dienst rufen dem Benutzer

ich könnte falsch zu informieren, aber es sieht aus wie Sie einige schwere Berechnungen oder ich versuchen zu tun/O auf UI-Thread. Ich empfehle Ihnen dringend, in diesem Fall Arbeiten am Thread-Pool durchzuführen. Sie können Task und TaskFactory verwenden, um die Arbeit einfach mit ThreadPool

1

Es gibt eine große Session von Laurent Bugnion online (Creator of). Theres auch ein aviable.

In mindestens einer von ihnen leben er Codes ein beschäftigt Indikator mit einem ist BusyProperty.

+0

Vereinbaren IsBusy-Eigenschaft auf ViewModel. – GazTheDestroyer

+0

Wie wird dies auf dem Hauptfenster angezeigt, im Gegensatz zu nur auf der Ansicht, die dem Ansichtsmodell entspricht, d.h. die Ansicht ist ein Kind der Maon-Shell. – user1328350

0

IMHO, dass es vollkommen in Ordnung ist, dass sich die Warte-Cursor-Logik neben dem Befehl im Ansichtsmodell befindet.

Um den Cursor am besten zu ändern, erstellen Sie einen IDisposable Wrapper, der die Mouse.OverrideCursor Eigenschaft ändert.

public class StackedCursorOverride : IDisposable 
{ 
    private readonly static Stack<Cursor> CursorStack; 

    static StackedCursorOverride() 
    { 
     CursorStack = new Stack<Cursor>(); 
    } 

    public StackedCursorOverride(Cursor cursor) 
    {    
     CursorStack.Push(cursor); 
     Mouse.OverrideCursor = cursor;    
    } 

    public void Dispose() 
    { 
     var previousCursor = CursorStack.Pop(); 
     if (CursorStack.Count == 0) 
     { 
      Mouse.OverrideCursor = null; 
      return; 
     } 

     // if next cursor is the same as the one we just popped, don't change the override 
     if ((CursorStack.Count > 0) && (CursorStack.Peek() != previousCursor)) 
      Mouse.OverrideCursor = CursorStack.Peek();    
    } 
} 

Verbrauch:

using (new StackedCursorOverride(Cursors.Wait)) 
{ 
    // ... 
} 

Das Vorstehende ist eine überarbeitete Version der Lösung, die ich auf diese question geschrieben.

+6

Ich bin mir nicht so sicher. Ein Warte-Cursor ist nur eine Implementierung eines Besetzt-Indikators. Das ViewModel sollte nicht über die View-Implementierung wissen. Was wäre, wenn die Ansicht auf eine andere Art und Weise wie etwa eine Animation oder eine einfache Textnachricht auf Aktivität hinweisen würde? Mit einer einfachen IsBusy-Eigenschaft kann die Ansicht implementieren, wie sie es möchte. – GazTheDestroyer

+0

@GazTheDestroyer: Das ist wahr, dachte nie so. – Dennis

10

Eine sehr einfache Methode besteht darin, einfach an die Eigenschaft 'Cursor' des Fensters (oder eines anderen Steuerelements) zu binden. Zum Beispiel:

XAML:

<Window 
    x:Class="Example.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    Cursor="{Binding Cursor}" /> 

Ansichtsmodell Cursor-Eigenschaft (Mit Apex.MVVM):

private NotifyingProperty cursor = new NotifyingProperty("Cursor", typeof(System.Windows.Input.Cursor), System.Windows.Input.Cursors.Arrow); 
    public System.Windows.Input.Cursor Cursor 
    { 
     get { return (System.Windows.Input.Cursor)GetValue(cursor); } 
     set { SetValue(cursor, value); } 
    } 

Dann einfach den Cursor in der Ansicht ändern, wenn nötig ...

public void DoSomethingLongCommand() 
    { 
     Cursor = System.Windows.Input.Cursors.Wait; 

     ... some long process ... 

     Cursor = System.Windows.Input.Cursors.Arrow; 
    } 
+0

Ich denke, Sie müssen NotifyingProperty zu DependencyProperty ändern – Sasha

+1

Hinweis: Ich verwende die Apex-Bibliothek (http://apex.codeplex.com/). Ich definiere den Cursor als eine Eigenschaft in meinem ViewModel. Ich mache es zu einer NotifyingProperty, damit es die Ansicht bei Änderungen anzeigen kann. Keine Notwendigkeit für eine DependencyProperty hier. – bradcarman

+0

Meine Schuld, tut mir leid – Sasha

0
private static void LoadWindow<T>(Window owner) where T : Window, new() 
{ 
    owner.Cursor = Cursors.Wait; 
    new T { Owner = owner }.Show(); 
    owner.Cursor = Cursors.Arrow; 
} 
Verwandte Themen