Wie Sie Ihrer Windows 8.1 App das Benehmen beibringen

Benimmregeln für Windows 8.1 mit dem Behaviors SDK
Kommentare

Mit Windows 8.1 und Visual Studio 2013 gab es einige Verbesserungen für die Entwicklung von Windows-Store-Apps – Windows Developer hat darüber berichtet. Von vielen unbemerkt erblickte aber auch ein neues SDK das Licht der Welt: das Behaviors SDK. In ähnlicher Form gab es das schon in Silverlight und der WPF, auch dort zu Unrecht mit zu wenig Aufmerksamkeit bedacht. Grund dafür ist möglicherweise die mangelhafte Dokumentation, die MSDN könnte die Funktionalität nicht allgemeiner zum Ausdruck bringen: „… are pieces of packaged code that you can reuse to add interactivity to your apps„.

Unter MVVM-Entwicklern gibt es einen inoffiziellen Ehrenkodex: die Code-Behind-Datei bleibt unangetastet, das höchste der Gefühle ist ein Einzeiler für die Zuweisung des ViewModels an den DataContext. Wir Entwickler trennen, wo wir trennen können – und UI-spezifischer Code ist böse. Nur: manchmal reicht(e) XAML nicht aus und der ungewollte Griff zur .xaml.cs-Datei blieb nicht aus. Das Behaviors SDK löst nun viele Probleme.

Welche Probleme löst das Behaviors SDK?

Sehen wir uns drei Szenarien an, in denen das Behaviors SDK Abhilfe schaffen kann. In MVVM werden Commands dazu verwendet, eine Verbindung zwischen dem UI (z. B. einem Button) und dem auszuführenden Code (z. B. dem Laden der Daten) herzustellen. Die Deklaration kann mittels Binding in XAML erfolgen, alles ist wunderbar. Schwierig wird es aber, wenn das Command nicht beim Standardevent (z. B. Click beim Button), sondern bei anderen Events gefeuert werden soll. Was, wenn Sie auf den Doppelklick reagieren möchten? Was, wenn Sie auf mehrere Events mit unterschiedlichen Commands reagieren möchten? Es führte kaum ein Weg an der Code-Behind-Datei vorbei. Das Behaviors SDK löst dieses Problem und geht noch einen Schritt weiter: Auslöser und Aktion sind abstrahiert und damit vielfältig einsetzbar. Als Auslöser kommen nicht nur Events in Frage, sondern auch Werteänderungen können die Ursache sein (damit ist ein Verhalten ähnlich dem aus der WPF bekannten DataTrigger möglich). Als Aktion kann nicht nur ein Command ausgeführt werden, das SDK liefert beispielsweise auch Möglichkeiten zur Page-Navigation, zum Abspielen eines Sounds oder dem Ändern einer Property mit.

Nicht alle Behaviors dienen aber dazu, Aktionen zu triggern: Sie können auch dazu dienen, ein spezifisches Verhalten zu Steuerelementen hinzuzufügen. Als Beispiel sei eine TextBox erwähnt, die den eingegebenen Text automatisch in ein gewünschtes StringFormat umwandelt, sobald der Fokus wechselt.

Geschichte des Behaviors SDK

Das Behaviors SDK hat bereits eine längere Vergangenheit hinter sich, zum ersten Mal tauchte es in Microsoft Expression Blend 3.0 auf. Der ursprüngliche Gedanke war, Designern eine Möglichkeit zu geben, das gewünschte Verhalten der Anwendung konfigurieren zu können: Der Entwickler stellt ein DragAndDropBehavior zur Verfügung, der Designer entscheidet in Expression Blend, wo er dieses Interaktionskonzept einsetzen möchte.

Um Behaviors für Silverlight oder die WPF selbst erstellen zu können, wurde das Microsoft Expression Blend SDK veröffentlicht – obwohl der Name darauf hindeutet, ist keine Lizenz oder Installation der Expression-Produkte notwendig. Das SDK beinhaltet die wesentlichen Basisklassen für Action, Trigger und Behaviors.

Mit dem Erfolg von MVVM stieg die Bedeutung: Der Wunsch, möglichst die gesamte View-Definition in XAML zu schreiben, wurde durch Behaviors wahr. Somit war es für viele WPF-Entwickler ein Rückschlag, als ein entsprechendes SDK für Windows-Store-Apps vorerst fehlte. Wer Apps bauen wollte, konnte zwar XAML nutzen, aber keine Behaviors. Mit dem Start von Windows 8.1 und Visual Studio 2013 wurden die Bitten der Entwickler schließlich erhört und das neue Behaviors SDK (XAML) in Form einer Visual Studio Extension eingeführt.

Gutes Benehmen für Windows 8.1: Erste Lektion

Wie funktionieren Behaviors? Versuchen wir als erstes Beispiel, die Verbindung zwischen einem beliebigen Event und einem Command herzustellen: Wir erstellen ein Bild, das beim Doppelklick (genauer: DoubleTapped-Event) den ChangeImageCommand aufruft. Sie benötigen Visual Studio 2013, um Windows-8.1-Apps erstellen und das Behaviors SDK nutzen zu können. Erstellen Sie ein neues Windows-Store-App-Projekt (als Blank-App oder XAML) und fügen Sie eine Referenz hinzu. Unter Windows | Extensions finden Sie das „Behaviors SDK (XAML)“ bereits vorinstalliert (Abb. 1), Sie müssen es nur noch anhaken.

Abb. 1: Hinzufügen der Behaviors-SDK-Referenz

Für unser Beispiel benötigen Sie Klassen aus zwei Namespaces: Microsoft.Xaml.Interactivity und Microsoft.Xaml.Interactions.Core. Fügen Sie die entsprechenden using-Deklarationen in der XAML-Datei hinzu (Listing 1). Innerhalb des Image-Tags setzen wir die Attached Property Interaction.Behaviors und verwenden ein EventTriggerBehavior als Auslöser. Über die EventName-Property geben wir bekannt, dass wir uns für das DoubleTapped-Event interessieren. Die Aktion, die anschließend ausgeführt wird, ist innerhalb des EventTriggerBehaviors zu finden: Wir verwenden eine InvokeCommandAction und binden diese an ein Command aus dem dahinterliegenden ViewModel. Bei jedem Doppelklick wird ab sofort das Kommando ausgeführt – ohne dass eine Zeile im Code-Behind nötig ist.



  
    
      
        
      
    
  

[ header = Seite 2: Mitgelieferte Behaviors und Actions ]

Mitgelieferte Behaviors und Actions

Neben dem EventTriggerBehavior gibt es noch ein weiteres Behavior im SDK, das als Auslöser für Aktionen verwendet werden kann: das DataTriggerBehavior. Hier erfolgt die Ausführung der Aktion abhängig vom Wert eines Bindings – ähnlich einem DataTrigger aus der WPF (Listing 2). Vor allem stehen aber weitere Actions zur Verfügung:

  • CallMethodAction: ruft die angegebene Methode eines beliebigen TargetObjects auf
  • ChangePropertyAction: ändert einen Property-Wert (Listing 2)
  • GoToStateAction: verändert den VisualState eines Controls
  • InvokeCommandAction: führt das angegebene Command aus (Listing 1)
  • NavigateToPageAction: wechselt auf die angegebene Seite
  • ControlStoryboardAction: steuert (startet, pausiert, stoppt etc.) ein Storyboard (zu finden im Namespace Microsoft.Xaml.Interactions.Media)
  • PlaySoundAction: spielt den angegebenen Sound ab (ebenfalls im Namespace Microsoft.Xaml.Interactions.Media)

Die beiden Trigger Behaviors können Sie nun beliebig mit den Aktionen kombinieren, auch mehrere Aktionen und mehrere Trigger sind erlaubt. Listing 2 zeigt ein DataTriggerBehavior, das abhängig von den Werten eines Sliders Hintergrundfarben setzt und einen Sound abspielt.



  
    
      
    
    
      
      
    
  

„IncrementalUpdateBehavior“

Die dritte angesprochene Variante sind Behaviors, die nicht als Trigger fungieren, sondern ein Verhalten beschreiben, das Steuerelementen bei Bedarf hinzugefügt werden kann.

IncrementalUpdate ist solch ein Behavior, das bei Grid- und ListViews mit einer großen Anzahl von Elementen und komplexen ItemTemplates zum Einsatz kommt. Schnelles Scrollen führt bei derartigen Controls dazu, dass man kurzzeitig keine Daten mehr sieht, weil die einzelnen Items nicht so schnell gerendert werden können und daher nur noch angedeutet werden. Das macht es natürlich schwierig, einen bestimmten Eintrag in der Liste zu finden. Das IncrementalUpdateBehavior ermöglicht es nun, für die einzelnen Elemente des ItemTemplates Prioritäten zu vergeben, die beim Rendern berücksichtigt werden. Dazu muss für jedes der Elemente ein Behavior definiert und das Property Phase gesetzt werden (Listing 3). Je niedriger der Wert, desto höher die Priorität. Somit können beispielsweise zuerst die Überschriften aller Items gerendert werden, die weniger wichtigen Details folgen dann, sobald der Scrollvorgang beendet ist (Abb. 2).


  
    
      
        
          
            
          
        
        
          
            
              
            
          
          
            
              
            
          
          
            
              
            
          
        
      
    
  

Abb. 2: Dank des „IncrementalUpdateBehaviors“ werden zuerst alle Namen gerendert, dann die Bilder und erst danach der Rest (vgl. Listing 3)

[ header = Seite 3: Eigene Behaviors implementieren ]

Eigene Behaviors für Windows 8.1 implementieren

Reicht dieser Werkzeugkasten der mitgelieferten Komponenten nicht aus, um die gewünschte Funktionalität zu erzielen, besteht auch die Möglichkeit, ihn mit eigenen Implementierungen zu erweitern. Während bei der Kombination von Trigger Behaviors und Actions die meisten Anwendungsfälle mit der Standardfunktionalität bereits abdeckt sein dürften, ist vor allem die Erstellung eigener Behaviors interessant, da man damit auch sehr spezielle Verhaltensweisen umsetzen kann. Bei der Entwicklung folgt man dabei stets dem gleichen Muster: Die eigene Klasse muss von DependencyObject abgeleitet sein und das Interface IBehavior implementieren, das im Wesentlichen aus zwei Lifecycle-Methoden besteht:

public interface IBehavior
{
  DependencyObject AssociatedObject { get; }
  void Attach(DependencyObject associatedObject);
  void Detach();
}

Nach dem Erstellen des Behaviors bekommt man über die Methode Attach das associatedObject mitgeteilt, also das Element, an dem das Behavior hängt. Dieses muss über eine gleichnamige Property verfügbar gemacht werden. Meist werden in Attach auch ein oder mehrere EventHandler registriert, die schließlich das eigentliche Verhalten implementieren. Die Methode Detach wird aufgerufen, wenn das entsprechende Element nicht mehr benötigt wird. Hier kann man also die EventHandler wieder entfernen.

Listing 4 demonstriert diese Vorgehensweise an dem eingangs erwähnten Beispiel eines NumberFormatBehaviors für TextBox-Controls. Es sorgt dafür, dass eingegebene Zahlen formatiert dargestellt werden, wenn die TextBox nicht den Fokus besitzt (z. B. als Währung: 5,90 € anstelle von 5,9 bei der Eingabe eines Preises). Bekommt die TextBox jedoch den Fokus, wird wieder auf die simple Schreibweise umgestellt, um Änderungen zu vereinfachen (man muss den Cursor nicht erst zwei Zeichen nach links bewegen, bevor man die Zahl ändern kann).

In Attach wird zunächst überprüft, ob das übergebene Objekt auch wirklich eine TextBox ist. Danach werden Handler für die Events GotFocus und LostFocus definiert. Detach deregistriert diese wieder.

Die eigentliche Logik des Behaviors befindet sich in den EventHandler-Methoden (Listing 5). Dort wird versucht, den aktuellen Text zu parsen (NumberStyles.Any ermöglicht das unter anderem auch für Zahlen mit Währungssymbolen). Falls das gelingt, wird die Text-Property mit der reinen Zahl (GotFocus) oder der formatierten Variante (LostFocus) überschrieben. In der Praxis wäre vermutlich eine noch etwas komplexere Logik notwendig, beispielsweise ein expliziter Aufruf von UpdateSource vor dem Überschreiben der Zahl mit dem formatierten String, falls für die TextBox ein Binding definiert wurde.

Natürlich wäre all das auch ohne Behaviors möglich – etwa durch Erstellen einer von TextBox abgeleiteten Klasse. Der Charme des Behaviors liegt aber darin, ein Stück Code gut gekapselt und wiederverwendbar zur Verfügung zu haben. Dadurch ist es einerseits möglich, eine TextBox mit mehreren unterschiedlichen Behaviors zu versehen, andererseits kann man natürlich auch allgemeinere Behaviors entwickeln, die nicht nur für einen bestimmten Controltyp funktionieren, sondern z. B. für alle Klassen, die von FrameworkElement ableiten. Beides wäre mit Vererbung nicht lösbar. Oder, um wieder einmal eine alte Weisheit zu bemühen: „Favor object composition over inheritance“. Eine Vielzahl von interessanten Anwendungsfällen für Behaviors findet sich hier

public class NumberFormatBehavior : DependencyObject, IBehavior
{
  private TextBox textBox;

  public DependencyObject AssociatedObject { get { return textBox; } }

  public string FormatString { get; set; }

  public void Attach(DependencyObject associatedObject)
  {
    textBox = associatedObject as TextBox;

    if (textBox == null)
    throw new ArgumentException("NumberFormatBehavior can only be attached to TextBox controls.");

    textBox.GotFocus += OnGotFocus;
    textBox.LostFocus += OnLostFocus;
  }

  public void Detach()
  {
    if (textBox != null)
    {
      textBox.GotFocus -= OnGotFocus;
      textBox.LostFocus -= OnLostFocus;
      textBox = null;
    }
  }

  ...
}
private void OnGotFocus(object sender, RoutedEventArgs e)
{
  if (!string.IsNullOrEmpty(FormatString))
  {
    double value;
    if (double.TryParse(textBox.Text, NumberStyles.Any, CultureInfo.CurrentUICulture, out value))
    {
      textBox.Text = value.ToString();

      // changing the text clears the selection
      textBox.Select(0, textBox.Text.Length); 
    }
  }
}

private void OnLostFocus(object sender, RoutedEventArgs e)
{
  if (!string.IsNullOrEmpty(FormatString))
  {
    double value;
    if (double.TryParse(textBox.Text, NumberStyles.Any, CultureInfo.CurrentUICulture, out value))
    {
      textBox.Text = value.ToString(FormatString);
    }
  }
}

Fazit

Das Behaviors SDK ist nun auch für Windows-Store-Apps verfügbar und das ist gut so. Es entspricht nicht eins zu eins dem aus Silverlight und der WPF bekannten, aber derartige Abweichungen sind ja leider nichts Neues in der Welt der Windows-8-Entwicklung – manches schlechte Benehmen bleibt eben bestehen.

Behaviors schaffen keine Wunder, man könnte alles auch in Code-Behind-Dateien lösen. Aber im Sinne des MVVM-Gedankens und der Wiederverwendbarkeit bieten Behaviors sehr elegante Möglichkeiten – probieren Sie’s aus!

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -