WrapPanel als ItemPanel scrollbar machen

  • WPF

Es gibt 13 Antworten in diesem Thema. Der letzte Beitrag () ist von PadreSperanza.

    WrapPanel als ItemPanel scrollbar machen

    Täglich grüßt das Murmeltier :D

    so, ich habe nun soweit schon eine ganze Menge umgesetzt bekommen und auch eine Collection mit Collections verwursteln können und das auch ohne großen Aufwand. Nun habe ich in der inneren ListView das ItemPanelTemplate als WrapPanel gesetzt und es tut auch, was es soll. Es wrapped, wenn es am Ende angekommen ist. Nun habe ich ein weiteres Phänomen, das mich wahnsinnig macht. Wenn ich am Rand (außerhalb des WrapPanels) scrolle, scrollt meine äußere ListView ohne weiteres von oben nach unten (und umgekehrt). Ist mein Cursor jedoch überhalb des WrapPanels, kann ich nicht mehr scrollen.

    Auch das Deaktivieren der ScrollViewer-Eigenschaften auf der inneren Listview, das Nutzen einer einzigen äußeren ScrollViewer-Instanz bringen hier keine Abhilfe. Ich verstehe auch, dass ich nun über dem WrapPanel bin und deshalb die ListView ja nicht weiß, dass ich bei ihr scrollen möchte. Also habe ich versucht, mittels "MouseWheel" dran zu kommen, aber auch hier schaffe ich es nicht, das Scrollen an die äußere ListView zu geben. Die Bilder verdeutlichen vielleicht, was ich meine.

    hier der XAML:

    XML-Quellcode

    1. <ListView Grid.Row="1" DataContext="{StaticResource ViewProgramManager}" x:Name="OuterListView" ItemsSource="{Binding ProgramManagerView }" IsSynchronizedWithCurrentItem="True"
    2. ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
    3. <ListView.Resources>
    4. <!--Inner Template for ProgramManager-->
    5. <DataTemplate x:Key="InnerTemplateForManagedApps">
    6. <Border BorderBrush="LightGray" BorderThickness="1" CornerRadius="1,0,0,0" Padding="4" Margin="5" Width="{Binding Source={StaticResource AppSettings}, Path=ManagedAppWidth, Converter={StaticResource AppWidthAndHeightToSizeConverter}}"
    7. Height="{Binding Source={StaticResource AppSettings}, Path=ManagedAppHeight, Converter={StaticResource AppWidthAndHeightToSizeConverter}}">
    8. <DockPanel>
    9. <TextBlock DockPanel.Dock="top" Text="{Binding Description}" TextWrapping="Wrap" FontSize="{Binding Source={StaticResource AppSettings}, Path=ManagedAppFontSize}"/>
    10. <DockPanel DockPanel.Dock="Bottom">
    11. <CheckBox DockPanel.Dock="Right" />
    12. <Button Content="Öffnen" FontSize="{Binding Source={StaticResource AppSettings}, Path=ManagedAppFontSize}"/>
    13. </DockPanel>
    14. <Image Source="pack://application:,,,/images\other\404.png" Width="{Binding Source={StaticResource AppSettings}, Path=ManagedAppImageSize, Converter={StaticResource ImageSizeConverter}}"
    15. HorizontalAlignment="{Binding Source={StaticResource AppSettings}, Path=ManagedAppImageAlignment, Converter={StaticResource ImageAlignmentConverter}}"/>
    16. </DockPanel>
    17. </Border>
    18. </DataTemplate>
    19. </ListView.Resources>
    20. <ListView.ItemTemplate>
    21. <DataTemplate>
    22. <Expander Header="{Binding Header}">
    23. <ListView ItemsSource="{Binding ManagedCollection}" ItemTemplate="{StaticResource InnerTemplateForManagedApps}"
    24. SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
    25. <ListView.ItemsPanel>
    26. <ItemsPanelTemplate>
    27. <WrapPanel ScrollViewer.CanContentScroll="True" ScrollViewer.IsDeferredScrollingEnabled="True" MouseWheel="WrapPanel_MouseWheel"/>
    28. </ItemsPanelTemplate>
    29. </ListView.ItemsPanel>
    30. </ListView>
    31. </Expander>
    32. </DataTemplate>
    33. </ListView.ItemTemplate>
    34. </ListView>
    Bilder
    • nicht_scrollbar.png

      42,65 kB, 652×427, 143 mal angesehen
    • scrollbar.png

      39,96 kB, 612×403, 141 mal angesehen
    • soll.png

      56,4 kB, 821×666, 149 mal angesehen

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

    Anhand der Bilder kann ich nicht genau erkennen was der Sinn eines ListView INNERHALB eines Listview ist.
    Also bei dir hat jedes ListView-ITEM ein ListView und dann wird der Scrollvorgang an dieses übergeben.

    Ich sehe aber anhand der Bilder jetzt nicht ob du dies so benötigst. Evtl. kannst du ja ein kleines Minibeispiel hochladen welches das veranschaulicht, evtl. denkst du auch gerade bezüglich DataTemplate in die falsche richtung.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Also das äußere ListView ist das äußere (dies enthält auch die ScrollBar rechts im Bild zu sehen).
    Das erste Element in dieser ListView wird dargestellt durch den Expander (mit Titel "erster Test"). Innerhalb dieses Objekts sind vier Objekte des Typs 1 enthalten. Diese bekommen ein DataTemplate zugewiesen und werden deshalb so dargestellt wie sie im Bild zu sehen sind. Das (innere) ListView habe ich so eingestellt, dass es als WrapPanel fungiert, damit diese vier Objekte gebrochen werden und untereinander angeordnet werden können.
    Das nächste Objekt der äußeren ListView enthält einen Expander (unten zu sehen mit dem Titel "zweiter Test") und dort sind andere Objekte (teils auch anderer Typen enthalten). Auch diese werden im inneren mittels WrapPanel umgebrochen, damit sie alle die gleichen Größen und Layouts aufweisen. Das aber ist auch eigtl nur kosmetischer Natur.

    Mein Problem ist, dass wenn mehrere Hauptobjekte vorhanden sind (hier dargestellt als Elemente mit Expandern), man zu den unteren runterscrollen muss. Was auch ohne Probleme funktioniert - jedenfalls solange sich die Maus nicht überhalb einer inneren ListView befindet.im Beispielbild scrollbar zu sehen. Sobald ich nun über die innere ListView gehe, kann ich gar nicht mehr scrollen, was daran liegt, dass die innere ListView keinen Scrollviewer hat (genaugenommen ist ScrollViewer.HorizontalScrollBarVisbillity="Disabled") und die Items - weil das WrapPanel sich nun am Content-Bereich des äußeren Controls ausrichtet - umbricht, statt sie in einer Reihe anzuzeigen.

    Ich hätte aber gerne, dass ich immer die äußere ListView scrollen kann, auch wenn ich über der inneren View scrolle.

    Ich hoffe, das ist nun verständlicher erklärt.
    Oder um an deine Antwort anzuknüpfen: Kann ich das Übergeben des Scrollvorgangs an die innere ListView verhindern?

    Dass meine Objekte in der äußeren ListView jeweils eine ListView haben ist auch tatsächlich so gewünscht und würde von mir auch so benötigt :)
    Sorry, ich sehe trotzdem noch nicht den Sinn einer ListView innerhalt eines ListView-Items.

    Wäre es viel Arbeit ein Beispiel hochzuladen, leider bin ich im Moment etwas eingespannt und habe vermutlich die nächsten Tage nicht die Zeit eines Nachzubauen.
    Oder Poste zumindest bitte die ViewModel Klasse welche zu dem gezeigten XAML gehört damit ich das so nachbauen kann wie deine Konstellation gerade ist ohne das ich großartig selbst viel Tippen muss.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    ich kann dir das morgen mal versuchen entschlackt zur Verfügung zu stellen.

    Ich habe im Beitrag oben nochmal ein weiteres Foto hinzugefügt. Da ist dann der Soll-Zustand ersichtlich. Das ist aus der alten Version, die noch klassisch nach "alter Programmiertechnik" erstellt worden ist. Ich wollte das nun ins MVVM-Pattern übersetzen und noch einige weitere Features hinzufügen. Dort sind dann unterschiedliche Objekte abgelegt, die man daraus öffnen kann.

    Dort ist das einfach nur ein Expander (abgeleitet), der dynamisch erzeugt wird. Da ich zur Laufzeit aber nicht weiß, was drin ist, erstelle ich das mithilfe von Collections, die dann die ListViews füllen, damit es so aussieht wie auf dem dritten Bild. Deshalb nutze ich eine ListView (OuterView), um die Elemente abfragen zu können und die innere View, um mittels Binding auf alles zugreifen zu können. Es funktioniert soweit auch gut und dadurch spare ich mir auch mehr als 30% Code ein, der im alten Projekt noch händisch gebaut wurde. Nur stoße ich immer mal wieder auf Dinge, die mich stören, oder über die ich stolpere. Wie zum Beispiel hier, dass das WrapPanel das Scrolling im Parent nicht unterstützt oder weitergibt
    Ahhhhhhhhh

    Bei deinem letzten Bild ist mir erst ein Licht aufgegangen.
    Du hättest dir lieber mal meine Tutorialreihe angesehen. Da habe ich ein Kapitel zu Binding an Collections.

    Da gehe ich auch sorgfältig auf Sortieren, Filtern und Gruppieren ein. Und Gruppieren ist genau das was du willst. Nur wusstest du das nicht und machst dies im Moment komplett händisch und umständlich.

    Anbei mal ein Beispiel wie man das macht ohne viel Code und/oder komplizierten und Abenteuerlichen XAML. Dann gibts auch die von dir erwähnten Probleme nicht.
    Habe mal in einem rudimentären Testprojekt Grouping und Filtering implementiert.

    Auf folgendem Screenshot sieht man das ganze Usercontrol des Views, mehr ist es nicht um soetwas zu implementieren.



    Ich hoffe das war das was du brauchtest.

    Grüße
    Sascha
    Dateien
    • MVVMPadreTest.zip

      (226,21 kB, 128 mal heruntergeladen, zuletzt: )
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Erstmal vielen Dank für die ganze Mühe, die du dir gemacht hast. Echt Klasse!!

    Ja, das ist quasi das, was ich erstellen möchte.

    Deinen Lösungsansatz habe ich verstanden. Nur da ich die Gruppen nicht als Enumeration erstellen kann (da die Nutzer eigene Gruppen definieren können), kann ich sie auch als String implementieren und danach filtern. Soweit wäre das kein Problem und funktioniert mit deinem Ansatz auch super.
    Ich weiß nur noch nicht, ob es meinen Ansprüchen genügt. Denn ich müsste die Daten ja dann wieder speichern und möchte einen gewissen Komfort mit speichern. Hierzu gehört auch, dass ich den letzten Status speichere, etc. Eventuell muss ich das sonst einfach anders machen. Dazu muss ich mir dein Projekt nochmal genau ansehen.
    Was ich aber deutlich sehen kann: Du nutzt ein UserControl, um die Apps anzuzeigen. Das hatte ich im alten Programm auch so. Ich dachte nur, dass ich mich davon trennen müsse, um das MVVM-Pattern umsetzen zu können. Das scheint aber doch nicht der Fall zu sein. Dann kann ich zumindest diese Designschritte wieder auslagern und damit das Main-Window wieder schlanker gestalten.
    Die nächste Sache, die ich gesehen habe: Ich habe wohl noch einen groben Schnitzer im MVVM-Pattern gehabt. Bei mir gibt es zwar Model, View und ViewModel, aber sie liegen alle im selben Projekt. Das ist bei dir auch anders gelöst. Und sieht auch eleganter aus.


    Und dann bin ich wieder bei einer anderen Frage angekommen: Wenn ich INotifyPropertyChanged implementiere, ist das dann der bessere Weg als eine DependencyProperty zu registrieren oder sind sie gleichbedeutend?
    OK, ich versuchs mal:

    PadreSperanza schrieb:

    Nur da ich die Gruppen nicht als Enumeration erstellen kann (da die Nutzer eigene Gruppen definieren können), kann ich sie auch als String implementieren und danach filtern.

    Für die Gruppierung ist das unerheblich. Wenn du genau schaust habe ich garnicht nach dem Enumerator Gruppiert sondern eben nach einen String ;)
    Hab ich dich erwischt.

    PadreSperanza schrieb:

    Ich weiß nur noch nicht, ob es meinen Ansprüchen genügt. Denn ich müsste die Daten ja dann wieder speichern und möchte einen gewissen Komfort mit speichern.

    Und, hast das eine mit dem anderen nichts zu tun. Dann speichere, was hält dich davon ab? Es ist ja von den Daten her nun nichts anders als vorher.

    PadreSperanza schrieb:

    Was ich aber deutlich sehen kann: Du nutzt ein UserControl, um die Apps anzuzeigen. Das hatte ich im alten Programm auch so. Ich dachte nur, dass ich mich davon trennen müsse, um das MVVM-Pattern umsetzen zu können.

    Wer hat dir das gesagt?
    MVVM bedeutet nur das es eine Trennung Zwischen den Schichten geben soll/muss. Nicht mehr und nicht weniger. Wann, Wo und wie du Usercontrols erstellst bleibt völlig dir überlassen.
    Auch wieviele Unter-ViewModels du machen möchtest um eben den Überblick zu behalten bzw. alles sauber zu halten.

    Ob du nun alles in ein Projekt packst ist zum einen Geschmacksache und zum andern Anforderung an wartbarkeit, Update-Routinen, UnitTest-Notwendigkeit.
    Ich habe übrigens ein Template hier im Forum im Tipps Bereich das dir viel abnimmt.

    In ViewModels NUR INotifyPropertyChanged!!!
    DependencyProperties haben nur in Views etwas verloren und haben im Grunde einen völlig anderen Einsatzzweck. aber das habe ich ales in meiner Tutorialreihe drinnen, für DP gibts auch ein eigenes Kapitel.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Okay, das hat mir jetzt dann doch mehr Arbeit erspart als ich dachte :D

    Na ja, ich bin erst seit 4 Wochen mit dem MVVM-Pattern so richtig am Werkeln und habe mir das alles nur durch Bücher und eure Hilfe hier im Forum angeeignet. Also fehlt mir da noch einiges. Das ich nachholen muss (und auch werde)

    In der alten Version habe ich quasi die Gruppennamen gespeichert, sie abgerufen und daraus jeweils einen Expander erstellt und diese dann mit den Daten gefüllt (alles noch rein Code-behind). Hier hatte ich den Gruppennamen vorrätig, weil vorher eine Gruppe ja erst erstellt werden musste. Und so konnte ich auch den Status (IsExpanded) und weitere Sachen speichern.
    Das heißt, wenn ich jetzt so etwas machen möchte, müsste ich ja durch Items laufen und deren Gruppennamen abfragen, nach denen ich nach deiner Version gruppiere, und diese dann speichere. Aber wie komme ich an den Expander an, um ihn wieder anzusprchen? Wahrscheinlich indem ich ein Item nehme und auf dessen VisualTree nach oben laufen bis ich ein Element vom Typ Expander finde?

    Ich hab den Code gerade nicht vor Augen. Aber ich werde mich da dran setzen und ihn nach deinen Hilfestellungen umbauen. danach melde ich mich nochmal - Aber ein paar neue, interessante Ansätze hast du mir schon mal wieder aufzeigen können :)
    Du musst da garnix durchlaufen. Das passiert alles automatisch. Schau dir mein Projekt an.
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Ne, jetzt verstehst du mich - glaube ich - falsch.

    Wenn ich das Programm starte, dann sollen die Expander geöffnet sein, die beim Schließen geöffnet waren und alle anderen nicht. Da habe ich aus deinem Projekt noch nicht raussehen können, wie ich diese Eigenschaften setzen kann.
    Und genau dafür hatte ich im alten Projekt eine Liste mit allen Gruppennamen - und bin diese dann einfach durchgelaufen, da ich die Expander ja mit Namen ansprechen konnte. Der Expander in deinem GroupStyle aber ist zwar im VisualTree vorhanden, aber wie kann ich diesen ansprechen, um ihn zum Beispiel codetechnisch zu benutzen.

    Wenn ich ihm einen x:Name gebe, ist das ja einfach. Aber das ganze würde ja dynamisch laufen. das heißt, zur Designzeit weiß ich ja weder wie viele Gruppen es gibt, noch wie sie heißen. Wie also dann ansprechen.


    Aber wie gesagt, ich muss das erstmal auf mein Projekt anpassen und adaptieren Das dauert jedoch. Aber ich melde mich. Und dann schaue ich weiter, was mir dann für Steine im Weg geblieben sind :)
    Achsooooo

    OK, jetzt weis ich was du meinst.

    Ja, das ist ein wenig Tricky da im Falle des Expander ein Binding auf dessen Eigenschaft "IsExpanded" zeigt das für diesen ein Objekt vom Typ CollectionViewGroupInternal als DatenKontext gilt.

    Aber das können wir uns doch zur nutze machen. In diesem Objekt stecken die Elemente welche in der aktuellen Gruppe enthalten sind. Gut, also in unsere Klasse eine Eigenschaft Expanded gepackt und diese kannst du nun beim einlesen setzen.

    Dann einen Converter dazwischen setzen und dann (in diesem Fall hab ichs mal so gemacht) abfragen ob mindestens ein Objekt der Auflistung die Eigenschaft Expanded gesetzt hat.
    Dann noch in unseren Konstruktor einen Parameter (optional) gepackt und gut.

    Sieht dann so aus:

    VB.NET-Quellcode

    1. Public Class GroupingExpanderConverter
    2. Implements IValueConverter
    3. Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
    4. Dim retValue = False
    5. Dim val = CType(value, ReadOnlyObservableCollection(Of Object))
    6. Return val.Any(Function(x) DirectCast(x, ListItemViewModel).Expanded)
    7. End Function
    8. Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
    9. Throw New NotImplementedException()
    10. End Function
    11. End Class


    Binding dann:

    XML-Quellcode

    1. <Expander IsExpanded="{Binding Items, Converter={StaticResource GroupingExpanderConverter},Mode=OneWay}">


    Ich pack dir das Projekt wieder rein.

    PS: Man muss einfach nur schaun worauf das binding zeigt, dann kommt der Rest eh von selbst.

    Grüße
    Sascha
    Dateien
    • MVVMPadreTest.zip

      (229,41 kB, 127 mal heruntergeladen, zuletzt: )
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Sooo...

    ich hab es hinbekommen :D

    Ich habe mich anhand deiner Vorlage orientiert und es fast so hinbekommen wie du (siehe Bild).
    Ich musst nur bei meinem ListView das Template direkt in das ItemTemplate packen und habe vorher ein UserControl daraus gemacht. Dem habe ich DependencyProperties spendiert, dann kann ich die Einstellungen für die App-Breite und Höhe an das Objekt binden im Template. Klappt super :)

    Den Converter mit dem Expander habe ich noch nicht erstellt. Den schaue ich mir dann erstmal gründlich an, um ihn auch zu verstehen.

    Hab vielen Vielen Dank für deine Mühen und vor allem für die unglaubliche Hilfe :)

    Das genaue Design (MouseOver, Border, Margin, etc.) wird natürlich noch überarbeitet, damit es schöner aussieht. Aber so finde ich es schon mal sehr elegant
    Bilder
    • grouped.png

      84,83 kB, 1.228×941, 133 mal angesehen