head.WriteLine()

Mittwoch, Mai 25, 2011

TT.UIA: Command Behavior

Wie hier bereits beschrieben, eignen sich Behaviors sehr gut um Problemstellungen rund um MVVM zu lösen. Ein häufiges Problem in diesem Zusammenhang ist der Umgang mit Commands. Sie können lediglich Buttons (und bei WPF auch Menu Items) zugewiesen werden – alle anderen UI-Elemente verfügen nicht über eine Command-Eigenschaft. Zudem wird der Command immer nur über das Click-Event ausgelöst. Auch bezieht sich der Status eines Command stets auf die IsEnabled-Eigenschaft des jeweiligen Controls. All diese Limitationen versucht TT.UIA mit dem Command Behavior zu lösen.

Das Command Behavior

Die Klasse CommandBehavior leitet von TriggerAction<T> ab und kann somit auf beliebige Elemente und deren Events angewendet werden. Beispiel:
<Button>
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="Click">
      <behaviors:CommandBehavior
        Command="{Binding OkCommand}"
        PropertyName="Visiblity"
        CanExecutePropertyValue="Visible"
        CanNotExecutePropertyValue="Collapsed" />
    </i:EventTrigger>
  </i:Interaction.Triggers>
</Button>

Hier wurde einem Button der CommandBehavior zugewiesen. Bei welchem Event das Command ausgeführt werden soll wurde hierbei über die EventName-Eigenschaft der Klasse EventTrigger angegeben.
Die Bindung zum Command kann daraufhin wie gewohnt über die Eigenschaften Command bzw. CommandParameter zugewiesen werden. Zusätzlich kann über die Eigenschaften PropertyName, CanExecutePropertyValue und ExecutePropertyValue angegeben werden, welche Eigenschaft des zugehörigen Elements auf welchen Wert gesetzt werden soll, wenn der Command aktiv bzw. inaktiv ist. Sollte für das Setzen des Eigenschaftenwertes eine Konvertierung erforderlich sein, kann über die Converter-Eigenschaft ein entsprechender Value Converter angegeben werden.
Darüber hinaus bietet CommandBehavior die Eigenschaft CommandAction. Dies ist ein shortcut für den Fall, dass sich der Status des Commands lediglich auf die Eigenschaft IsEnabled oder Visibility auswirken sollen. Hierbei muss CommandAction lediglich auf den Wert Hide oder Disable gesetzt werden. Hierbei kann die explizite Angabe über PropertyName, CanExecutePropertyValue und ExecutePropertyValue entfallen. Beispiel:
<Button>
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="Click">
      <behaviors:CommandBehavior
        Command="{Binding OkCommand}"
        CommandAction="Hide" />
    </i:EventTrigger>
  </i:Interaction.Triggers>
</Button>

So, das war es erstmal mit meiner Blog-Serie zu TT.UIA. Als nächstes steht die Silverlight-Portierung an. Stay tuned!

Labels:

Dienstag, Mai 24, 2011

TT.UIA: Adorner Factory Behaviors

Wie hier und hier bereits beschrieben, haben Expression Blend Behaviors ein paar Limitationen. Hierzu zählt zum Beispiel die Tatsache, dass sie nicht so flexibel wie Adorner sind. So können sie zum Beispiel nur das zugewiesene Element selbst manipulieren und keine zusätzlichen Elemente einfügen. Um diese Limitationen zu umgehen, bietet TT.UIA die Klasse AdornerFactoryBehavior.
Mit ihr kann ein beliebiger Adorner in Form eines Behaviors zugewiesen werden. Beispiel:
<TextBox>
  <i:Interaction.Behaviors>
    <behaviors:AdornerFactoryBehavior
AdornerTypeName="Thinktecture.UIAnnotations.ElementTrackerAdorner" />
  </i:Interaction.Behaviors>
</TextBox>

Dies hat den Vorteil, dass der Adorner sehr einfach mit Expression Blend zugewiesen werden kann.
AdornerFactoryBehaviorInBlend
Darüber hinaus ist es auch möglich, einen Adorner mehrfach dem gleichen Element zuzuweisen, was mit der Attached Property-Syntax nicht möglich ist.
Die meisten Adorner von TT.UIA verfügen jedoch über zusätzliche Eigenschaften, die nicht generisch über AdornerFactoryBehavior zugewiesen werden können. Daher bietet es sich an, eine entsprechende Ableitung zu erstellen. Ein Beispiel hierfür ist die Klasse CueBannerBehavior. Sie kapselt den CueBannerAdorner in dem sie von AdornerFactoryBehavior ableitet und die Eigenschaft CueBannerText bereitstellt.
public class CueBannerAdornerFactoryBehavior
: AdornerFactoryBehavior
{
  protected override Adorner CreateAdorner()
  {
    var adorner = new CueBannerAdorner(this.AssociatedObject);
    adorner.CueBanner = this.CueBannerText;
    return adorner;
  }
  public static readonly DependencyProperty
    CueBannerTextProperty = DependencyProperty.Register(
      "CueBannerText", typeof(string),
      typeof(CueBannerAdornerFactoryBehavior),
      new PropertyMetadata("", 
        OnCueBannerTextPropertyChanged));
  public string CueBannerText
  {
    get { return (string)GetValue(CueBannerTextProperty); }
    set { SetValue(CueBannerTextProperty, value); }
  }
  private static void OnCueBannerTextPropertyChanged(
    DependencyObject sender,
    DependencyPropertyChangedEventArgs e)
  {
    ((CueBannerAdornerFactoryBehavior)sender)
    .OnCueBannerTextChanged(sender, e);
  }
  private void OnCueBannerTextChanged(
    DependencyObject sender,
    DependencyPropertyChangedEventArgs e)
  {
    if (this.Adorner != null && e.NewValue != null)
      ((CueBannerAdorner)this.Adorner)
      .CueBanner = e.NewValue.ToString();
  }
}

Hier wurde zunächst die Methode CreateAdorner() der Basisklasse überschieben und hierin der CueBannerAdorner erstellt. Die Property CueBannerText muss nun lediglich an den Adorner durchgereicht werden.
Im nächsten Post geht es mal nicht um grafische Effekte, sondern den sinnvollen Einsatz von Behaviors in MVVM-Szenarien.

Labels:

Montag, Mai 23, 2011

TT.UIA: Behavior Factories

Wie hier bereits beschrieben, haben Expression Blend Behaviors ein paar Limitationen. Hierzu zählt zum Beispiel die Tatsache, dass sie nicht per Style zugewiesen werden können. Um diese Limitation zu umgehen, bietet TT.UIA die Behavior Factories.

Die Klasse BehaviorFactory

Behaviors werden zwar auch über Attached Properties zugewiesen, können aber nicht über einen Style zugewiesen werden. Dies ist per Design so und führt bei jedem Versuch zu einer Exception. Behaviors lassen sich jedoch per Code zuweisen. Diese Möglichkeit nutzt die Klasse BehaviorFactory. Sie stellt die Attached Property BehaviorTypeName zu Verfügung, über die die Typbezeichnung des Behaviors angegeben werden kann. Der so angegebene Behavior-Typ wird daraufhin instanziiert und per Code an das entsprechende Element gebunden.
In folgendem Beispiel wird das Focus Behavior mit Hilfe von BehaviorFactory innerhalb eines Styles zugewiesen:
<Window>
<Window.Resources>
<Style TargetType="TextBox">
<Setter Property="behaviors:BehaviorFactory.BehaviorTypeName" Value="Thinktecture.UIAnnotations.Behaviors.FocusBehavior" />
</Style>
</Window.Resources>
...
</Window>

Da der Style implizit definiert wurde, werden daraufhin alle Textboxen des Fensters mit dem Behavior ausgestattet, ohne das diese explizit zugewiesen werden müssen.
In diesem Fall kann es jedoch vorkommen, das eine bestimmte Textbox des Fensters nicht mit dem Behavior ausgestattet werden soll. Hierfür bietet BehaviorFactory die Attached Property DetachBehaviorTypeName. Sie unterbindet für das zugehörige Element ist Zuweisung des über einen Style definierten Behaviors.
<TextBox
behaviors:BehaviorFactory.DetachBehaviorTypeName=
"Thinktecture.UIAnnotations.Behaviors.FocusBehavior" />

TriggerActionFactory und TargetedTriggerActionFactory

Wenn das betreffende Behavior nicht von Behavior<T>, sondern von TriggerAction<T> oder TargetedTriggerAction<T> ableitet, muss zusätzlich noch der Name des auslösenden Events, bzw. das Zielelement angegeben werden. Hierfür bietet TT.UIA die spezialisierten Klassen TriggerActionFactory und TargetedTriggerActionFactory.
Im nächsten Post geht es ebenfalls um Behaviors. Hier werde ich die Klasse AdornerFactoryBehavior vorstellen, die verwendet werden kann, um beliebige Adorner per Behavior zuzuweisen.

Labels:

Samstag, Mai 21, 2011

Slides von der BASTA! ON TOUR

Die erste BASTA! ON TOUR in diesem Jahr hat richtig Spaß gemacht! Hier die Slides meiner Sessions:

Freitag, Mai 20, 2011

TT.UIA: Behaviors

Wie hier bereits beschrieben, haben Expression Blend Behaviors gegenüber Attached Properties einige Vorteile. Hierzu zählt vor allem die Designer-Unterstützung in Blend. Ein weiterer Vorteil ist jedoch auch die Tatsache, dass Behaviors mit mehreren Elementen verbunden werden können.
Ein einfaches Beispiel:Ein Dialog enthält eine Textbox/Button-Kombination zur Auswahl einer Datei.
OpenFileBehavior
Klassischerweise würde man hier im Code-Behind den Dateidialog öffnen und die Auswahl in die daneben stehende Textbox übertragen. Da dies eine Funktionalität ist, die häufiger benötigt wird, bietet es sich an sie zu kapseln. Der Code-Behind-Ansatz ist jedoch auch in MVVM-Szenarien ein Problem. Das Öffnen des Dialogs möchte man jedoch auch nicht im ViewModel hinterlegen, da es sich um UI-Code handelt.

Das Open File Behavior

Mit einem Behavior lässt sich solch eine Funktionalität sehr komfortabel abbilden. TT.UIA bietet hierfür die Klasse OpenFileBehavior. Sie leitet von TargettetTriggerAction<T> ab und kann somit nicht nur ein Element (der Button im oberen Beispiel), sondern auch an ein Zielelement (die Textbox) gebunden werden.
...
<Button
  xmlns:i=
    "clr-namespace:System.Windows.Interactivity;
    assembly=System.Windows.Interactivity"
  xmlns:behaviors=
    "clr-namespace:Thinktecture.UIAnnotations.Behaviors;
     assembly=Thinktecture.UIAnnotations">

  <i:Interaction.Triggers>
    <i:EventTrigger EventName="Click">
      <behaviors:OpenFileBehavior
        TargetName="imageTextBox"
        DialogTitle="Select an image..."
        Filter="PNG Files|*.png" />
    </i:EventTrigger>
  </i:Interaction.Triggers>

</Button>

<TextBox x:Name="imageTextBox" />
...

Die Deklaration erfolgt hierbei über die Klasse System.Windows.Interactivity.Interaction, die Teil des Expression Blend SDKs ist.

Das Busy Behavior

Ein weiteres Problem in MVVM-Anwendungen ist der Umgang mit Cursorn und anderen Fortschrittsanzeigen. Denn das ViewModel sollte weder explizit einen Cursors setzen, noch ein entsprechendes UI-Element direkt steuern. In Silverlight löst man dies meist mit dem beliebten (wenn auch hässlichen) BusyIndicator, die per Data Binding an das ViewModel gebunden wird. Hierfür muss die Komponente jedoch zur Entwurfszeit in das Layout des jeweiligen Fensters so eingefügt werden, dass sie im Vordergrund steht. Dies verursacht bei nachträglichen Layout-Änderungen häufig Probleme. Zudem sollte die Bedienung des Fensters verhindert werden, sobald der BusyIndicator aktiv wird.
All diese Herausforderungen versucht die Klasse *BusyBehavior* zu lösen. Sie zeigt eine überdimensionale Eieruhr und legt dabei einen Overlay über das zugehörige Fenster um die Bedienung der Oberfläche durch den Benutzer zu verhindern. Das Busy Behavior kann mit einem beliebigen Element verknüpft werden. Es ermittelt zur Laufzeit das übergeordnete Fenster und fügt sich auf oberster Ebene in den visuellen Baum ein.
<i:Interaction.Behaviors>
  <behaviors:BusyBehavior
    IsBusy="{Binding IsBusy}" />
</i:Interaction.Behaviors>

Die Steuerung erfolgt über die Eigenschaft *IsBusy*, die zum Beispiel an eine entsprechende Eigenschaft des zugehörigen ViewModels gebunden werden kann. Das Ganze sieht dann zur Laufzeit in etwas so aus:
BusyBehavior

Das Dialog Button Behavior

Die Klasse DialogButtonBehavior steuert das Verhalten von Dialogschaltflächen. Klassischerweise hat ein Dialog einen Accept- und einen Cancel-Button, womit der Benutzer das Fenster mit Enter bestätigen, bzw. mit ESC abbrechen kann. Hierbei wird das Fenster geschlossen und die DialogResult-Eigenschaft entsprechend gesetzt. All dies fasst DialogButtonBehavior zusammen, wie das folgende Beispiel zeigt:
<Button>
  <i:Interaction.Behaviors>
    <behaviors:DialogButtonBehavior ButtonType="Ok" />
  </i:Interaction.Behaviors>
</Button>

Über die Eigenschaft ButtonType kann das entsprechende Verhalten definiert werden.

Das Focus Behavior

Die Klasse FocusBehavior markiert das fokussierte Eingabeelement mit einer beliebigen Hintergrundfarbe.
<TextBox>
  <i:Interaction.Behaviors>
    <behaviors:FocusBehavior HighlightBrush="Yellow" />
  </i:Interaction.Behaviors>
</TextBox>

FocusBehavior
So, das war’s erstmal mit den Behaviors von TT.UIA. Im nächsten Post gehe ich auf die Behavior Factories ein, mit denen einige Limitationen der Behaviors behoben werden können.

Labels:

Donnerstag, Mai 19, 2011

TT.UIA: Generic Adorner

Wie hier bereits beschrieben, bringt TT.UIA eine Reihe fertiger Adorner mit. Unter ihnen ist auch der Generic Adorner, mit dem beliebige Elemente als Adorner definiert und mit anderen Elementen verknüpft werden können.
Die Klasse GenericAdorner dient als Platzhalter für beliebigen Content, der frei positioniert werden kann. Die Zuweisung erfolgt hingegen über die Klasse GenericAdorners, welche entsprechende Attached Properties für die Konfiguration bereitstellt.
Das folgende Beispiel demonstriert die Verwendung:
<Window ...>
  <Window.Resources>
    <ToolBar x:Key="adornerToolbar">
      <Button ToolTip="Cut" Command="Cut">
        <Image Source="/Images/Cut.png" Stretch="None" />
      </Button>
      <Button ToolTip="Copy" Command="Copy">
        <Image Source="/Images/Copy.png" Stretch="None" />
      </Button>
      <Button ToolTip="Paste" Command="Paste">
        <Image Source="/Images/Paste.png" Stretch="None" />
      </Button>
    </ToolBar>
  </Window.Resources>

  <TextBox
    am:GenericAdorners.AdornerElement="{StaticResource adornerToolbar}"
    am:GenericAdorners.VerticalAlignment="Top"
    am:GenericAdorners.HorizontalAlignment="Right"
    am:GenericAdorners.Placement="VerticalOuterHorizontalInner"
    am:GenericAdorners.Margin="0,0,0,0"
    am:GenericAdorners.Visible="True"
    am:GenericAdorners.VisibleOnFocus="True" />

</Window>

Hier wurde zunächst eine Toolbar im Resource Dictionary des Fensters definiert, welche als Adorner einer Textbox zugewiesen werden soll. Die Zuweisung erfolgt über die Attached Property GenericAdorners.AdornerElement. Für die Positionierung stehen die Properties HorizontalAlignment, VerticalAlignment und Placement zu Verfügung. Placement gibt hierbei an, ob sich die Position auf den inneren oder äußeren Rahmen des jeweiligen Controls beziehen soll. Die folgende Abbildung zeigt die möglichen Kombinationen:
GenericAdornerPlacement
Über die Eigenschaft VisibleOnFocus kann zusätzlich festgelegt werden, ob der Adorner dynamisch eingeblendet werden soll, wenn das zugehörige Element den Fokus erhält.
In den Beispielen zu TT.UIA ist eine kleine Demo enthalten, die den Einsatz von GenericAdorner zeigt.
GenericAdornerDemo
Das war’s erstmal mit den Adornern. Im nächsten Post geht es um die Behaviors in TT.UIA.

Labels:

Mittwoch, Mai 18, 2011

TT.UIA: Data Annotation Support

Wie ich hier bereits beschrieben habe, können die Adorner von TT.UIA sehr leicht per Style zugewiesen werden. Doch neben dieser existiert noch eine weitere Möglichkeit die Zuweisung zu automatisieren.
TT.UIA bietet mit Annotations.AttachDataAnnotations eine Attached Property, die auf Root-Ebene zugewiesen wird. Wird sie auf true gesetzt, werden alle Elemente des Fensters oder des User Controls durchlaufen und dessen Datenbindungen analysiert. TT.UIA prüft hierbei, ob die Datenquelle der jeweiligen Bindung über Data Annotations verfügt. Ist dem so, werden für bestimmte Annotations entsprechenden Adorner gesetzt. Hier ein einfaches Beispiel für eine annotierte Datenquelle:
using System.ComponentModel.DataAnnotations;
public class Person
{
  [Required]
  [Display(Prompt = "Enter the full name!")]
  public string Name { get; set; }
}

Hier wurde die Eigenschaft Name als Pflichtfeld markiert und ein entsprechender Prompt-Text hinterlegt.
Das Markup des Fensters könnte nun zum Beispiel wie folgt aussehen:
<Window uia:ApplyDataAnnotations="True">
  <TextBox Text="{Binding Person.Name}" />
</Window>

Das Ergebnis sähe hierbei wie folgt aus:
ApplyDataAnnotations
Für das Required-Attribut wurde der Required-Adorner und für den Prompt-Text der CueBanner-Adorner zugewiesen.
Darüber hinaus werden die folgenden Data Annotations von TT.UIA unterstützt:
  • EditableAttribute.AllowEdit
    Setzt die IsEnabled-Eigenschaft des zugehörigen Elements.
  • RequiredAttribute
    Verknüpft das zugehörige Element mit dem Required Adorner.
  • StringLengthAttribute.MaximumLength
    Setzt die MaxLength-Eigenschaft des zugehörigen Eingabesteuerelements (TextBox, ComboBox oder DatePicker).
  • DisplayFormatAttribute.DataFormatString
    Das Format wird dem Binding-Objekt des zugehörigen Elements zugewiesen.
  • DisplayFormatAttribute.NullDisplayText
    Verknüpft das zugehörige Element mit dem CueBanner Adorner.
Darüber hinaus bietet die Annotations-Klasse mit der Attached Property DescriptionDisplayControl die Möglichkeit, Beschreibungstexte die über DisplayAttribute.Description zugewiesen wurden, in einem beliebigen Control anzuzeigen. Beispiel:
Datenquelle:
using System.ComponentModel.DataAnnotations;
public class Person
{
  [Display(Description="The name of the person.")]
  [Required]
  [Display(Prompt = "Enter the full name!")]
  public string Name { get; set; }
}

Markup:
<Window uia:ApplyDataAnnotations="True"
  uia:Annotations.DescriptionDisplayControl="{Binding ElementName=descriptionLabel}"
  uia:Annotations.DescriptionDefaultText="Ready.">
  …
</Window>

Ergebnis:
DescriptionDisplayControl
Die Anzeige erfolgt hierbei beim Erhalt des Fokus. Welchen Text das Statuselement anzeigen soll, wenn kein gebundenes Element den Fokus hat, kann über die Eigenschaft Annotations.DescriptionDefaultText festgelegt werden.
Im nächsten Post geht es um den Generic Adorner, mit dem beliebige UI-Elemente als Adorner an andere Element “angehängt“ werden können.

Labels:

Dienstag, Mai 17, 2011

TT.UIA: Adorner

Nachdem ich im letzten Post die Funktionsweise von TT.UIA vorgestellt habe, soll es nun um die einzelnen Adorner und ihre Anwendung gehen. Doch bevor es losgeht, erkläre ich - zum besseren Verständig - das Prinzip der WPF Adorner noch einmal in ein paar Worten.

WPF Adorner

Bei Adornern handelt es sich um grafische Erweiterungen, die zur Laufzeit mit beliebigen Elementen des visuellen Baums verbunden werden können. Hierbei zeichnet der Adorner über die Oberfläche des zugehörigen Controls, bzw. erweitert dieses um zusätzliche Elemente. Adorner sind Teil von WPF und wurden ursprünglich für die Abbildung von Drag & Drop-Funktionalität entwickelt. Sie eignen sich jedoch auch gut für punktuelle Erweiterungen. So werden sie beispielsweise auch bei der Validierungsfunktionalität von WPF verwendet.

TT.UIA-Adorner

Für die Zuweisung von Adornern bietet TT.UIA die zentrale Klasse Annotations. Sie bietet eine Reihe von Attached Properties, die zur Erstellung von Konfiguration dienen. Beispiel:
<TextBox 
  uia:Annotations.ShowElementTracker="True"
  uia:Annotations.TrackerImageAlignment="Left" />

ElementTracker
Hier wurde der ElementTracker-Adorner zugewiesen und über die Eigenschaft TrackerImageAlignment positioniert.

Adorner per Style zuweisen

Die Zuweisung per Attached Property hat den Vorteil, dass kein zusätzlicher Code erforderlich ist und sich die Adorner auch per Style zuweisen lassen. Beispiel:
<Style TargetType="{x:Type TextBox}">
  <Setter Property="uia:Annotations.ShowElementTracker"
          Value="True" />
  <Setter Property="uia:Annotations.TrackerImageAlignment"
          Value="Left" />
</Style>

Hier wurde ein impliziter Style definiert, der automatisch allen Textboxen den ElementTracker-Adorner zuweist.

Die Adorner-Sammlung von TT.UIA

TT.UIA bietet eine Reihe vorgefertigter Adorner. Hier eine kurze Auflistung der entsprechenden Attached Properties mit Beschreibung:
  • HighlightOnFocus markiert das fokussierte Element mit einem weichen Rahmen.
    HighlightOnFocus
  • IsRequired markiert ein Pflichtfeld, welches über RequiredImageAlignment positioniert werden kann.
    Required
  • ShowEditTracker markiert Felder, dessen Wert vom Benutzer geändert wurden. Die Position kann über TrackerImageAlignment festgelegt werden.
    EditTracker
  • ShowElementTracker zeigt ein Symbol vor oder hinter dem aktuell fokussierten Element an. Die Position kann über TrackerImageAlignment festgelegt werden.
    ElementTracker
  • AttachErrorTemplate verknüpft das Control mit einem Error Template, das im Fehlerfall angezeigt wird.
    ErrorTemplate
  • AttachEditContextMenu verknüpft ein Eingabesteuerelement mit einem erweiterten Editor-Context-Menü.
    EditContextMenu
  • AttachEditorToolbar verknüpft ein Eingabesteuerelement mit einer Editor-Toolbar.
    EditorToolbar
  • CueBanner weist dem zugewiesenen Control ein Wasserzeichentext zu.
    CueBanner
  • AttachSmartTag weist dem zugewiesenen Control einen Smart Tag zu. Dessen Menü muss hierbei über die Eigenschaft SmartTagMenu zugewiesen werden.
    SmartTag
Eine detaillierte Beschreibung aller Adorner findet ihr in der Dokumentation von TT.UIA, die Implementierungsdetails werde ich in Kürze in der dotnetpro veröffentlichen.
Im nächsten Post geht es um die Möglichkeit Adorner automatisch per Data Annotations zuzuweisen.

Labels:

Montag, Mai 16, 2011

TT.UIA: Einführung

Die Idee hinter thinktecture UIAnnotations (TT.UIA) ist es, UI-Interaktionslogik zu kapseln und diese deklarativ an vorhandene Steuerelemente “anzuhängen”. Der Vorteil liegt hierbei in der flexiblen Anwendbarkeit. Wenn man zum Beispiel eine Textbox mit einem Wasserzeichentext (CueBanner) ausstatten will, gibt es mehrere Möglichkeiten.
  • Templating: Man erstellt ein Custom Template für die Textbox. Dies ist kein leichtes Unterfangen und ohne Code nur sehr schwer zu realisieren. Zudem muss das Template explizit oder implizit zugewiesen werden.
  • Vererbung: Hierbei wird eine Ableitung von Textbox erstellt und mit einem Custom Template verknüpft. Bei diesem Ansatz ist alles zwar schön gekapselt, jedoch funktioniert dies nur für Textboxen – Comboboxen, DatePicker und co. sind hiervon natürlich nicht betroffen. Daher müsste man den Vorgang für diese Controls wiederholen. Und wenn dann noch weitere Features implementiert werden sollen, ist man schnell dabei für die komplette Toolbox Ableitungen zu erstellen.
Noch schwieriger wird es, wenn die Erweiterungen nicht nur das Control selbst betreffen. Wenn zum Beispiel neben allen Input Controls, die an Pflichtfelder gebunden sind, ein entsprechendes Symbol erscheinen soll, müssten für alle Control-Typen Custom- oder User Controls erstellt werden.
Aus diesen (und vielen weiteren) Gründe liegt es nahe, die entsprechende Funktionalität mehr oder weniger Control-neutral abzubilden und diese dann im jeweiligen Fall an das gewünschte Control “anzuhängen”. Beispiel:
<TextBox uia:CueBanner="Enter your name!" />
CueBanner
In diesem Beispiel wird von TT.UIA ein WPF Adorner erzeugt, der direkt auf die Oberfläche des entsprechenden Controls zeichnet. Dies hat den Vorteil, dass keine Ableitung des Controls erforderlich ist. Zudem kann der Adorner auch außerhalb des Controls zeichnen, was eine leichte Kennzeichnung von Felder (Pflichtfeld, …) ermöglicht.
Für das “Anhängen” von UI-Funktionalität bietet TT.UIA zwei verschiedene Ansätze: Attached Properties und Expression Blend Behaviors. Beide Varianten haben Ihre Vor- und Nachteile.
  • Attached Properties bestechen durch ihre Einfachheit. Wie im oberen Beispiel dargestellt, können Sie sehr leicht an ein beliebiges Control angehängt werden. Doch der eigentliche Vorteil – gegenüber Behaviors – ist, dass sie auch per Style zugewiesen werden können.
  • Expression Blend Behaviors sind vor allem für Designer bzw. alle die vornehmlich in Expression Blend entwickeln interessant. Denn Blend bietet eine sehr gute Designer-Unterstützung für Behaviors. XAML-lastige Entwickler mögen sie wegen ihrer ausladenden Syntax hingegen eher weniger. Der wirkliche Vorteil gegenüber Attached Properties ist jedoch, dass man sie mit Events verknüpfen kann.
Hier ein Beispiel für die Deklaration eines TT.UIA-Behavior.
<TextBox>
  <i:Interaction.Behaviors>
    <behaviors:FocusBehavior HighlightBrush="Yellow" />
  </i:Interaction.Behaviors>
</TextBox>

FocusBehavior
Hier wird das FocusBehavior verwendet, welches das fokussierte Control in einer beliebigen Farbe einfärbt.
Im nächsten Post stelle ich die einzelnen Adorner und Behaviors von TT.UIA im Detail vor.

Labels:

Montag, Mai 09, 2011

Just Released: thinktecture UIAnnotations

Nach mehr als einem Jahr bin ich nun endlich mit meinem UIAnnotations-Projekt fertig!
thinktecture UIAnnotations
With thinktecture UIAnnotations you can extend your UI with additional functionality via Adorners and Behaviors. All parts are assignable via markup, which is helpful in MVVM scenarios and promotes the developer/designer interaction.
With thinktecture UIAnnotations you can declarative attach visual Adorners and Expression Blend Behaviors to your user interfaces, including smart tags, editor toolbars and visual state markers for required fields, edit states and more. It also can automatically attach adorners to elements while reflecting the Data Annotations of the associated data sources. For a better MVVM support it offers a generic command behavior that is assignable to every event of a given element. thinktecture UIAnnotations is available for WPF and Silverlight (comming soon).
thinktecture UIAnnotations in brief:
  • 10+ useful adorners and 5+ Expression Blend Behaviors.
  • automatic assignment of adorners via Data Annotations.
  • a custom adorner model for silverlight (comming soon).
  • GenericAdorner allows to attach every element as an adorner.
  • AdornerBehaviorFactory that can be used to wrap every adorner as an Expression Blend Behavior.
  • BehaviorFactory allows the assignment of Expression Blend Behaviors via styles.
  • with Command Behavior you can map every control event to a command.
Und da ein Bild mehr sagt als tausend Worte:
Showcase
Die Details folgen in späteren Posts. Stay tuned!

Labels: