WPF Control State Persistance

Das Problem ist ja allgemein bekannt.. Wenn moderne SmartClient Oberflächen schon die Möglichkeit bieten, alles anzupassen und das UI bis in's letzte Detail zu personalisieren (Farben, Positionen, etc..), dann möchte der Benutzer diesen - vor allem "kreativ-mühsamen" Prozess meistens nicht bei jedem Start der Applikation durchführen: Sprich wir benötigen eine Möglichkeit die angepassten Eigenschaften der einzelnen Controls lokal oder in einer Datenbank zu speichern!

Bei WPF Applikationen würde sich dafür normalerweise bei erster Überlegung Databinding anbieten! Doch denkt man im Detail über das Problem nach, so scheint die Lösung doch nicht so einfach, wie zuerst geglaubt...

Eine mögliche Lösung für das Problem wird in diesem Artikel auf Codeproject vorgestellt:

Der Autor verwendet dafür Attached Properties und MarkupExtensions.

  • Attached Properties sind der Nachfolger des "IExtenderProvider"-Interfaces für Windows Forms (wird z.B. dort beim ErrorProvider verwendet:
    • Eigenschaften, die eigentlich nur im Context anderer Controls interessant sind, werden lokal in einem Control gespeichert (möglichwird das durch neue Dependency Properties)
    • So kann man z.B. die Spalte/Zeile einer Textbox innerhalb eines Grid-Panels direkt in der Textbox speichern. Dies geschieht in XAML durch Angabe des Klassennamnes der attached Property:
      • <Textbox Grid.Column="1" Grid.Row="0"/>
    • Column und Row sind Properties der Klasse Grid, die aber in der Textbox gespeichert werden und später vom Grid für die Positionierung der Textbox abgefragt werden können.
  • Markup Extensions agieren als ein Verweis bzw. Platzhalten für andere Daten. Eine Markup Extension beginnt und endet mit einer geschwungenen Klammer und wird innerhalb eines XAML Attributes verwendet:
    • <Button Background="{StaticResource BgBrush}" />
    • <Button Content="{Binding Source=SrcObject,Path=prop}" />
    • In beiden Fällen wir die Binding Extension (die mit Zusatzeigenschaften angegeben wird) nachher durch ein konkretes Objekt ersetzt, das über Binding oder ResourceBinding besorgt wird.

Zusammengefügt, funktioniert die State Persistance folgendermaßen:

 <ListView>
    <ListView.View>
        <GridView>
           <GridViewColumn
                ElementState.Mode  ="Persist"                ElementState.UId
  ="DemoWindow_GridViewColumn1"  
                DisplayMemberBinding="{Binding Path=FirstName}"
                Header="First Name"
                Width  ="{ElementState Default=100}"  />
           <GridViewColumn
                ElementState.Mode  ="Persist"                ElementState.UId
  ="DemoWindow_GridViewColumn2"  
                DisplayMemberBinding="{Binding Path=LastName}"
                Header="Last Name"
                Width  ="{ElementState Default=100}"   />
           <GridViewColumn
                ElementState.Mode  ="Persist"                ElementState.UId
  ="DemoWindow_GridViewColumn3"  
                DisplayMemberBinding="{Binding Path=Blog}"
                Header="Blog"
                Width  ="{ElementState Default=Auto}"   />
        </GridView>
    </ListView.View>
</ListView> 

Mit Hilfe der Attached Properties ElementState.Mode und UId werden Metadaten für das Control angegeben. Die Eigenschaften die personalisierbar sein sollen, werden über eine eigene MarkupExtension ElementState auf die Datenquelle gebunden.

Alles in alle sicher ein gute Möglichkeit, Userinterface-Einstellungen auf Benutzerebene zu speichern!

[ knom ]