vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#
Brandneu! sevEingabe v2.0 - Das Eingabecontrol der Superlative!  
 vb@rchiv Quick-Search: Suche startenErweiterte Suche starten   RSS-Feeds  | Newsletter  | Impressum  | Datenschutz  | vb@rchiv CD Vol.6  | Shop Copyright ©2000-2017
 
zurück
Rubrik: .NET   |   VB-Versionen: VB200801.02.10
ListView filtern, sortieren und gruppieren in WPF

Dieser Workshop zeigt, wie man ein ListView in WPF (Visual Basic Studio 2008) relativ einfach filtern, sortieren und gruppieren kann und dies auch angemessen darstellt.

Autor:  MaasBewertung:     [ Jetzt bewerten ]Views:  30.376 

Im Folgenenden soll gezeigt werden, wie es mit relativ einfachen Mitteln (das sieht jetzt viel aus, ist aber eigentlich ganz übersichtlich) möglich ist, eine an ein ListView gebundene Collection zu filtern, sortieren und/oder zu gruppieren.

Zuerst muss eine neue WPF-Applikation erstellt werden:

Als Basis für die folgenden Beispiele wird nachfolgende Kontakt-Klasse und Enum benutzt, welche Informationen über eine Person beinhaltet. Eine Liste dieser Kontaktdaten wird dann geordnet.

Imports System.Collections.ObjectModel
Imports System.ComponentModel
 
Public Enum KontaktTyp
  Privat
  Geschäftlich
  Sonstige
End Enum
 
Public Class Kontakt
 
  Private mTyp As KontaktTyp = KontaktTyp.Sonstige
  Private mName As String = String.Empty
  Private mVorname As String = String.Empty
  Private mGeburtsDatum As Date
 
  Public Property Typ() As KontaktTyp
    Get
      Return mTyp
    End Get
    Set(ByVal value As KontaktTyp)
      mTyp = value
    End Set
  End Property
 
  Public Property Name() As String
    Get
      Return mName
    End Get
    Set(ByVal value As String)
      mName = value
    End Set
  End Property
 
  Public Property Vorname() As String
    Get
      Return mVorname
    End Get
    Set(ByVal value As String)
      mVorname = value
    End Set
  End Property
 
  Public Property Geburtsdatum() As Date
    Get
      Return mGeburtsDatum
    End Get
    Set(ByVal value As Date)
      mGeburtsDatum = value
    End Set
  End Property
 
  Public Sub New()
  End Sub
 
  Public Sub New(ByVal vorname As String, ByVal name As String, _
    ByVal geburtsdatum As Date, ByVal typ As KontaktTyp)
 
    mVorname = vorname
    mName = name
    mGeburtsDatum = geburtsdatum
    mTyp = typ
  End Sub
 
  Public Overrides Function ToString() As String
    Return String.Format("{0}, {1}", mName, mVorname)
  End Function
 
End Class

Das Fenster für die Darstellung sieht dann so aus.
Das Beziehen von Daten oder das Setzen von Eigenschaften läuft fast komplett über DataBinding. Es wird hier vorausgesetzt, dass die Grundlagen des WPF-DataBindings bekannt sind.

<Window x:Class="Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation";
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml";
  Title="ListView filtern, sortieren und gruppieren in WPF"
  Height="431"
  Width="681">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="1*" />
      <RowDefinition Height="130" />
      <RowDefinition Height="35" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="200" />
      <ColumnDefinition Width="200" />
      <ColumnDefinition Width="200" />
      <ColumnDefinition Width="1*" />
    </Grid.ColumnDefinitions>
    <ListView ItemsSource="{Binding Kontakte}"
      Margin="12"
      Name="ListView1"
      Grid.ColumnSpan="4">
    <ListView.View>
      <GridView>
        <GridViewColumn Width="180"
          Header="Name"
          DisplayMemberBinding="{Binding Name}"/>
        <GridViewColumn Width="140"
          Header="Vorname"
          DisplayMemberBinding="{Binding Vorname}" />
        <GridViewColumn Width="140"
          Header="Typ"
          DisplayMemberBinding="{Binding Typ}" />
        <GridViewColumn Width="140"
          Header="Geburtsdatum"
          DisplayMemberBinding="{Binding Geburtsdatum, StringFormat='{}{0:dd.MM.yyyy}'}" />
        </GridView>
      </ListView.View>
    </ListView>
  </Grid>
</Window>

Der code-behind:

Imports System.Collections.ObjectModel
Imports System.ComponentModel
 
Class Window1
 
  Private mKontakte As New ObservableCollection(Of Kontakt)
 
  Public Property Kontakte() As ObservableCollection(Of Kontakt)
    Get
      Return mKontakte
    End Get
    Set(ByVal value As ObservableCollection(Of Kontakt))
      mKontakte = value
    End Set
  End Property
 
  Public Sub New()
    InitializeComponent()
 
    ListView1.DataContext = Me
 
    ' Beispiel Liste
    mKontakte.Add(New Kontakt("Bernd", "Schuster", #1/8/1960#, KontaktTyp.Geschäftlich))
    mKontakte.Add(New Kontakt("Lisa", "Müller", #1/1/1965#, KontaktTyp.Privat))
    mKontakte.Add(New Kontakt("Stefan", "Meyer", #1/2/1970#, KontaktTyp.Sonstige))
    mKontakte.Add(New Kontakt("Klaus", "Schulz", #1/3/1975#, KontaktTyp.Geschäftlich))
    mKontakte.Add(New Kontakt("Martin", "Schmidt", #1/4/1980#, KontaktTyp.Sonstige))
    mKontakte.Add(New Kontakt("Christina", "Fischer", #1/5/1985#, KontaktTyp.Geschäftlich))
    mKontakte.Add(New Kontakt("Kurt", "Wagner", #1/6/1990#, KontaktTyp.Privat))
    mKontakte.Add(New Kontakt("Anne", "Bauer", #1/7/1995#, KontaktTyp.Sonstige))
    mKontakte.Add(New Kontakt("Kai", "Krüger", #1/8/2000#, KontaktTyp.Privat))
  End Sub
 
End Class

Das Ganze sollte jetzt so aussehen:

ListCollectionView

Das Prinzip in WPF ist es für das ListView sozusagen eine extra Ansicht zu erstellen, die nur im Standardfall der echten Auflistung entspricht. Man kann diese Ansicht beeinflussen, ohne damit die echte Auflistung zu ändern.

An die ListCollectionView der Kontakte-Liste, mit der man das umsetzen kann, kommt man mit folgendem Code:

Dim view As ListCollectionView = _
  CType(CollectionViewSource.GetDefaultView(mKontakte), _
  ListCollectionView)

Um die Ansicht manuell zu aktualisieren, kann man .Refresh() aufrufen. Dies wird nicht benötigt, wenn die Auflistung irgendwie geändert wird. Der Refresh-Vorgang wird dann automatisch ausgelöst. Wenn der Typ der Auflistung INotifyPropertyChanged implementiert, führt das Auslösen des PropertyChanged-Events auch zu einem Refresh.

view.Refresh()

Das ist die Aufgabe für den Update-Button, der jetzt hinzugefügt wird.

  <Grid>
    '...
    <Button Grid.Column="2"
      Grid.Row="2"
      HorizontalAlignment="Right"
      Margin="0,6,11,0"
      Name="btnUpdate"
      Width="75"
      Height="23"
      VerticalAlignment="Top">Update
    </Button>
  </Grid>
  Private Sub btnUpdate_Click(ByVal sender As Object, _
    ByVal e As RoutedEventArgs) Handles btnUpdate.Click
 
    Dim view As ListCollectionView = _
      CType(CollectionViewSource.GetDefaultView(mKontakte), _
      ListCollectionView)
    view.Refresh()
  End Sub

Ich benutze in diesem Beispiel eine ObservableCollection, da diese Vorteile im Gegensatz zur BindingList hat, bzw. deren CollectionView. Die BindingListCollectionView hat genauso wie die ItemCollection des ListView keine CustomSort Eigenschaft. Ansonsten sind BindingList und ObservableCollection im Verhalten gleich was das DataBinding angeht.

Filtern

Die Filter-Eigenschaft der ListCollectionView ist vom Typ System.Predicate(Of Object) und diese kann mit einer Funktion bedient werden.
Diese Funktion sieht dann etwa so aus.

Public Function Filter(ByVal itemObj As Object) As Boolean
  ' ...
End Function

itemObj ist in diesem Fall der Kontakt, welcher überprüft wird. Es wird im Fall einer Aktualisierung der Ansicht für jeden Item diese Funktionaufgerufen. Der Boolean Wert, der zurück gegeben wird, zeigt ganz einfach ob der Item angezeigt wird oder nicht.

Man könnte diese Funktion nun direkt in die Window-Klasse schreiben, aber da die Filterung von einigen Optionen abhängig gemacht werden und am Ende kein Spagetti-Code rauskommen soll, wird das alles in eine Klasse gepackt.

Beim Filtern soll es möglich sein nach einem Keyword zu suchen, nur einen bestimmten oder alle KontaktTypen anzuzeigen und eine Grenze für das Geburtsdatum zu setzen. Dazu einfach folgende Filter-Klasse hinzufügen.

Public Class KontaktFilter
  Private mSuchwort As String = String.Empty
  Private mNurJuengerAls As Date
  Private mFilterTypen As Boolean = False
  Private mErlaubterTyp As KontaktTyp = KontaktTyp.Sonstige
 
  Public Property Suchwort() As String
    Get
      Return mSuchwort
    End Get
    Set(ByVal value As String)
      mSuchwort = value
    End Set
  End Property
 
  Public Property NurJuengerAls() As Date
    Get
      Return mNurJuengerAls
    End Get
    Set(ByVal value As Date)
      mNurJuengerAls = value
    End Set
  End Property
 
  Public Property FilterTypen() As Boolean
    Get
      Return mFilterTypen
    End Get
    Set(ByVal value As Boolean)
      mFilterTypen = value
    End Set
  End Property
 
  Public Property ErlaubterTyp() As KontaktTyp
    Get
      Return mErlaubterTyp
    End Get
    Set(ByVal value As KontaktTyp)
      mErlaubterTyp = value
    End Set
  End Property
 
  Public Function Filter(ByVal itemObj As Object) As Boolean
    Dim item As Kontakt = TryCast(itemObj, Kontakt)
    If item IsNot Nothing Then
      ' Wenn eine Bedingung nicht erfüllt wird, ist der Item raus
      If mSuchwort <> String.Empty Then
        ' Nach Keyword suchen
        If Not (item.Vorname.ToLower.Contains(mSuchwort.ToLower) Or _
          item.Name.ToLower.Contains(mSuchwort.ToLower)) Then
          Return False
        End If
      End If
 
      If mFilterTypen Then
        ' Nur bestimmten Typ erlauben
        If mErlaubterTyp <> item.Typ Then
          Return False
        End If
      End If
 
      ' Geburtstagsgrenze
      If item.Geburtsdatum < mNurJuengerAls Then
        Return False
      End If
 
      ' und so weiter ...
 
      Return True
    Else
      Return False
    End If
  End Function
End Class

Die Window-Klasse wird mit folgendem Code erweitert.

Class Window1
 
  Private mFilter As New KontaktFilter
 
  ' Brauchen wir für das DataBinding
  Public ReadOnly Property Filter() As KontaktFilter
    Get
      Return mFilter
    End Get
  End Property
 
  ...
 
  Public Sub New()
    txtNurJuengerAls.DataContext = Me
    txtSuchwort.DataContext = Me
    cmbTyp.ItemsSource = New KontaktTyp() {KontaktTyp.Geschäftlich, _
                                           KontaktTyp.Privat, _
                                           KontaktTyp.Sonstige}
    cmbTyp.DataContext = Me
    chkFilterTypen.DataContext = Me
 
    ...
 
    Dim view As ListCollectionView = _
      CType(CollectionViewSource.GetDefaultView(mKontakte), _
      ListCollectionView)
    ' Hier wird der zu benutzende Filter gesetzt;
    ' jede Änderung im Filter wird beim Refresh übernommen, 
    ' da die Funktion neu aufgerufen wird
    view.Filter = New Predicate(Of Object)(AddressOf mFilter.Filter)
  End Sub
 
End Class

Window-XAML

<Window x:Class="Window1" >
  <Grid>
 
    '...
 
    <GroupBox Grid.Row="1" Header="Filter" Name="GroupBox1" Margin="5,0" >
      <Grid>
        <TextBox Height="23"
          Margin="47,13,11,0"
          Name="txtNurJuengerAls"
          VerticalAlignment="Top"
          Text="{Binding Filter.NurJuengerAls,Mode=TwoWay,StringFormat='{}{0:dd.MM.yyyy}'}" />
        <TextBlock Height="21"
          Margin="7,15,0,0"
          Name="TextBlock1"
          VerticalAlignment="Top"
          HorizontalAlignment="Left"
          Width="40"
          Text="Geb." />
        <TextBox Margin="47,42,11,0"
          Name="txtSuchwort"
          Height="23.04"
          VerticalAlignment="Top"
          Text="{Binding Filter.Suchwort,Mode=TwoWay}" />
        <TextBlock HorizontalAlignment="Left"
          Margin="7,44,0,0"
          Name="TextBlock2"
          Text="Name"
          Width="40"
          Height="21.04"
          VerticalAlignment="Top" />
        <ComboBox Height="23"
          Margin="47,71.04,11,0"
          Name="cmbTyp"
          VerticalAlignment="Top"
          SelectedItem="{Binding Filter.ErlaubterTyp,Mode=TwoWay}"
          IsEnabled="{Binding IsChecked,ElementName=chkFilterTypen}"/>
        <CheckBox IsChecked="{Binding Filter.FilterTypen,Mode=TwoWay}"
          IsThreeState="False" Height="16"
          Margin="7,0,0,17"
          Name="chkFilterTypen"
          VerticalAlignment="Bottom"
          HorizontalAlignment="Left"
          Width="41">Typ</CheckBox>
      </Grid>
    </GroupBox>
  </Grid>
</Window>

Jetzt einfach mal verschiedene Keywords (Buchstaben oder Teile von Namen) oder die anderen Optionen ausprobieren und danach "Update" klicken.

Sortieren

Um sortieren zu können muss man eine Klasse erstellen, welche das Interface IComparer implementiert. Damit werden dann die verschiedenen Items verglichen und geordnet. An sich verläuft alles wie bei einem normalen Comparer.

In dieser Klasse gibt es noch die Option der Richtung der Sortierung und der Eigenschaft des Kontakts nach dem sortiert wird.

Imports System.ComponentModel
 
Public Enum KontaktEigenschaft
  Name
  Vorname
  Typ
  Geburtsdatum
End Enum
 
Public Class KontaktSortierer
  Implements IComparer
 
  Private mSortiertNach As KontaktEigenschaft = KontaktEigenschaft.Name
  Private mSortierRichtung As ListSortDirection = ListSortDirection.Ascending
 
  Public Property SortiertNach() As KontaktEigenschaft
    Get
      Return mSortiertNach
    End Get
    Set(ByVal value As KontaktEigenschaft)
      mSortiertNach = value
    End Set
  End Property
  Public Property SortierRichtung() As ListSortDirection
    Get
      Return mSortierRichtung
    End Get
    Set(ByVal value As ListSortDirection)
      mSortierRichtung = value
    End Set
  End Property
 
  Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer _
    Implements System.Collections.IComparer.Compare
 
    Dim itemX As Kontakt = TryCast(x, Kontakt)
    Dim itemY As Kontakt = TryCast(y, Kontakt)
    If itemX IsNot Nothing AndAlso itemY IsNot Nothing Then
      Dim sortMultiplier As Integer = 0
      If mSortierRichtung = ListSortDirection.Ascending Then : sortMultiplier = 1
      Else : sortMultiplier = -1
      End If
      Select Case mSortiertNach
        Case KontaktEigenschaft.Name
          Return String.Compare(itemX.Name, itemY.Name) * sortMultiplier
        Case KontaktEigenschaft.Vorname
          Return String.Compare(itemX.Vorname, itemY.Vorname) * sortMultiplier
        Case KontaktEigenschaft.Typ
          If CInt(itemX.Typ) > CInt(itemY.Typ) Then
            Return 1 * sortMultiplier
          ElseIf CInt(itemX.Typ) < CInt(itemY.Typ) Then
            Return -1 * sortMultiplier
          Else
            Return 0
          End If
        Case KontaktEigenschaft.Geburtsdatum
          Return Date.Compare(itemX.Geburtsdatum, itemY.Geburtsdatum) * sortMultiplier
      End Select
    End If
    Return -1
  End Function
 
End Class

Zur Window-Klasse kommt folgendes hinzu:

Class Window1
 
  Private mSortierung As New KontaktSortierer
 
  ' Brauchen wir für das DataBinding
  Public ReadOnly Property Sortierung() As KontaktSortierer
    Get
      Return mSortierung
    End Get
  End Property
 
  ' ...
 
  Public Sub New()
    cmbSortiertNach.ItemsSource = _
      New KontaktEigenschaft() {KontaktEigenschaft.Name, _
      KontaktEigenschaft.Vorname, _
      KontaktEigenschaft.Typ, _
      KontaktEigenschaft.Geburtsdatum}
 
    cmbSortiertNach.DataContext = Me
    cmbSortierRichtung.ItemsSource = _
      New ListSortDirection() {ListSortDirection.Ascending, _
      ListSortDirection.Descending}
    cmbSortierRichtung.DataContext = Me
 
    ' ...
 
    Dim view As ListCollectionView = _
      CType(CollectionViewSource.GetDefaultView(mKontakte), _
      ListCollectionView)
    view.Filter = New Predicate(Of Object)(AddressOf mFilter.Filter)
 
    ' Die Sortierung hinzufügen
    view.CustomSort = mSortierung
  End Sub
 
End Class

Window-XAML

<Window x:Class="Window1" >
  <Grid>
    '...
 
    <GroupBox Header="Sortierung" Name="GroupBox2" Grid.Column="1" Grid.Row="1" Margin="5,0">
      <Grid>
        <ComboBox SelectedItem="{Binding Sortierung.SortiertNach,Mode=TwoWay}"
          Height="23" Margin="15,13,15,0"
          Name="cmbSortiertNach"
          VerticalAlignment="Top" />
        <ComboBox SelectedItem="{Binding Sortierung.SortierRichtung,Mode=TwoWay}"
          Margin="15,42,15,0"
          Name="cmbSortierRichtung"
          Height="23.04"
          VerticalAlignment="Top" />
      </Grid>
    </GroupBox>
  </Grid>
</Window>

Gruppieren

Das Gruppieren ist erstmal das Leichteste von allem. Es reicht eigentlich folgendes der Ansicht hinzuzufügen.

view.GroupDescriptions.Add(New PropertyGroupDescription("Typ"))

Damit würden die Items nach der Eigenschaft Typ gruppiert werden.

Man kann es aber auch noch individueller gestalten, z.B. bei Eigenschaften, wo die Auswahlmöglichkeit nicht so begrenzt ist. Würde man nach der Eigenschaft Geburtsdatum gruppieren, würde in diesem Beispiel jeder Kontakt seine eigene Gruppe bekommen. Das ist aber nicht der tiefere Sinn der Gruppierung, also kann man noch einen IValueConverter übergeben. Mit diesem können die Items individuell gruppiert werden. Wenn kein Eigenschaftenname übergeben wird, wird dem Converter das gesamte Item-Objekt übergeben. Bei einem übergebenen Namen wird das Objekt der Eigenschaft übergeben, in dem folgendem Fall ein Datum. Die Gruppierer-Klasse kann natürlich genauso individuell gestaltet werden wie der Filter und der Sortierer.

In diesem Beispiel werden die Geburtstage in Dekaden gruppiert.

Public Class GeburtsdekadenGruppierer
  Implements IValueConverter
 
  Public Function Convert(ByVal value As Object, _
    ByVal targetType As System.Type, _
    ByVal parameter As Object, _
    ByVal culture As System.Globalization.CultureInfo) As Object _
    Implements System.Windows.Data.IValueConverter.Convert
 
    If value.GetType Is GetType(Date) Then
      Dim d As Date = CDate(value)
      If d > #1/1/1950# And d < #1/1/1959# Then
        Return "1950 - 1959"
      ElseIf d > #1/1/1960# And d < #1/1/1969# Then
        Return "1960 - 1969"
      ElseIf d > #1/1/1970# And d < #1/1/1979# Then
        Return "1970 - 1979"
      ElseIf d > #1/1/1980# And d < #1/1/1989# Then
        Return "1980 - 1989"
      ElseIf d > #1/1/1990# And d < #1/1/1999# Then
        Return "1990 - 1999"
      ElseIf d > #1/1/2000# And d < #1/1/2009# Then
        Return "2000 - 2009"
      Else
        Return "2010 - Heute"
      End If
    Else
      Return "Sonstige"
    End If
  End Function
 
  Public Function ConvertBack(ByVal value As Object, _
    ByVal targetType As System.Type, _
    ByVal parameter As Object, _
    ByVal culture As System.Globalization.CultureInfo) As Object _
    Implements System.Windows.Data.IValueConverter.ConvertBack
 
    Throw New NotSupportedException("Rückwärtige Konvertierung wird nicht unterstützt.")
  End Function
End Class

Um das Ganze anzuwenden, wird der Window-Klasse folgendes hinzugefügt:

Class Window1
 
  Private mGruppierungAktiviert As Boolean = False
  Private mGruppierung As KontaktEigenschaft = KontaktEigenschaft.Typ
 
  ' Brauchen wir für das DataBinding
  Public Property GruppierungAktiviert() As Boolean
    Get
      Return mGruppierungAktiviert
    End Get
    Set(ByVal value As Boolean)
      mGruppierungAktiviert = value
    End Set
  End Property
 
  Public Property Gruppierung() As KontaktEigenschaft
    Get
      Return mGruppierung
    End Get
    Set(ByVal value As KontaktEigenschaft)
      mGruppierung = value
    End Set
  End Property
 
  ' ...
 
  Public Sub New()
    chkGrpAktiviert.DataContext = Me
    cmbGruppierung.ItemsSource = _
      New KontaktEigenschaft() {KontaktEigenschaft.Typ, _
      KontaktEigenschaft.Geburtsdatum}
    cmbGruppierung.DataContext = Me
 
    ' ...
 
  End Sub
 
 
  Private Sub btnUpdate_Click(ByVal sender As Object, _
    ByVal e As RoutedEventArgs) Handles btnUpdate.Click
 
    Dim view As ListCollectionView = _
      CType(CollectionViewSource.GetDefaultView(mKontakte), _
      ListCollectionView)
    view.GroupDescriptions.Clear()
 
    If mGruppierungAktiviert Then
      If mGruppierung = KontaktEigenschaft.Typ Then
        view.GroupDescriptions.Add(New PropertyGroupDescription("Typ"))
      ElseIf mGruppierung = KontaktEigenschaft.Geburtsdatum Then
        view.GroupDescriptions.Add(New _
        PropertyGroupDescription("Geburtsdatum", New GeburtsdekadenGruppierer))
      End If
    End If
 
    view.Refresh()
  End Sub
 
End Class

Window-XAML

<Window x:Class="Window1" >
  <Grid>
    '...
 
    <GroupBox Header="Gruppierung" Margin="5,0" Name="GroupBox3"
      Grid.Column="2" Grid.Row="1">
      <Grid>
        <CheckBox IsChecked="{Binding GruppierungAktiviert,Mode=TwoWay}"
          Margin="17,15,23,0"
          Name="chkGrpAktiviert"
          Height="16.04"
          VerticalAlignment="Top">Aktiviert</CheckBox>
        <ComboBox SelectedItem="{Binding Gruppierung,Mode=TwoWay}"
          Margin="17,44,23,0"
          Name="cmbGruppierung"
          Height="23.04"
          VerticalAlignment="Top" />
      </Grid>
    </GroupBox>
  </Grid>
</Window>

Jetzt werden die Items zwar schon gruppiert, aber das ist noch nicht so richtig erkennbar. Dafür bietet WPF auch eine Lösung mit der Bestimmung eines eigenen Styles für ein GroupItem. In diesem Style werden die Items dann in Expandern gruppiert.

Dafür dem ListView den folgenden GroupStyle hinzufügen.

<Window x:Class="Window1" >
  <Grid>
    '...
 
    <ListView ItemsSource="{Binding Kontakte}"
      Margin="12"
      Name="ListView1"
      Grid.ColumnSpan="4">
      <ListView.View>
        '...
      </ListView.View>
 
      <ListView.GroupStyle>
        <GroupStyle>
          <GroupStyle.ContainerStyle>
            <Style TargetType="{x:Type GroupItem}">
              <Setter Property="Margin" Value="0,0,0,5"/>
              <Setter Property="Template">
                <Setter.Value>
                  <ControlTemplate TargetType="{x:Type GroupItem}">
                    <Expander Margin="2,0,2,0" Header="{Binding Path=Content.Name,
                      RelativeSource={RelativeSource
                      Mode=FindAncestor,
                      AncestorType={x:Type GroupItem}}}"
                      BorderBrush="Black"
                      BorderThickness="1"
                      Background="LightBlue">
                      <Expander.Content>
                        <Border Background="White"
                          Margin="2"
                          CornerRadius="3">
                          <ItemsPresenter />
                        </Border>
                      </Expander.Content>
                    </Expander>
                  </ControlTemplate>
                </Setter.Value>
              </Setter>
            </Style>
          </GroupStyle.ContainerStyle>
        </GroupStyle>
      </ListView.GroupStyle>
    </ListView>
 
    '...
  </Grid>
</Window>

Das Ganze sollte am Ende so aussehen:

So... das war's dann auch schon.
Viel Spaß damit.

 Download FilterSortGroup (22 KB)

Dieser Workshop wurde bereits 30.376 mal aufgerufen.

Über diesen Workshop im Forum diskutieren
Haben Sie Fragen oder Anregungen zu diesem Workshop, können Sie gerne mit anderen darüber in unserem Forum diskutieren.

Neue Diskussion eröffnen

nach obenzurück


Anzeige

Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Workshops finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6
(einschl. Beispielprojekt!)

Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
- nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten
- Symbol-Galerie mit mehr als 3.200 Icons im modernen Look
Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m.
 
   

Druckansicht Druckansicht Copyright ©2000-2017 vb@rchiv Dieter Otter
Alle Rechte vorbehalten.
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.

Diese Seiten wurden optimiert für eine Bildschirmauflösung von mind. 1280x1024 Pixel