GroupStyle zur Laufzeit ändern/austauschen

  • WPF
  • .NET (FX) 4.5–4.8

Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von eichseinet.

    GroupStyle zur Laufzeit ändern/austauschen

    Hallo Leute.

    Wie lässt sich zur Laufzeit ein neuer GroupStyle zuweisen? Das Programm soll per Button einem Datagrid einen anderen Groupstyle zuweisen. Im Internet fand ich zwar den "GroupStyleSelector", aber das scheint jedem Item abhängig von Inhalt einen anderen Style zuzuweisen. Auch mit der Handhabung kam ich nicht weiter.
    Inzwischen konnte ich den Style zumindest aus dem DataGrid auslagern und per StaticResource zuweisen. Aber wie ließe sich die Resource per Code neu zuweisen?

    Zuweisung des GroupStyles an das DataGrid

    XML-Quellcode

    1. <DataGrid.GroupStyle>
    2. <GroupStyle ContainerStyle="{StaticResource GroupStyle_Key}"/>
    3. </DataGrid.GroupStyle>


    Der GroupStyle selbst

    XML-Quellcode

    1. <Style TargetType="{x:Type GroupItem}" x:Key="GroupStyle_Key">
    2. <Setter Property="Margin" Value="0,0,0,5"/>
    3. <Setter Property="Template">
    4. <Setter.Value>
    5. <ControlTemplate TargetType="{x:Type GroupItem}">
    6. <Expander IsExpanded="{Binding DataContext.Expanded, Source={x:Reference dummyElement}}"
    7. BorderThickness="1,1,1,5">
    8. <Expander.Header>
    9. <DockPanel>
    10. <TextBlock FontWeight="Bold"
    11. Text="{Binding Path=Name}"
    12. Margin="5,0,10,0"
    13. Width="300"/>
    14. <TextBlock FontWeight="Bold"
    15. Text="{Binding Path=ItemCount}"/>
    16. </DockPanel>
    17. </Expander.Header>
    18. <Expander.Content>
    19. <ItemsPresenter />
    20. </Expander.Content>
    21. </Expander>
    22. </ControlTemplate>
    23. </Setter.Value>
    24. </Setter>
    25. </Style>


    Gruß
    eddi
    An was bindest du die Datagrid?
    Die Beispiele Online benutzen alle eine CollectionView.
    Und dann wird einfach hiermit Gruppiert:
    Bsp. Mit eine Person-Liste:

    C#-Quellcode

    1. public ICollectionView Persons { get; set; }
    2. List<Person> personList;
    3. var collectionView = CollectionViewSource.GetDefaultView(personList);
    4. collectionView.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
    5. Persons = collectionView

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Amro“ ()

    hier scheint mir beschrieben, was zu tun ist: learn.microsoft.com/en-us/prev…s/apps/hh780627(v=win.10)
    nämlich du musst (u.a.) einen eigenen GroupStyleSelector schreiben, der tut, wasser soll.
    Hmm, vlt. gibts auch andere Ansätze ob man den GroupStyle per Binding zuweisen kann, und da eine Art GroupStyleSelector im ViewModel basteln, der in Abhängigkeit von iwas einen anderen GroupStyle zuweist?

    Klingt alles ziemlich umständlich.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    @amro; die Fragt ist, wie ich den Teil im XAML zur Laufzeit ändern kann.
    @Erfinderdesrades; der link funktioniert leider nicht. Ich konnte aber inzwischen schon über den Groupstyle selector den Style umschalten. Leider musste ich dabei auf 2 globale Variablen zugreifen. Etwas unschön, aber es funktioniert.
    Kompliziert? Äh, Ja. Es ist auch nur ganz wenig Aufwand und hat kaum Zeit gekostet für ein wenig mehr Bedienkomfort. ;)

    eichseinet schrieb:

    Das Programm soll per Button einem Datagrid einen anderen Groupstyle zuweisen


    ??? ?(

    eichseinet schrieb:

    @amro; die Fragt ist, wie ich den Teil im XAML zur Laufzeit ändern kann.


    Wie oben beschrieben.
    Aber ich würde eine ListView nehmen.
    Das DataGrid zickt etwas wenn du die Colunms selbst einfügst.

    Hier nochmal ausführlicher:
    Spoiler anzeigen

    C#-Quellcode

    1. public partial class MainWindow : Window
    2. {
    3. public ICollectionView Persons { get; set; }
    4. List<Person> personList;
    5. public MainWindow()
    6. {
    7. InitializeComponent();
    8. DataContext = this;
    9. // Dummy Daten
    10. personList = new List<Person>
    11. {
    12. new Person { Name = "John", Age = 25 },
    13. new Person { Name = "Emily", Age = 32 },
    14. new Person { Name = "David", Age = 25 },
    15. new Person { Name = "Sarah", Age = 32 },
    16. new Person { Name = "Michael", Age = 25 },
    17. new Person { Name = "Emma", Age = 32 },
    18. new Person { Name = "John", Age = 28 },
    19. new Person { Name = "Emily", Age = 30 },
    20. new Person { Name = "David", Age = 27 },
    21. new Person { Name = "Sarah", Age = 30 }
    22. };
    23. Persons = CollectionViewSource.GetDefaultView(personList);
    24. }
    25. private void btnGroupByName_Click(object sender, RoutedEventArgs e)
    26. {
    27. var collectionView = CollectionViewSource.GetDefaultView(personList);
    28. // Vorhandene Gruppierungen entfernen
    29. collectionView.GroupDescriptions.Clear();
    30. //neue Gruppierung
    31. collectionView.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
    32. Persons = collectionView;
    33. }
    34. private void btnGroupByAge_Click(object sender, RoutedEventArgs e)
    35. {
    36. var collectionView = CollectionViewSource.GetDefaultView(personList);
    37. // Vorhandene Gruppierungen entfernen
    38. collectionView.GroupDescriptions.Clear();
    39. //neue Gruppierung
    40. collectionView.GroupDescriptions.Add(new PropertyGroupDescription("Age"));
    41. Persons = collectionView;
    42. }
    43. }
    44. public class Person
    45. {
    46. public string? Name { get; set; }
    47. public int Age { get; set; }
    48. }

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Amro“ ()

    @amro; das ist ein Missverständnis. Ich möchte nicht die Groupdiscription ändern, sondern den Groupstyle. Also nicht umschalten zw. gruppiert nach Name oder Alter. Es soll sich das Aussehen der Gruppierung ändern. Als Beispiel keinen Expander mehr anzeigen.
    In meinem Fall ist die "isexpanded" Eigenschaft mal gebunden (alle öffnen schließen gleichzeitig) und mal ohne ein Binding (alle können einzeln geöffnet werden)
    Ich poste das Ganze mal, wenn ich dieZeit finde.
    Zuerst werden die beide Styles als Resorce angelegt. Unten ist dann der GroupStyle_Selector als Resource mit Key angelegt und dessen Propertys werden belegt.

    XML-Quellcode

    1. <Grid.Resources>
    2. <Style TargetType="{x:Type GroupItem}" x:Key="GroupStyle_Key">
    3. <Setter Property="Margin" Value="0,0,0,5"/>
    4. <Setter Property="Template">
    5. <Setter.Value>
    6. <ControlTemplate TargetType="{x:Type GroupItem}">
    7. <Expander IsExpanded="{Binding DataContext.Expanded, Source={x:Reference dummyElement}}"
    8. BorderThickness="1,1,1,5">
    9. <Expander.Header>
    10. <DockPanel>
    11. <TextBlock FontWeight="Bold"
    12. Text="{Binding Path=Name}"
    13. Margin="5,0,10,0"
    14. Width="300"/>
    15. <TextBlock FontWeight="Bold"
    16. Text="{Binding Path=ItemCount}"/>
    17. </DockPanel>
    18. </Expander.Header>
    19. <Expander.Content>
    20. <ItemsPresenter />
    21. </Expander.Content>
    22. </Expander>
    23. </ControlTemplate>
    24. </Setter.Value>
    25. </Setter>
    26. </Style>
    27. <Style TargetType="{x:Type GroupItem}" x:Key="GroupStyleg_Key">
    28. <Setter Property="Margin" Value="0,0,0,5"/>
    29. <Setter Property="Template">
    30. <Setter.Value>
    31. <ControlTemplate TargetType="{x:Type GroupItem}">
    32. <Expander IsExpanded="True"
    33. BorderThickness="1,1,1,5">
    34. <Expander.Header>
    35. <DockPanel>
    36. <TextBlock FontWeight="Bold"
    37. Text="{Binding Path=Name}"
    38. Margin="5,0,10,0"
    39. Width="300"/>
    40. <TextBlock FontWeight="Bold"
    41. Text="{Binding Path=ItemCount}"/>
    42. </DockPanel>
    43. </Expander.Header>
    44. <Expander.Content>
    45. <ItemsPresenter />
    46. </Expander.Content>
    47. </Expander>
    48. </ControlTemplate>
    49. </Setter.Value>
    50. </Setter>
    51. </Style>
    52. <local:GroupStyle_Selector_Klasse x:Key="GSS"
    53. Expand_alle_Style="{StaticResource GroupStyle_Key}"
    54. Expand_getrennt_Style="{StaticResource GroupStyleg_Key}"
    55. Sender="{x:Static local:ExpandStyle_enum.Warteliste}"/>
    56. </Grid.Resources>


    Hier ist der GroupStyle_Selector dann ans DataGrid gebunden.

    XML-Quellcode

    1. <DataGrid.GroupStyle>
    2. <GroupStyle ContainerStyleSelector="{StaticResource GSS}"/>
    3. </DataGrid.GroupStyle>


    Und zuletzt noch der GroupStyle_Selector selbst:
    Da wird jetzt etwas unschön, weil mir kein Weg einfiel, um z.B. Application.Expand_alle als Parameter zu senden, bzw. daran zu binden. Dieser Wert wird von einem Button umgeschaltet und dann die View aktualisiert. Das Ganze wird für 2 verschiedene Fenster / UserControls benötigt. daher der Enum, der als Parameter gesendet wird und bestimmt welche Variable abgefragt wird.

    VB.NET-Quellcode

    1. Public Enum ExpandStyle_enum
    2. Warteliste
    3. Meldungen
    4. End Enum
    5. Public Class GroupStyle_Selector_Klasse
    6. Inherits StyleSelector
    7. Public Property Expand_alle_Style As Style
    8. Public Property Expand_getrennt_Style As Style
    9. Public Property Sender As ExpandStyle_enum
    10. Public Overrides Function SelectStyle(Item As Object, Container As DependencyObject) As Style
    11. Select Case Sender
    12. Case ExpandStyle_enum.Warteliste
    13. If Application.Expand_alle Then Return Expand_alle_Style
    14. Return Expand_getrennt_Style
    15. Case ExpandStyle_enum.Meldungen
    16. If Application.Expand_alle_Fenster Then Return Expand_alle_Style
    17. Return Expand_getrennt_Style
    18. Case Else
    19. Return Nothing
    20. End Select
    21. End Function
    22. End Class


    Die Lösung ist nicht perfekt, funktioniert aber. (MVVM ist für mich kein Thema)
    Gruß
    eddi
    naja, noch sehe ich nix, was meiner Vorstellung von MVVM krass widersprechen würde.
    Tatsächlich scheint mir post#3 recht genau umgesetzt (link habich inzw. repariert).
    Allerdings verwendest du StyleSelector als Basisklasse, wo im Link ein GroupStyleSelector verwendet ist.

    Jdfs. zu MVVM: Spannend wäre nun, den Button-Code mal zu sehen - wärest du so nett?
    Habe erst jetzt entdeckt, dass es noch Antworten gibt. Hier der ICommand und die Sub dazu.

    VB.NET-Quellcode

    1. Public Property But_Expanded_com As ICommand = New RelayCommand_Klasse(AddressOf Expander_umschalten, AddressOf Canexecute)
    2. Private Sub Expander_umschalten(obj As Object)
    3. Application.Expand_alle = Not Application.Expand_alle
    4. View.Refresh()
    5. End Sub


    XML-Quellcode



    Der Button schaltet einfach nur den globalen Wert um, der im Selector abgefragt ist und leitet ein Refresh der View ein. Weil der Selector für 2 unterschiedliche Fenster im Einsatz ist, gibt es 2 globale Variablen. So lässt es sich getrennt umschalten.
    Warum ich StyleSelector statt GroupStyleSelector verwende kann ich nicht mehr nachvollziehen. Es wurde so viel im Netz gestöbert und ausprobiert...
    Den link seh ich mir dann die Tage noch an. Vielleicht findet sich da noch was Interessantes.

    Gruß
    eddi
    Jo, das ist doch MVVM - zumindest der Versuch.
    Und ist ganz unbestritten ziemlich hässlich, oder?
    Das finde ich bleibt festzuhalten, dass es vielleicht manche Dinge gibt, die auch mit MVVM nicht schön lösbar sind.

    Vielleicht aber ist das Viewmodel hier auch nicht der richtige Ort für die Lösung - sondern die sollte ganz im View verbleiben.
    Mit anderen Worten: dass diese ungewöhnliche Anforderung tatsächlich mal ins Code-Behind des Views gehört (das wäre für mich eine Premiere).

    Aber nur mit "Vielleicht". Also müsste man ergebnisoffen ausprobieren, und ggfs. auch wieder rückbauen.
    (Wenn man überhaupt gewillt ist, da noch weiter zu investieren, obwohl eine Lösung ja schon da ist.)
    und da waren sie wieder, meine 3 Probleme. Der GroupStyleSelector löst das Problem so, wie ich mir ursprünglich vorgestellt hatte. Er wählt im Code eine Resource und über gibt sie dann als Groupstyle.
    Leider scheitert es schon an der Klasse und dem Vererben! "Inherits GroupStyleSelector" meldet dann "Klassen können nur von Klassen erben".
    Die Klasse wurde zuerst komplett eigenständig in dem Programm angelegt und als Versuch im CodeBehind des UserControls.

    Problem 2 kommt dann später auf mich zu. Der Groupstyle bindet auf ungewöhnliche Art, weil er in einem DataGrid sitzt.

    XML-Quellcode

    1. <Expander IsExpanded="{Binding DataContext.Expanded, Source={x:Reference dummyElement}}"

    Bindet man ganz normal auf das Property "Expanded", dann funktioniert es einfach nicht. Hatte damals nach längerer Suche diese Lösung gefunden. (das Gleiche Problem besteht auch z.B. beim Binden auf Eigenschaften der Column usw., nur so als Fakt nebenbei)
    Aufgrund dieser Bindung kann ich den Style nicht einfach in die "Application" verschieben. Dort aber sucht der Selector danach (soweit ich richtig verstnaden habe).

    VB.NET-Quellcode

    1. Return CType(Application.Current.Resources("listViewGroupStyle"), GroupStyle)


    Das Programm funktioniert zwar, aber auf den GroupStyleSelector bin ich trotzdem neugierig. Der ursprünglich Code ist in C# geschrieben. Das sollte aber doch bei der Vererbung nichts ändern. Die Klasse "GroupStyleSelector" müsste doch Teil des XAML sein (also ich meine sie ist für die Controls zuständig) und sollte somit für C# und VB.net gleich sein / heissen?
    In einem komplett neuen und bis auf den GroupStyleSelector leeren Projekt tritt der gleiche Fehler auf.
    Auch ein extra erstelltes C# Projekt meldet Fehler an der Stelle.
    Hatte dann die Importierten Namespaces in Verdacht, aber das führte auch zu nix.
    Das ist mir den Aufwand nicht mehr wert! Das Programm funktioniert ja zum Glück.

    Danke für die Hilfe

    Gruß
    eddi
    hmm - jetzt ergoogle ich was ganz anneres als zuvor:
    learn.microsoft.com/en-us/dotn…r?view=windowsdesktop-7.0
    und
    learn.microsoft.com/en-us/dotn…r?view=windowsdesktop-7.0

    Das heisst, es gibt eine databindable Property ItemsControl.GroupStyleSelector des Datentyps GroupStyleSelector.
    Dabei ist der Datentyp GroupStyleSelector ein Delegat, welcher für die Argumente (CollectionViewGroup group, int level) einen GroupStyle returnt.

    Deine Fehlermeldung ist also richtig, GroupStyleSelector ist keine Klasse.
    Eine Klasse GroupStyleSelector, finde ich aber auch, aber da steht iwas von WinRT - vielleicht ist das eine andere Technologie. und nicht das Xaml, was wir so kennen (oder auch nicht).
    Schon vor Erstellung des Beitrags hier war mir der GroupStyleSelector in einigen Foren begegnet. Klappte halt nicht. Unter deinem Link gab's aber noch neue und genauere Infos dazu. Naja, Versuchs wars wert.
    U.u. lässt sich mit der folgenden Zeile noch irgenwo etwas anfangen.

    VB.NET-Quellcode

    1. Return CType(Application.Current.Resources("listViewGroupStyle"), GroupStyle)

    Wie ich mich kenne packt mich in Kürze doch wieder die Neugier und es folgen noch ein paar Tests.

    Danke für die Hilfe und Mühe.