DataGrid Binden an Klasse

  • WPF

Es gibt 21 Antworten in diesem Thema. Der letzte Beitrag () ist von Nofear23m.

    DataGrid Binden an Klasse

    Hallo liebes Forum,
    heute habe ich ein sehr komplexes Problem.

    In meinem Fenster habe ich ein DataGrid mit DataTemplates

    XML-Quellcode

    1. <DataGrid AutoGenerateColumns="False" x:Name="dg_data" Margin="10,10,10,10.334" ItemsSource="{Binding Consumers}"
    2. AlternatingRowBackground="Gainsboro" AlternationCount="2"
    3. CanUserReorderColumns="False" CanUserResizeColumns="True"
    4. CanUserResizeRows="False" CanUserSortColumns="True"
    5. CanUserAddRows="False" CanUserDeleteRows="False"
    6. SelectionChanged="Dg_data_SelectionChanged"
    7. SelectionMode="Single" SelectionUnit="FullRow" Grid.Row="2" >
    8. <DataGrid.Columns>
    9. <DataGridTemplateColumn CanUserSort="True" Header="Datenherkunft">
    10. <DataGridTemplateColumn.CellTemplate>
    11. <DataTemplate>
    12. <TextBlock Padding="7,3" Text="{Binding DataCollectorID}"/>
    13. </DataTemplate>
    14. </DataGridTemplateColumn.CellTemplate>
    15. </DataGridTemplateColumn>
    16. <DataGridTemplateColumn CanUserSort="True" Header="Stockwerk">
    17. <DataGridTemplateColumn.CellTemplate>
    18. <DataTemplate>
    19. <TextBlock Padding="7,3" Text="{Binding Floor}"/>
    20. </DataTemplate>
    21. </DataGridTemplateColumn.CellTemplate>
    22. </DataGridTemplateColumn>
    23. <DataGridTemplateColumn CanUserSort="True" Header="Raum">
    24. <DataGridTemplateColumn.CellTemplate>
    25. <DataTemplate>
    26. <TextBlock Padding="7,3" Text="{Binding RoomNumber}"/>
    27. </DataTemplate>
    28. </DataGridTemplateColumn.CellTemplate>
    29. </DataGridTemplateColumn>
    30. <DataGridTemplateColumn CanUserSort="True" Header="Gerät">
    31. <DataGridTemplateColumn.CellTemplate>
    32. <DataTemplate>
    33. <TextBlock Padding="7,3" Text="{Binding DeviceID}"/>
    34. </DataTemplate>
    35. </DataGridTemplateColumn.CellTemplate>
    36. </DataGridTemplateColumn>
    37. <DataGridTemplateColumn CanUserSort="True" Header="Anzahl">
    38. <DataGridTemplateColumn.CellTemplate>
    39. <DataTemplate>
    40. <TextBlock Padding="7,3" Text="{Binding DeviceCount}"/>
    41. </DataTemplate>
    42. </DataGridTemplateColumn.CellTemplate>
    43. </DataGridTemplateColumn>
    44. <DataGridTemplateColumn CanUserSort="True" Header="Zeitbereich">
    45. <DataGridTemplateColumn.CellTemplate>
    46. <DataTemplate>
    47. <TextBlock Padding="7,3" Text="{Binding TimeAreaID}"/>
    48. </DataTemplate>
    49. </DataGridTemplateColumn.CellTemplate>
    50. </DataGridTemplateColumn>
    51. </DataGrid.Columns>
    52. </DataGrid>



    Dieses DataGrid wird an eine ObservableCollection "Consumers" gebunden, die ich im Code Behind definiere.

    C#-Quellcode

    1. ObservableCollection<Consumer> Consumers { get; set; }
    2. List<Device> Devices { get; set; }
    3. List<DataCollector> DataCollectors { get; set; }
    4. List<TimeArea> TimeAreas { get; set; }
    5. List<Room> Rooms { get; set; }
    6. List<DeviceGroup> DeviceGroups { get; set; }
    7. public win_overview()
    8. {
    9. InitializeComponent();
    10. openFile();
    11. }
    12. public void openFile()
    13. {
    14. Consumers = new ObservableCollection<Consumer>();
    15. //ObservabelCollection wird befüllt ...
    16. dg_data.DataContext = this;
    17. }


    Hier einmal die Klasse Consumer

    C#-Quellcode

    1. public class Consumer
    2. {
    3. public int ID { get; set; }
    4. public int DeviceID { get; set; }
    5. public int TimeAreaID { get; set; }
    6. public int DataCollectorID { get; set; }
    7. public int RoomID { get; set; }
    8. public int DeviceCount { get; set; }
    9. }



    Nun möchte ich aber, dass in dem DataGrid statt der ID der zugehörige Text eines Objekts angezeigt wird.
    Beispiel:

    Ich habe in der Klasse Consumer die Eigenschaft RoomID, darin ist eine Zahl gespeichert.
    In dem DataGrid soll nun die Eigenschaft Floor + RoomNumber des Objects Room angezeigt werden, dass die gleiche ID hat.
    Die ganzen Room Objekte sind in einer Liste im Code Behind gespoeichert (s.o.

    C#-Quellcode

    1. public class Room
    2. {
    3. public int ID { get; set; }
    4. public string Floor { get; set; }
    5. public string RoomNumber { get; set; }
    6. }



    Ich weiß, das klingt sehr kompliziert, kann mir aber trotzdem jemand weiterhelfen?

    Viele Grüße
    Florian
    ----

    WebApps mit C#: Blazor

    flori2212 schrieb:

    Ich weiß, das klingt sehr kompliziert, kann mir aber trotzdem jemand weiterhelfen?


    Ne, gar nicht. Das ist ein Standardszenario für ein ViewModel.

    Du hast dein Model (welches so ist wie es ist) welches die "Consumer" hält und deine Models für die anderen Objekte.
    Die Anzeige im View soll aber anders aussehen da du in deinem Model die Datenstruktur nicht so drinnen hast wie es angezeigt werden soll.

    Also machst du ein ViewModel. Ein ViewModel ist eine Klasse welche die Daten läd (deine Instanzen der Model-Objekte) und aufbereitet bzw. sich dann auch um die speicherung, validierung usw. kümmert.
    Auf dieses ViewModel bindest du dann die View (anstatt auf die CodeBehind).

    In deinem konkreten Beispiel:

    Eine "ConsumerListViewModel" welche INotifyPropertyChanged implementiert, denn das sehe ich bei dir jetzt nirgens. (näheres in meiner Tutorialreihe Kapitel Binding)
    Weiters eine Klasse "ConsumerViewModel" auf mit INotidyPropertyChanged (am besten legst du dir eine Basisklasse an) welche die Eigenschaften enthält welche du im deinem DataGrid benötigst. Also nicht wie im Model die RoomID sondern eben Public Property Room As Room.
    Dieser Klasse kannst du im Konstruktor dann alle Infos mitgeben welche diese benötigt.

    Diese Klasse "ConsumerListViewModel" bekommt eine Eigenschaft Consumers vom Typ ObservableCollection(Of ConsumerViewModel)
    Im Konstruktor dieser Klasse erledigst du das erstellen alle "Consumer"-ViewModel Objekte.

    Jetzt nur noch das DataGrid drauf Bindind und fertich.

    Falls das nicht verständlich war kann ich evtl. ein kleines Beispiel machen.

    PS: Warum für jede Spalte ein DataTemplate wenn es eh nur Textblöcke sind? Da kannst du ja auch die DataGridTextColumn nehmen oder?

    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. ##

    Hallo Nofear23

    Erstmal vielen Dank für deine Antwort und dein Bemühen.
    Ich habe jetzt die beiden Klassen erstellt, wie du mir geraten hast:

    C#-Quellcode

    1. public class ConsumerViewModel : INotifyPropertyChanged
    2. {
    3. private Room _Room;
    4. public Room Room {
    5. get { return _Room; }
    6. set
    7. {
    8. _Room = value;
    9. RaisePropertyChanged("Room");
    10. }
    11. }
    12. private DataCollector _DataCollector;
    13. public DataCollector DataCollector {
    14. get { return _DataCollector; }
    15. set
    16. {
    17. _DataCollector = value;
    18. RaisePropertyChanged("DataCollector");
    19. }
    20. }
    21. private Device _Device;
    22. public Device Device {
    23. get { return _Device; }
    24. set
    25. {
    26. _Device = value;
    27. RaisePropertyChanged("Device");
    28. }
    29. }
    30. private TimeArea _TimeArea;
    31. public TimeArea TimeArea {
    32. get
    33. {
    34. return _TimeArea;
    35. }
    36. set
    37. {
    38. _TimeArea = value;
    39. RaisePropertyChanged("TimeArea");
    40. }
    41. }
    42. public ConsumerViewModel(Room room, DataCollector dataCollector, Device device, TimeArea timeArea)
    43. {
    44. Room = room;
    45. DataCollector = dataCollector;
    46. Device = device;
    47. TimeArea = timeArea;
    48. }
    49. // Hilfsklasse PropertyChanged
    50. public void RaisePropertyChanged(string property = "")
    51. {
    52. PropertyChanged(this, new PropertyChangedEventArgs(property));
    53. }
    54. public event PropertyChangedEventHandler PropertyChanged;
    55. }


    und

    C#-Quellcode

    1. public class ConsumerListViewModel : INotifyPropertyChanged
    2. {
    3. private ObservableCollection<ConsumerViewModel> _Consumers;
    4. ObservableCollection<ConsumerViewModel> Consumers {
    5. get { return _Consumers; }
    6. set {
    7. _Consumers = value;
    8. RaisePropertyChanged("Consumers");
    9. }
    10. }
    11. public ConsumerListViewModel(ObservableCollection<ConsumerViewModel> consumers)
    12. {
    13. Consumers = consumers;
    14. }
    15. // Hilfsklasse PropertyChanged
    16. public void RaisePropertyChanged(string property = "")
    17. {
    18. PropertyChanged(this, new PropertyChangedEventArgs(property));
    19. }
    20. public event PropertyChangedEventHandler PropertyChanged;
    21. }


    Nun verstehe ich nur noch folgendes nicht:

    1. Wie genau wandel ich meine Objekte vom Typ "Consumer" in "ConsumerViewModel" um
    2. Wo erstelle ich das Objekt "ConsumerListViewModel"? Einfach als Eigenschaft in der CodeBehind?

    3. Es wäre super freundlich wenn du mir eventuell kurz ein Beispiel XAML Code schreiben könntest, wie man jetzt im DataGrid zum Beispiel die Eigenschaft RoomNumber anzeigt.


    Viele Grüße
    Florian
    ----

    WebApps mit C#: Blazor

    flori2212 schrieb:

    Wie genau wandel ich meine Objekte vom Typ "Consumer" in "ConsumerViewModel" um

    Du kannst dem Konstruktor das Model-Objekt mitgeben. Umwandeln musst du im Grunde nicht.
    Die Daten der Rooms, Devices, usw. musst du eben auch mitgeben. Ich mache das meisst mit einem Tuple. Der Vorteil ist das der Konstruktor dann nicht zig Parameter haben muss.

    Wenn du eine Eigenschaft im ViewModel hast welche einer Eigenschaft im Model entspricht (in deinem Fall die ID z.b.) dann kannst du diese im Property durchreichen.
    Ein Property muss ja kein Backingfield haben, du kannst im Getter der ViewModel-Property den Wert des Property von Model-Objekt weiterreichen. Siehe mein Beispiel bei der ID des ConsumerViewModel.

    flori2212 schrieb:

    Wo erstelle ich das Objekt "ConsumerListViewModel"? Einfach als Eigenschaft in der CodeBehind?

    Oder schlicht im XAML. Siehe mein Beispiel im MainWindow. (Das geht aber nur wenn ein parameterloser Konstruktor vorhanden ist.

    flori2212 schrieb:

    Es wäre super freundlich wenn du mir eventuell kurz ein Beispiel XAML Code schreiben könntest, wie man jetzt im DataGrid zum Beispiel die Eigenschaft RoomNumber anzeigt.

    Anbei das Beispiel Aber hald in VB.Net anstatt C#.

    Grüße
    Sascha
    Dateien
    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. ##

    Hallo Nofear23M

    Vielen Dank für das Beispiel, dadurch wird mir einiges klarer.
    Ich werde morgen wohl oder übel mein Projekt nochmals umschreiben müssen.

    Vielen lieben Dank für deine Hilfe.

    Lg
    Florian
    ----

    WebApps mit C#: Blazor
    Hallo Nofear23M

    Ich habe mir heute morgen ausführlich deinen Beispielcode angeschaut und auch fast alles verstanden.

    Doch was sind das für Anweisungen mit dem "x" und dem "Where".
    Ich verstehe vom PRinzip was die machen, aber was für eine "Spracheigenschaft" ist den das? Hab ich bis jetzt noch nie gesehen

    VB.NET-Quellcode

    1. Friend Sub New(consumerModel As Model.Consumer, aviableData As (Devices As List(Of Model.Device), Rooms As List(Of Model.Room)), deviceCount As Integer)
    2. _consumerModel = consumerModel
    3. Device = aviableData.Devices.Where(Function(x) x.ID = _consumerModel.DeviceId).SingleOrDefault()
    4. Room = aviableData.Rooms.Where(Function(x) x.ID = _consumerModel.RoomId).SingleOrDefault()
    5. _deviceCount = deviceCount
    6. End Sub


    Genau so hier (aber das gehört ja indirekt zusammen)

    VB.NET-Quellcode

    1. TestConsumer.ForEach(Sub(x) AllConsumers.Add(New ConsumerViewModel(x, (TestDevices, TestRooms), 4)))




    So weit, so gut.



    Jetzt habe ich die Beispielanwendung ein wenig erweitert, habe ein Objekt hinzugefügt, mit dem Namen "SelectedConsumer" und ein Paar Textboxen neben das DataGrid, wo man die Eigenschaften eines Ausgewählten Consumers bearbeiten kann.
    Das funktioniert auch alles soweit.


    Nun habe ich aber das Problem, dass ich auch eine ComboBox hinzufügen will, in der ALLE verfügbaren "Devices" angezeigt werden, sodass man eines von denen auswählen kann. Nun weiß ich aber nicht, woran ich die ItemsSource Eigenschaft der ComboBox binden soll.

    Im Anhang das erweiterte Beipielprojekt.

    Viele Grüße
    Florian
    Dateien
    ----

    WebApps mit C#: Blazor
    Hallo

    Das ist LINQ mit Lamdba Audrücken.

    Function(x) x.prop = "test" ist in C#
    X => X.prop = "test"


    Da ich heute unterwegs bin kann ich dir das Beispiel erst morgen altualisieren.

    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. ##

    Hallo Florian

    SO, heute gehts wieder.
    Benötigst du nun eine genauere Erklärung von Lambda-Ausdrücken oder ist das soweit klar??

    flori2212 schrieb:

    Nun habe ich aber das Problem, dass ich auch eine ComboBox hinzufügen will, in der ALLE verfügbaren "Devices" angezeigt werden, sodass man eines von denen auswählen kann. Nun weiß ich aber nicht, woran ich die ItemsSource Eigenschaft der ComboBox binden soll.

    Ich dachte mir fast das dies als nächstes kommen wird. Aus diesem Grund habe ich dem Konstruktor von ConsumerViewModel gleich alle übergeben anstatt nur das Device oder den Raum der benötigt wurde. Hätte ja für das erste Beispiel gereicht. Aber ich dachte mir das dies kommen wird, weshalb ich die Liste übergeben hatte.

    So benötigst du im Grunde nur noch zwei Properties welche die List(Of Devices oder Rooms) hält, auf diese kannst du dann Binden.
    Wenn der Raum geändert wird musst du nur darauf achten das du brav die neue ID in dein Consumer Objekt (Das Model-Objekt) speicherst. Denn das geschied ja nicht von selbst. Ich habe dir das im Beispiel mal kommentiert.

    PS: Ich war so frei und habe dir das DataGrid auch gleich mit Comboboxen ausgestattet, ich denke das wäre die nächste Frage. :whistling:

    Grüße
    Sascha
    Dateien
    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. ##

    Hallo Sascha

    Nach einem langen Schultag gleich deine Antwort durchgelesen :) :) .


    Benötigst du nun eine genauere Erklärung von Lambda-Ausdrücken oder ist das soweit klar??


    Heute oder morgen bekomme ich ein neues C# Buch, wo dieser Bereich ausführlich behandelt wird, also erstmal nicht.

    So, weiter zu dem Projekt.
    Ich habe mir deine Umsetzung angeschaut, und soweit auch verstanden.

    Das mit dem DataGrid war lieb gemeint, und es ist auch gut, dass ich einmal gesehen habe, wie man so etwa umsetzt, aber ich will eh, dass man die Daten im DataGrid NICHT direkt bearbeiten kann (sondern in dem Feld neben dran).
    Dafür habe ich DataGridTemplateColounms benutzt (siehe Code)

    Ich habe das Projekt jetzt noch mit zwei Models erweitert, wie ich es brauche.

    So weit, so gut, doch jetzt habe ich leider schon wieder Probleme.

    1. Wie soll ich dem Nutzer die Möglichkeit geben, einen neuen Consumer einzugeben? In einem Dialog?

    2. Ich habe, wie im WPF Tutorial von dir gezeigt, eine CollectionViewSouce hinzugefügt, damit ich die "Consumer" nach RoomNumber grupieren kann. Aber ich scheitere schon beim sortieren (siehe Code).

    3. Ich will ein eigenes Fenster machen, in dem ich die "Devices" verwalten kann, also auch ein DataGrid, halt nur mit den Devices drine. Wie gehe ich da vor. Übergebe ich einfach die List<Device> an die neue Klasse? Oder brauche ich auch ein DeviceViewModel?

    Ich weiß, das sind schon wieder viele Fragen, aber wenn du Zeit findest wäre es echt schön, wenn du mir helfen könntest.

    Viele Grüße
    Florian

    --- Edit: Dateianhang vergessen :) --
    Dateien
    ----

    WebApps mit C#: Blazor
    Hallo

    flori2212 schrieb:

    Ich weiß, das sind schon wieder viele Fragen, aber wenn du Zeit findest wäre es echt schön, wenn du mir helfen könntest.

    In der tat einige Fragen die zum Teil so pauschal nicht beantwortet werden könnnen. Da kommst es immer darauf an.

    flori2212 schrieb:

    Wie soll ich dem Nutzer die Möglichkeit geben, einen neuen Consumer einzugeben? In einem Dialog?

    Da musst DU wissen. Das bleibt dir als Programmierer/Designer selbst überlassen. Die einen mögen Dialoge, die anderen eher nicht. Ich bin der Freund von Dialogen da immer alles brav getrennt ist und der User auch genau weis was was eingegeben werden muss und was was gespeichert wird/wurde. Du kannst aber auch einen Button "New Record" haben wo ein neuer Consumer hinzugefüt wird, dann sind die Textboxen leer und der User muss die Daten eingeben.

    flori2212 schrieb:

    Ich habe, wie im WPF Tutorial von dir gezeigt, eine CollectionViewSouce hinzugefügt, damit ich die "Consumer" nach RoomNumber grupieren kann. Aber ich scheitere schon beim sortieren

    Du scheiterst weil die deine Klassen anscheinend nicht kennst. Mach mal deine Klasse Room aus und du wirst sehen warum das nicht klappt.

    XML-Quellcode

    1. <!--Warum geht das nicht, dei Sortierung???-->
    2. <componentModel:SortDescription PropertyName="Room.Floor"/>

    Weil es die Eigenschaft Floor nicht gibt!
    Das zeigt die auch beim ausführen der App die Ausgabe schön an. (diese bitte immer im Auge behalten!!!)
    Spoiler anzeigen

    Quellcode

    1. System.Windows.Data Error: 40 : BindingExpression path error: 'Floor' property not found on 'object' ''Room' (HashCode=62074597)'. null
    2. System.Windows.Data Error: 40 : BindingExpression path error: 'Floor' property not found on 'object' ''Room' (HashCode=61986480)'. null
    3. System.Windows.Data Error: 40 : BindingExpression path error: 'Floor' property not found on 'object' ''Room' (HashCode=21007413)'. null
    4. System.Windows.Data Error: 40 : BindingExpression path error: 'Floor' property not found on 'object' ''Room' (HashCode=21007413)'. null
    5. System.Windows.Data Error: 40 : BindingExpression path error: 'Floor' property not found on 'object' ''Room' (HashCode=61986480)'. null
    6. System.Windows.Data Error: 40 : BindingExpression path error: 'Floor' property not found on 'object' ''Room' (HashCode=61986480)'. null
    7. System.Windows.Data Error: 40 : BindingExpression path error: 'Floor' property not found on 'object' ''Room' (HashCode=62074597)'. null
    8. System.Windows.Data Error: 40 : BindingExpression path error: 'Floor' property not found on 'object' ''Room' (HashCode=54848996)'. null
    9. System.Windows.Data Error: 40 : BindingExpression path error: 'Floor' property not found on 'object' ''Room' (HashCode=61986480)'. null

    Die Ausgabe zeigt dir das ein Property mit dem Namen 'Floor' im Objekt "Room" nicht vorhanden ist. Und das stimmt auch. Ist nämlich "FloorNumber" bzw. "RoomNumber"!

    XML-Quellcode

    1. <CollectionViewSource.SortDescriptions>
    2. <componentModel:SortDescription PropertyName="Room.FloorNumber"/>
    3. </CollectionViewSource.SortDescriptions>



    flori2212 schrieb:

    Ich will ein eigenes Fenster machen, in dem ich die "Devices" verwalten kann, also auch ein DataGrid, halt nur mit den Devices drine. Wie gehe ich da vor. Übergebe ich einfach die List<Device> an die neue Klasse? Oder brauche ich auch ein DeviceViewModel?

    Das kommt darauf an. Ich sag mal - klappen würde beides. Es kann nur später zu problemen kommen sobald es nur ein wenig komplexer wird da 1.) eine List(Of T) im gegensatz zur ObservableCollection(Of T) kein INotifyCollectionChanged implementiert und 2.) die einzelnen Properties der Modelklasse kein iNotifyPropertyChanged.PropertyChanged werfen und du somit änderungen am Property im View nicht mitbekommst.
    Das fällt in diesem Fall vermutlich gar nicht auf, später wundert man sich aber warum das nicht klappt.

    Es ist ein wenig mehr Arbeit aber ich mache es immer so (und empfehle es immer) eine ViewModel-Klasse für ALLES zu machen was in einem View angezeigt wird.
    Ich würde es ManageDevicesViewModel oder AddEditDeviceViewModel nennen.

    Ich weis, das ist anfangs verwirrend und wirkt total umständlich, auch kann man sich anfangs nicht vorstellen das man daraus einen mehrwert generieren könnte, aber glaub mir - so ist es.
    Du bist viel flexibler und mit jeder Klasse, jedem Fenster und jedem View mehr gewinnst du enorm an übersichtlichkeit. Model - View und ViewModel ist getrennt und jeder Teil übernimmt NUR seine Aufagbe und du weist immer wo was drinnen ist und was was macht.

    Ich hoffe ich konnte das verständlich rüberbringen. Falls Fragen offen geblieben sind trau dich nur.

    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. ##

    Hallo Sascha

    vielen Dank für deine ausführliche Antwort.
    Ich werde jetzt das Gesagte umsetzten und hoffen, dass nicht so schnell wieder Fragen auftauchen. :)

    Viele Grüße
    Florian
    ----

    WebApps mit C#: Blazor

    flori2212 schrieb:

    Ich werde jetzt das Gesagte umsetzten und hoffen, dass nicht so schnell wieder Fragen auftauchen.

    Aber für das ist dieses Forum ja da.

    Solange die Beschreibung gut genug ist werde ich versuchen so gut wie möglich zu Helfen. Die WPF ist sehr leistungsstark und macht spass, aber es ist ein lange Weg dort hin sie zu verstehen.
    Aber er zahlt sich aus (finde ich).

    Wenn du Fragen hast mach einfach einen Thread auf - nur keine Scheue.

    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. ##

    Hallo Sascha

    Ich bin gerade noch am arbeiten (morgen ist ja frei) und ich muss sagen es läuft alles super.
    Ich hab das Prinzip verstanden, hab zur Übung weitere Klassen implementiert.

    Das einzige was ich noch nicht ganz verstanden habe ist, wann man jetzt ein ​ViewModel und ein ​ListViewModel brauch.
    Braucht man es, sobald man Daten anzeigen will?

    Viele Grüße
    Florian
    ----

    WebApps mit C#: Blazor
    Anzeigen, verarbeiten, speichern. Egal.

    Faustregel: Ein ViewModel stellt die View dar.
    Wenn ich ein gut gemachtes ViewModel sehe kann ich mir vorstellen wie das UI dazu aussieht.
    Ob eine TextBox oder ne Listbox dann oben oder unten ist und wie die aussieht ist dabei egal. Ich muss anhand eines ViewModel sehen können was im View wie, wann passiert und welche funktionalitäten der User im View hat.

    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. ##

    Du erinnerst dich bestimmt noch an das Beispielprojekt mit den Consumer und Rooms, Devices usw.
    Warum haben wir da nur ein ViewModel für Consumer angelegt.
    Die anderen Daten wie Room werden doch auch im View angezeigt, oder?

    Viele Grüße
    Florian
    ----

    WebApps mit C#: Blazor
    Genau.
    In diesem Fall war es nicht nötwendig. Somit blieb es einfacher. Nichts desto trotz hat das ViewModel alles abgebildet. Die Collection und das SelectedItem.

    Man wusste also das eine eine Liste gibt wo ein Item Selectiert wurde mit welchem weitergearbeitet werden kann.

    Macht man richtiges MVVM würde man natürlich für jedes Model Objekt wieder ein ViewModel machen, aber solange dies nicht notwendig ist wäre das zu viel für den Anfang. Aber du kannst es ja mal probieren und dich melden wenn du probleme dabei hast.

    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. ##

    Hallo Nofear23M

    Ich habe jetzt für jedes Model ein ViewModel erzeugt und auch eingebaut.
    Auch habe ich mit Hilfe von deinem Tutorial ein ICollectionView eingebaut, sodass das Filtern möglich ist.
    Insgesamt bin ich schon ganz zufrieden mit dem Programm, schau es dir doch gerne mal kurz an und sage mir ob man am Code Style noch was verbessern kann.

    Aber jetzt direkt noch ein paar Fragen:

    1. Wie würdest du die Daten am Ende speichern? Ist ein XML Serializer am besten dafür geeignet? wenn ja, dann weiß ich wie man's macht :)

    2. Wie du im Beispielprojekt siehst habe ich ein Fenster hinzugefügt, indem man die "Devices" bearbeiten und auch löschen kann
    a. Ich mag, dass man nur Geräte löschen kann, die von KEINEM Consumer verwendet werden? wie frage ich das ab? Soll ich einfach eine Liste mit verwendeten Devices mit übergeben beim Formularaufruf?
    b. Ich mag, wenn man auf Übernehmen klickt, die Liste mit den "Devices" zurück an das Hauptfenster geben? Wie mache ich das am besten?

    3. Wenn ich "wie wild" auf dem DataGrid rumklicke sortiert sich es ab und zu neu. Woran liegt das?

    4. Das ist jetzt wirklich kompliziert und ich hab kein Plan ob das überhaupt so einfach geht. Wenn ich im DataGrid mehrere Objekte ausgewählt habe, und dann die Eigenschaften im rechten Fenster ändere, übernimmt das immer nur das zuerst selektierte Objekt. Wie bekomme ich es hin, dass das alle Objekte übernehmen. (Ich denke, ich muss die ​SelectedConsumer Eigenschaft in eine Liste umwandeln)

    Ich hoffe du kannst mir bei der einen oder anderen Frage weiter helfen

    Viele Grüße
    Florian
    Dateien
    ----

    WebApps mit C#: Blazor
    Hallo

    Das sind ja eine Menge Fragen, ich versuche mal auf diese einzugehen, aber die ein oder andere Frage ist fast schon was für ein eigenes Thema weil teilweise eher komplex.

    flori2212 schrieb:

    Ich habe jetzt für jedes Model ein ViewModel erzeugt und auch eingebaut.
    Auch habe ich mit Hilfe von deinem Tutorial ein ICollectionView eingebaut, sodass das Filtern möglich ist.

    Sehr schön, ja die ICollectionView ist schon cool. Warum sind aber so viele ViewModel-Klassen vorhanden welche keinerlei code haben? Ich nehme mal an die sind einfach schon mal für später vorbereitet oder?

    flori2212 schrieb:

    Insgesamt bin ich schon ganz zufrieden mit dem Programm, schau es dir doch gerne mal kurz an und sage mir ob man am Code Style noch was verbessern kann.

    Ich finde das sieht gar nicht schlecht auf. Der Code ist schön übersichtlich und macht genau das was er soll, man findet sich schnell zurecht was wo passiert und weis bescheid. Sicher gibt es immer Dinge die man bemengeln könnte, aber das wäre Haarspalterei. Ich bin jetzt nicht Zeile für Zeile durchgegangen, aber ansich sieht das gut aus. Das kann sich aber schnell ändern sobald du die Daten auch speichern udn wieder abrufen musst, wo wir beim nächsten sind...

    flori2212 schrieb:

    1. Wie würdest du die Daten am Ende speichern? Ist ein XML Serializer am besten dafür geeignet? wenn ja, dann weiß ich wie man's macht

    Das musst du selbst entscheiden, soll das Programm Multiuserfähig sein oder nicht, reicht es in XML Files oder brauchst du eine DB usw.
    Das Model ist ja jetzt im Moment eher für die Serialisierung in mehrere XMLs optimiert. Würde ich auch so machen wenn du keine Multiuserumgebung benötigst.

    Damit du da auf der sicheren Seite bist würde ich eine Signleton Klasse machen welche mir das laden und speichern abnimmt und mir immer den aktuellsten Container zurückliefert.

    flori2212 schrieb:

    Ich mag, dass man nur Geräte löschen kann, die von KEINEM Consumer verwendet werden? wie frage ich das ab? Soll ich einfach eine Liste mit verwendeten Devices mit übergeben beim Formularaufruf?

    Wenn du die Daten mal speichern und laden kannst, hast du auch jederzeit die möglichkeit diese zu laden und über LINQ abzufragen ob die DeviceID irgendwo in verwendung ist.
    Das könntest du ja auch direkt im ViewModel als ReadOnly-Property hinterlegen, dann hast du es gleich als Spalte in deinem Datagrid (Anzahl der Verweise auf dieses Objekt).

    flori2212 schrieb:

    Ich mag, wenn man auf Übernehmen klickt, die Liste mit den "Devices" zurück an das Hauptfenster geben? Wie mache ich das am besten?

    Sobald du mal laden und speichern kannst ist dies einfacher. Dann meldest beim schliessen des Dialogs einfach zurück das sich Daten geändert haben und das darunter liegende ViewModel läd die betreffenden Daten neu.

    flori2212 schrieb:

    Wenn ich "wie wild" auf dem DataGrid rumklicke sortiert sich es ab und zu neu. Woran liegt das?

    Das kann ich nicht repoduzieren, egal ob ich gefiltert habe oder nicht, ich kann wild rumklicken - auf die Expander aus und einklappen und dabei wild rumklicken klappt. Da musst du wohl genauer werden.

    flori2212 schrieb:

    Das ist jetzt wirklich kompliziert und ich hab kein Plan ob das überhaupt so einfach geht. Wenn ich im DataGrid mehrere Objekte ausgewählt habe, und dann die Eigenschaften im rechten Fenster ändere, übernimmt das immer nur das zuerst selektierte Objekt. Wie bekomme ich es hin, dass das alle Objekte übernehmen. (Ich denke, ich muss die ​SelectedConsumer Eigenschaft in eine Liste umwandeln)

    Das ist nicht ganz so einfach - geht, aber nicht so einfach. "Umwandeln" kannst du da nix. Es wird von SelectedItem IMMER nur ein Item zurückgegeben. Deshalb auch SelectedItem und nicht SelectedItems.

    Das es eine SelectedItems nicht als DependecyProperty gibt ist das auch nicht so einfach, wir aber unter WPF sowieso anders gehandelt. Bevor ich dir das erkläre (besser in einem neuen Thread) musst du dir aber im klaren sein WIE du die Daten dann bearbeiten willst. Angenommen die wählst nun 3 Items aus. Was soll dann rechts in den Comboboxen und Textboxen stehen? Du könntest ja drei Items in verschiedenen Räumen ausgewählt haben.

    Ich hatte sowas schon mal gemacht (inkl. der Massenbearbeitung), ist aber relativ Tricky. :whistling:
    Falls du sagst: "OK, Tricky oder nicht, ich würde gerne lernen wie sowas geht" können wir das in einem neuen Thread durchgehen, aber hier ist es denke ich fehl am Platz.

    So, wurde wieder fast ein Roman.

    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. ##

    Hallo

    Es freut mich erst mal, das ich das System von Model und ViewModel verstanden habe und im Code umsetzen konnte (dank dir).

    Nofear23m schrieb:

    Damit du da auf der sicheren Seite bist würde ich eine Signleton Klasse machen welche mir das laden und speichern abnimmt und mir immer den aktuellsten Container zurückliefert.

    Was meinst du mit einer Signleton Klasse? Ich kann mir leider nichts darunter vorstellen.

    Nofear23m schrieb:

    Sobald du mal laden und speichern kannst ist dies einfacher. Dann meldest beim schliessen des Dialogs einfach zurück das sich Daten geändert haben und das darunter liegende ViewModel läd die betreffenden Daten neu.


    Das ist eine gute Idee, so mach ich es.

    Nofear23m schrieb:

    Das kann ich nicht repoduzieren, egal ob ich gefiltert habe oder nicht, ich kann wild rumklicken - auf die Expander aus und einklappen und dabei wild rumklicken klappt. Da musst du wohl genauer werden.


    Ich habe jetzt ein kurzes Video angehängt, wo man es hoffentlich erkennt. Ich klicke im Grunde wild auf den einzelnen Zellen herum und die Zeilen ordnen sich neu an (schau am besten auf die ID)

    Nofear23m schrieb:

    Falls du sagst: "OK, Tricky oder nicht, ich würde gerne lernen wie sowas geht" können wir das in einem neuen Thread durchgehen, aber hier ist es denke ich fehl am Platz.


    Das verstehe ich. Ich stelle das Thema mal ganz hinten auf meine ToDo Liste, wenn ich mit allem anderen fertig bin, werde ich einen neuen Thread aufmachen, wo ich nochmal genau frage.



    Ich wünsche dir noch einen schönen Abend und nochmals vielen Dank für die ganze Zeit, die du dir für mich nimmst :)
    Florian
    Bilder
    • ezgif-1-6edf2a8a4d0c.gif

      897,19 kB, 800×282, 80 mal angesehen
    ----

    WebApps mit C#: Blazor
    Hallo

    Sorry, es sollte Singleton heißen. Tippfehler. Google mal danach bzw. mach gerne einen neuen Thread auf mit der Frage "XMLs in unterschiedlichen ViewModels laden und speichern".

    Bez. deines problems. Danke für das Gif das war Hilfreich. So wie ich das sehe passiert das immer wenn eine Zelle in den bearbeitungmodus geht. (Meisst erst beim zweiten klick)
    Du hast ja für jeder Spalte ein ein DataGridTemplateColumn verwendet anstatt einfach eine DataGridTextColumn welche ja ausgereicht hätte. Nur leider nur ein CellTemplate aber KEIN CellEditingTemplate. Die Zelle geht also in den Bearbeitungmodus und hat dann aber kein Template dafür.
    Wäre OK wenn die Spalte "ReadOnly" ist, hast du aber nicht gemacht. Nun kennt sich die WPF nicht mehr ganz aus. Und das Bindung geht wie es aussieht kurz verloren was ein umschlichten zur folge hat da die SortDescription auf "Room.RoomNumber" festgelegt ist.

    Also für das DataGrid selbst einfach mal IsReadOnly="True" eingestellt und gut wars. So muss man es nicht für jede Spalte angeben. Und da du ja sowieso nicht im DataGrid bearbeiten willt ist das ja auch OK.

    Was ich noch gemacht habe: Ich habe den SortMemberPath für die Spalten angegeben. So kann der User auch selbst umsortieren. Das würde mit einer DataGridTextColumn OutOfTheBox funktionieren, mit einem Template musst du angeben nach welchem Property im ViewModel sortiert werden soll wenn der jeweilige Spaltenheader geklickt wird. (Ja, so flexibel ist die WPF, du kann nach einem ganz anderen Property sortieren lassen als in der Zelle angezeigt wird. Beispiel: Du zeigst den Raumnamen an und wenn man ober klickt wird nach der Raumnummer sortiert. Vieleicht ein blödes Beispiel ohne sinn, aber ich brauchte es schon öfters.

    z.b. für die Spalte Room.Floor einfach:

    XML-Quellcode

    1. <DataGridTemplateColumn CanUserSort="True" SortMemberPath="Room.Floor" Header="Stockwerk">
    2. <DataGridTemplateColumn.CellTemplate>
    3. <DataTemplate>
    4. <TextBlock Padding="7,3" Text="{Binding Room.Floor}"/>
    5. </DataTemplate>
    6. </DataGridTemplateColumn.CellTemplate>
    7. </DataGridTemplateColumn>
    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. ##