ListCollectionView

WPF_logo

In einem Kundenprojekt gab es die Anforderung die Daten aus einem DataGrid in der vom Anwender sortierten Reihenfolge zu drucken. Üblicherweise bindet man in einer WPF-Anwendung (MVVM) das DataGrid an die Instanz einer ObservableCollection. Eine gebundene ObservableCollection hat aber leider den Nachteil, dass sie vom Sortieren durch den Anwender (z.B. durch Click im Spaltenheader) nichts mitbekommt. D.h. die ursprüngliche Reihenfolge der Items in der ObservableCollection bleibt erhalten. Abhilfe schafft hier die Klasse ListCollectionView. Man erzeugt sich einfach eine neue Instanz einer ListCollectionView in dem man beim Konstruktor einfach die Instanz der ObservableCollection (oder einer Liste wie im Beispielprojekt) übergibt und bindet diese neue Instanz an das DataGrid. Wie man leicht mit einer Foreach-Schleife feststellen kann, entspricht die Reihenfolge der Items dann der Reihenfolge im DataGrid.

Hier ein Beispielprojekt mit freundlicher Unterstützung von Thomas Sobizack.

Weitere Infos z.B. unter http://weblogs.asp.net/monikadyrda/wpf-listcollectionview-for-sorting-filtering-and-grouping

 

VirtualizingWrapPanel

WPF besitzt mit dem VirtualizingStackPanel und dem DataGridCellsPanel zwei Subklassen, die von der abstrakten Klasse VirtualizingPanel abgeleitet sind. Das VirtualizingStackPanel wird in der Regel innerhalb von anderen Steuerelementen wie z.B. der ListBox oder einem ListView zum Anordnen und damit zur Anzeige der ListItems verwendet. Warum wird dort kein normales Stackpanel verwendet? Die Antwort ist relativ einfach: Aus Performancegründen. Stellen Sie sich eine ListView mit einigen tausend Einträgen vor. Bei einem VirtualizingStackPanel werden nur die Einträge erstellt, die im aktuell sichtbaren Bereich des ListViews liegen. Erst wenn der Anwender mit dem Scrollen beginnt werden die anderen Einträge erstellt. Bei einem normalen StackPanel würden zunächst alle Items (also auch die zunächst nicht sichtbaren) erstellt. Das führt in der Regel zu deutlichen Performanceproblemen. Unter Umständen kann der Bildschirm mehrere Minuten einfrieren bevor alle Elemente gerendert sind. (Das DataGridCellsPanel arbeitet analog wie das VirtualizingStackPanel. Allerdings kommt es lediglich intern im DataGrid zum Einsatz.)

In einem Kundenprojekt habe ich nun analog ein VirtualizingWrapPanel benötigt, da ich eine größere Anzahl von Items innerhalb eines ListViews in Form eines WrapPanels darstellen wollte. Leider stellt einem das .Net Framework dafür out of the box keine entsprechende Klasse zur Verfügung. Abhilfe verspricht hier das folgende Projekt: http://www.codeproject.com/Articles/75847/Virtualizing-WrapPanel. Das Projekt liefert ein solches frei verfügbares VirtualizingWrapPanel inklusive SourceCode. Leider haben meine Tests gezeigt, dass dieses Panel zumindest für meine Ansprüche nicht performant genug ist. Beim Vergrößern eines Fensters ruckelte es doch ziemlich stark, wenn die Einträge neu umgebrochen werden müssen. Beim Scrollen gibt es teilweise den gleichen Effekt. Außerdem funktionierte es in meinem Anwendungsfall nicht ohne Probleme. Ab und an kam es leider zu Abstürzen bzw. unbehandelten Exceptions.

Die einzige vernünftige Lösung bietet aktuell meines Wissens nach die Firma Binarymission (http://binarymission.co.uk/binaryvirtualizingwrappanelwpf.htm). Deren VirtualizingWrapPanel funktionierte in meinem Anwendungsfall ohne Probleme. Das Produkt ist aber leider kostenpflichtig!

Falls jemand eine bessere Lösung weiß, wäre ich für einen entsprechenden Hinweis sehr dankbar!

WPF Binding Converter

Das .Net-Framework kommt OFTB bereits mit einigen Konvertern daher: AlternationConverter, BooleanToVisibilityConverter, BorderGapMaskConverter, DataGridLengthConverter, MenuScrollingVisibilityConverter, ZoomPercentageConverter, JournalEntryListConverter, ProgressBarBrushConverter, ProgressBarHighlightConverter, JournalEntryUnifiedViewConverter (weitere Infos unter: http://asimsajjad.blogspot.de/2010/05/built-in-wpf-ivalueconverters.html)

Ein Konverter, den ich schon häufiger vermisst habe,  ist der BoolToOppositeBoolConverter-Converter, der einfach “True” in “False” konvertiert und umgekehrt: http://www.codeproject.com/Articles/24330/WPF-Bind-to-Opposite-Boolean-Value-Using-a-Convert

Weitere Konverter findet man hier: http://wpfconverters.codeplex.com/

Einführung in MVVM

Die beste Einführung in MVVM, die ich gefunden habe, ist diese: http://visualstudiogallery.msdn.microsoft.com/af12ce3b-8337-4bbb-853f-9fdad7105ec8

Ein weiteres Skript findet sich hier: http://msdn.microsoft.com/de-de/magazine/dd419663.aspx

Besonderheiten im Zusammenhang mit einem TreeView zeigt dieser Artikel auf.

WPF Search TextBox

SearchTextBox

Leider gibt es im .Net Framework out of the Box keine sogenannte SearchTextBox wie sie z.B. von den Office-Anwendungen bekannt ist. Abhilfe schafft hier ein Steuerelement von David Owens: http://davidowens.wordpress.com/2009/02/18/wpf-search-text-box/.

Die SearchTextBox kann in zwei verschiedenen Modi betrieben werden. Im 1. Modus wird die Suche sofort beim Eintippen eines Suchbegriffs durchgeführt. Im 2. Fall muss man erst die Suchschaltfläche betätigen.

WPF Borderless Window

Wenn man mit Hilfe von WPF Applikationen mit einem modernen Design bauen will, hat man mit den in WPF angebotenen Styles so seine Probleme. Wenn man z.B. eine Applikation mit Office 2013 Style bauen will, benötigt man in WPF ein sogenanntes “Borderless Window”, also ein Fenster ohne den unschönen dicken Rahmen. Hier leistet die frei verfügbare MahApps-Bibliothek gesponsert von der Firma AIT gute Dienste: http://mahapps.com/MahApps.Metro/.

BorderlessWindow

Neben den Styles bietet die Bibliothek auch einige sehr interessante neue Steuerelemente wie z.B. “Panorama”. Panorama ist eine seitwärts scrollende Liste, wie sie z.B. vom Windows 8 Startbildschirm bekannt ist:

Panorama

WPF Favorite Button

With this post I want to present c# source code for implementing a favorite button in WPF.

An empty favorite button becomes visible on moving the mouse into the window pane. You’ll also get a tooltip “add to favorites”:

Favorite1

On clicking the favorite button you get the following with tooltip “remove from favorites”:

Favorite2

SourceCode:

<Window x:Class="FavoriteButton.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Favoriten" Height="160" Width="160" ResizeMode="NoResize">

    <Window.Resources>
        <BitmapImage x:Key="Star" UriSource="/Images/star.png" />
        <BitmapImage x:Key="StarEmpty" UriSource="/Images/star empty.png" />
    </Window.Resources>

    <Grid x:Name="LayoutRoot" Background="Transparent">
        <ToggleButton Focusable="False" HorizontalAlignment="Left" Margin="55,40,0,0"  VerticalAlignment="Top" Width="40" Height="40" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" IsTabStop="False" >
            <ToggleButton.Style>
                <Style TargetType="{x:Type ToggleButton}">                                       
                    <Setter Property="HorizontalContentAlignment" Value="Center"/>
                    <Setter Property="VerticalContentAlignment" Value="Center"/>                 
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ToggleButton}">
                                <Border>
                                    <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                                      RecognizesAccessKey="True"                                                   
                                                      HorizontalAlignment="Center"/>
                                </Border>
                                
                                <ControlTemplate.Triggers>                                                                     
                                    <MultiDataTrigger>
                                        <MultiDataTrigger.Conditions>
                                            <Condition Binding = "{Binding ElementName=LayoutRoot, Path=IsMouseOver}" Value="True" />
                                            <Condition Binding = "{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsChecked}" Value="False" />
                                            <Condition Binding = "{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsEnabled}" Value="True" />
                                        </MultiDataTrigger.Conditions>

                                        <Setter Property="Content">
                                            <Setter.Value>                                                
                                                    <Image Source="{StaticResource StarEmpty}"/>                                                
                                            </Setter.Value>                                            
                                        </Setter>
                                        <Setter Property="ToolTip" Value="Zu Favoritenliste hinzufügen"/>
                                    </MultiDataTrigger>                                      

                                    <Trigger  Property="IsChecked" Value="True">
                                        <Setter Property="Content">
                                            <Setter.Value>                                                
                                                    <Image Source="{StaticResource Star}"/>
                                            </Setter.Value>
                                        </Setter>
                                        <Setter Property="ToolTip" Value="Aus Favoritenliste entfernen"/>
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ToggleButton.Style>            
        </ToggleButton>
    </Grid>
</Window>