Laden...

Forenbeiträge von Palladin007 Ingesamt 2.080 Beiträge

10.03.2022 - 17:35 Uhr

Um zwei Zeiten zu subtrahieren schau dir das mal an:

Oder einfach der Minus-Operator 😉

10.03.2022 - 17:27 Uhr

Also Du vermisst an der DataGridTemplateColumn eine Binding-Property?

Füge den Bindings im Template doch einfach die Property mit hinzu?
Ansonsten - wenn es sowieso in C#-Code aufgebaut wird - gibt der Column als Template-Inhalt ein ContenControl bindest die Content-Property wie Du es brauchst und dein "richtiges" Template bekommt die ContentTemplate-Property.

Also sowas, nur eben dynamisch:


<DataGridTemplateColumn>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding ...}">
              <ContentControl.ContentTemplate>
                  <DataTemplate>
                      </TextBlock Text="{Binding Name}" Margin="10"/>
                  </DataTemplate>
              </ContentControl.ContentTemplate>
            </ContentControl>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Oder - wenn das Margin das Ziel ist - Du nutzt DataGridTextColumn.ElementStyle und definierst das Margin als Style.

09.03.2022 - 19:48 Uhr

Wäre trotzdem interessant zu wissen, ob ich als Control-Entwickler irgendwas tun kann, um das zu verhindern.

Nein.

Das StackPanel bietet dem Inhalt unbegrenzt viel Platz und der ScrollViewer füllt den Platz aus, den es hat.
Ergo: Der ScrollViewer kann unendlich groß werden, was die Scroll-Funktion überflüssig macht.

Besser wäre ein Grid oder ein DockPanel oder der ScrollViewer bekommt feste Maße.

09.03.2022 - 02:24 Uhr

Warum merkst Du dir nicht die Liste und aktualisierst sie regelmäßig?
Dass der Client sich abmeldet, darauf kannst Du jedenfalls nicht bauen.

Ach und diese leeren catches - gewöhn dir das ab, ganz schnell. Fehler gehören vorher geprüft und entsprechend behandelt.
Und wenn das leere catch tatsächlich sinnvoll ist, dann gehört da wenigstens ein Kommentar mit einer ausführlichen Begründung rein, warum es leer ist - und ggf. ein Log.
Glaub mir, Du willst nicht erleben, was daraus wird, wenn so eine Anwendung länger so weiter entwickelt wird ...

07.03.2022 - 00:39 Uhr

... oder eine Suchmaschine befragen.
Dein Problem wurde öfter beantwortet, als Du lesen kannst.

05.03.2022 - 15:27 Uhr

Ich bezweifle, dass das ein Beispiel von Microsoft ist.

Die StartupUri muss auf eine xaml-Datei zeigen, nicht auf eine Methode.
Wenn Du eine Methode haben willst, nimm das StartupUri raus und verwende das Startup-Event oder überschreibe die OnStartup-Methode.

05.03.2022 - 15:22 Uhr

resx-Dateien generieren (wenn das Tool dafür eingestellt ist - sollte default sein) eine Klassen

get-values-from-resx-files-in-xaml

Dass die Code-Generierung auf public stehen muss, hatte ich vorhin vergessen.

05.03.2022 - 14:38 Uhr

resx-Dateien kannst Du so nicht nutzen, die sind unabhängig von WPF.
StaticResource funktioniert nur mit ResourceDictionaries, die im aktuellen Kontext oder der ganzen App bekannt sind.

resx-Dateien generieren (wenn das Tool dafür eingestellt ist - sollte default sein) eine Klassen, über die Du die Properties nutzen kannst. Da die static ist, kannst Du in WPF über "{x:Static namespace:Class.Property}" darauf zugreifen.

04.03.2022 - 12:14 Uhr

was passiert wenn der Timer gerade läuft?

Indem Du es dir merkst.
Ein Boolean, Task für den aktuell laufenden Prozess bzw. null, ein ManualResetEvent, der den Zustand signalisiert, etc.
Je nachdem, wie Du damit umgehen willst, gibt's verschiedene Wege.

04.03.2022 - 10:11 Uhr

Schau dir doch die Timer-Klassen an?
Wenn es sofort triggern soll, stopst Du, setzt Du die Zeit auf 0 und startest wieder - oder nur auf 0 setzen, teste Mal aus, wie die sich dabei verhalten.
Ich nutze dafür aktuell den DispatcherTimer: Stop, Intervall setzen, Start

02.03.2022 - 22:53 Uhr

Das würde sich einfach beheben lassen wenn ich im Konstruktor nicht den Zugriff mache sondern die Mothode innerhalb von dem besagten ViewModel im Konstruktor aufrufen würde. Sofern das so gemeint war.

Och das hatten wir doch schon 🙁
Es interessiert nicht, ob der Code im Konstruktor steht, sondern ob er darin aufgerufen wird.
Genauso auch Probierties, die sollten möglichst nichts langwieriges machen.

Bei zwei weiteren Klassen habe ich den Zugriff nicht im Konstruktor, sondern als Loaded Event einer View aufgerufen. Die Loaded Methode steht dabei im ViewModel und wird von der View dann nach dem Initialisieren angestossen. Das dürfte dann gut sein so richtig?

Das klingt schon besser.
Ich würde den EventHandler aber nicht im ViewModel deklarieren, sondern in der View und darin rufst Du dann eine eigene Reload-Methode auf.
Noch besser wäre mMn. aber, wenn es ein Command ist und der aus dem XAML heraus aufgerufen wird.
Ob das notwendig ist ... kann man vermutlich drüber streiten, ich mache es aber immer so.

Ein Command kann einen Konstruktor haben um teile des ViewModels zu kennen und zusätzlich auch Parameter über die View erhalten. Wenn auch indirekt, da der eigentliche Parameter ja dennoch aus dem ViewModel kommt.

Ich habe es bisher nur selten gesehen, dass ein Command als eigene Klasse implementiert wurde, meistens nutzt man dafür RelayCommands.
Beides ist aber richtig, nur ist es bei einer eigenständigen Klasse umständlicher, das ViewModel zu steuern.

Die Daten holt sich das Model vom ViewModel (oder bekommt sie übergeben) um zu serialisieren.

Models sind dumm, die holen sich nichts und tun auch nichts.
Models transportieren Daten in einer Form, wie Du sie brauchst.
Das kann eine DB-Entity sein, oder ein DTO zwischen Service und ViewModel oder irgendetwas anderes, aber sie haben alle eines gemeinsam: Sie tun nichts.
Der Service bekommt ein Model (oder mehrere) übergeben oder liefert sie als Rückgabewert oder arbeitet intern damit - die eigentliche Logik befindet sich im Service.

Viel mehr Sinn hatte ich bis dato in einem Model nicht gesehen

Der Sinn ist das Transportieren von Daten in genau der Form, wie sie gebraucht werden.
Methode X braucht ganz bestimmte Eigenschaften aus der View, also übergibst Du NICHT das ViewModel, sondern erzeugst ein passendes Model mit den benötigten Eigenschaften und übergibst dieses Model.
Und Methode X gibt selber wieder ein Model zurück, mit Eigenschaften, die Du im ViewModel lesen und verwenden kannst.

ein Kalkulations Service oder sonstige Tätigkeiten gelten auch als Model

Nein.
Service => Logik
Model => Daten
DTO (Data Transfer Object) => Model => Daten

Ich kann diese vielen Dinge nicht ganz zuordnen da ich dachte es gibt 3 Bereiche (Model, View, ViewModel) und dann kam Th69 und sagt was von weiteren Bereichen wobei er den Service zum ViewModel gepackt hat.
Zu trennen zwischen Business - Logik und Logik die das ViewModel machen soll ist mir nicht klar.

So könnten die drei Schichten aussehen.

`- Presentation ¬

    • View (nutzt ViewModels) <-- Das erste "V" in MVVM
    • ViewModel (nutzt Services und übergibt/empfängt Models) <-- Das "VM" in MVVM
  • Business ¬
    • Models (Dumme Daten) <-- Das erste "M" in MVVM
    • Services (Empfängt oder liefert Models) <-- Da ist die Logik
  • Data ¬
    • Repository (liest/schreibt Daten aus der Datenquelle) <-- Da werden z.B. XMLs gelesen/geschrieben
    • Datenquelle (Datenbank, XML, etc.)`

Allerdings ist das Model-"M" in MVVM in der Darstellung vielleicht etwas missverständlich.
Das Model-"M" in MVVM meint alle Schichten unterhalb der View, also in diesem Fall "Business" und "Data".
Wenn man die Trennung aber strikt durch zieht und das ViewModel nur auf Business-Models zugreifen darf (nicht auf Data), befindet sich das Model effektiv nur an dieser einen Stelle - daher habe ich es dort auch eingezeichnet.

02.03.2022 - 09:27 Uhr

Wenn ein ViewModel einen Konstruktor hat der auf die Datanbank zugreift, dann eigentlich nur um Dropdowns zu befüllen.

Konstruktoren sollten NIEMALS irgendwas mit der Datenbank tun! Eigentlich generell nichts, was lange dauern kann.
Konstruktoren bereiten die Grundfunktionalität vor, sind dabei in den Möglichkeiten aber sehr beschränkt.
Wenn Du Daten laden willst, dann baue eine Load- oder Reload-Funktionalität ein und rufe die dann aus der View (oder von irgendwo sonst) heraus auf.

Dem Rest konnte ich aber nicht ganz folgen.

Ich habe das eigentlich so gemeint, dass ich eine Klasse "CalculateAreasService" erstelle die z.B. so aussehen würde.

Dann siehe meine erste Antwort auf den Kommentar 😉
Nicht static!

Außerdem ist ein Business-Service eine Schicht unter der View, ViewModels gehören aber zur View.
Deine Beispiel-Methode ist also eine Verletzung der Schichten-Trennung.

Oder Du willst es nicht als Business-Service aufbauen, weil es zu "unwichtig" ist, um als eigener Business-Service aufgebaut zu werden.
Das wäre denkbar (mach ich auch manchmal), aber auch dann solltest Du keine direkten Abhängigkeiten (ViewModel als Parameter) verwenden, da Du dann neben dem ausgelagerten Code alle anderen Vorteile verlierst.

01.03.2022 - 21:07 Uhr

Mein Model hat neben den Eigenschaften dann noch einen oder mehrere Konstruktoren die beim Instanziieren die Eigenschaften anhand von eine Datenbank oder von einem ViewModel ein Item aus der Liste bekommt

Das klingt, als würde das Model ein ViewModel als Parameter bekommen?

kann bei Wertveränderung nicht innerhalb vom Model sondern mit einer Service - Klasse seine Daten aktualisieren.

Das Model ist am besten unveränderlich, ja.
Es kann aber gewollt sein, dass Änderungen im ViewModel erlaubt sind, z.B. stehen die Änderungen erst im ViewModel, bis auf Speichern gedrückt wird. Der Speichern-Command sammelt dann die Änderungen und lässt sie vom Service speichern.
Z.B. könnte dann der Speichern-Command wieder ein Model zusammen bauen, in dem die ID und die (geänderten) Daten stehen und der Service sucht, validiert und aktualisiert den Datensatz.

Kann man eine Service - Klasse auch grob als Methodenklasse mit statischen Methoden bezeichnen?

Wir sind hier bei OOP und Du willst statisch arbeiten? O.o Nein, natürlich nicht statisch.
Immer wenn Abhängigkeiten zwischen Klassen bestehen, ist alles, was statisch ist, ein Risiko - also eigentlich immer.
Es gibt ein paar Ausnahmen, da nutze ich auch static (z.B. technisch vorgegebene Werte, das sind dann Konstanten), aber das sind Ausnahmen.

Oder Du meinst das "statisch" im übertragenen Sinn, dann s.u. 🙂

Als Beispiel:


public interface IMyService
{
    double GetTotalSurfaceArea(Guid shapeGroupId);
}
public class MyService
{
    // Database connection (or repository) and more dependencies

    public double GetTotalSurfaceArea(Guid shapeGroupId)
    {
        var result = 0;

        foreach (var shape in GetAllShapes(shapeGroupId))
            result += CalculateSurfaceArea(shape);

        return result;
    }

    private IEnumerable<MyShape> GetAllShapes(Guid groupId)
    {
        // Load shapes from database (or repository)
    }
    private double CalculateSurfaceArea(MyShape shape)
    {
        // Calculation of the surface area based on the dimensions and shape type
    }
}

Oder der Service arbeitet ohne Datenbank und bekommt stattdessen eine Liste mit Shape-Models, die die Grunddaten (Typ und Maße) enthalten.
Das Interface ist nicht zwingend notwendig, ist aber Gold wert, wenn Du die ViewModels automatisch testen willst und die nur das Interface kennen müssen - dann kannst Du das Interface mocken und z.B. Fehler provozieren und den Umgang damit testen lassen.

Kann man eine Service - Klasse auch grob als Methodenklasse mit statischen Methoden bezeichnen?

Wenn Du das "statisch" im übertragenen Sinn meinst, könnte man das so sehen, ja.
Häufig sind diese Methoden unabhängig voneinander, sie sind also nur "zufällig" in der selben Klasse.
Ob das nun der ideale Weg ist, sei mal dahin gestellt, aber die großen Ziele (Trennung von Verantwortlichkeiten und Testbarkeit) kann man damit erreichen und das ist das wichtigste.

01.03.2022 - 20:17 Uhr

Du wirst auch nicht das richtige Video finden.

Wie gesagt:

Schau dir die Artikel dazu von Microsoft an, außerdem haben einige größere Namen in der Blogger-Welt (z.B. Stephen Toub) gute Artikel dazu geschrieben.

28.02.2022 - 21:40 Uhr

What's new in C# 8.0 - C# Guide: Nullable reference types
Dein Kurs ist etwas über zwei Jahre veraltet 😉

Das Feature gibt's seit September 2019 und konnte manuell aktiviert werden.
Mittlerweile (seit .NET 6 glaube ich) ist es standardmäßig aktiv und das ist mMn. auch gut so.

Du kannst es abschalten, aber gewöhn dich lieber daran, immerhin kann dir der Compiler viele Fehler zeigen, die sonst nur zur Laufzeit auffallen würden.
Es gibt zwar ein paar Situationen, da ist es etwas tricky, aber die Zeitersparnis durch die vielen Fehler, die so nie auftreten, ist es allemal wert.

Mit Fragezeichen heißt also, dass null erlaubt ist, aber immer, wenn Du damit arbeitest, musst Du vorher auf null prüfen oder anderweitig sicherstellen, dass es nicht null ist.
Ohne Fragezeichen heißt, dass es nicht null sein darf, dann musst Du aber auch jederzeit garantieren, dass es nie null ist. Z.B. verlangt der Konstruktor, dass Du eine Referenz angibst.
Das Thema ist aber umfangreicher, als meine zwei Sätze, also lies dich da ein.

Bei Events heißt das: Kein Handler = null
Und Events sollte man auch aus anderen Gründen immer mit dem "MyEvent?.Invoke()" aufrufen.

28.02.2022 - 10:10 Uhr

Da diese beiden SubViewModels jedoch eigenständige Bereiche sind und nicht eine Ableitung von ItemViewModel ist ein Interface hier nicht der richtige Weg. Es würde dann ja wieder dazu führen, dass im ItemViewModel enorm viel implementiert sein muss

Warum? O.o
Du brauchst aktuell das ItemViewModel - was spricht dagegen, dem ItemViewModel ein Interface zu geben, was genau die Methoden definiert, die Du brauchst?

Du könntest natürlich auch einen anderen Weg gehen und die Berechnung ganz raus ziehen. Gerade wenn die Berechnung komplexer ist, kann das durchaus interessant sein.
Du hast dann also eine Klasse (kein ViewModel), die sich nur um die Berechnung kümmert. Die ViewModels füttern sie mit den benötigten Daten und dein AddonViewModel bekommt eine Referenz (ggf. hinter einem Interface) darauf.
Also eine Art AddonCalculationService

27.02.2022 - 18:06 Uhr

Warum muss die CalculateAreas-Methode denn im AddonsViewModel sein?
Leg sie ins ItemViewModel und Du hast auf alles Zugriff was Du brauchst.

Oder reich das ItemViewModel zum AddonsViewModel durch.
Wenn dir das nicht gefällt (was ich nachvollziehen kann), kannst Du auch eine Abstraktion (Interface) dazwischen legen, dann arbeitest Du beim Berechnen mit dem Interface und das ItemViewModel implementiert dieses Interface.

26.02.2022 - 17:51 Uhr

Ich muss gestehen, ich kannte das StatusBar-Control bisher nicht und hab's bisher auch nie gebraucht.
Was spricht dagegen, dass Du stattdessen einfach ein Grid nutzt und passend gestaltest?
Dann kannst Du auch den GridSplitter nutzen.

24.02.2022 - 23:39 Uhr

Indem Du ein string[] zurück gibst.


return Task.FromResult(new string[0]);

Nur löst das dein Problem nicht.

Und ich teile Abts Meinung, dass Du dich nicht ausreichend mit dem Thema beschäftigt hast 😉
Die Syntax und welche zwei/drei relevante Methoden es gibt, reicht nun mal nicht aus.

Du musst verstehen, was im Hintergrund passiert, was das "await" bedeutet und was der Compiler daraus macht und warum und wie das mit dem SynchronizationContext zusammen hängt.
Hat niemand behauptet, dass es leicht ist, im Gegenteil, das Thema ist schwer, aber wenn Du nicht ständig mit solchen (oder schlimmeren) Problemen kämpfen willst, musst Du das durch machen.

24.02.2022 - 20:03 Uhr

Schau dir an, was IProgress<T> und was CancellationToken sind.

Das IProgress würde ich ganz weg lassen, Du nutzt es ja sowieso nicht - zumindest nicht so wirklich.
Der CancellationToken wäre in diesem Fall default, das ist aber keine generelle Regel.
Auch ist das async void meistens eine fürchterliche Idee, in diesem Fall aber in Ordnung, da es bei Events keine bessere Möglichkeit gibt.

Abgesehen davon ist deine Open_File_Dialog-Methode nicht asynchron.
Du rufst synchron auf und gibst das Ergebnis in einem abgeschlossenen Task zurück, am Ende läuft also alles synchron, nur komplizierter.
Du kannst dennoch so arbeiten, es ist auch nicht schlimm, aber in dem Fall einfach überflüssig.

23.02.2022 - 19:27 Uhr

Was mir dazu gerade noch einfällt:

HTTP-Requests sind mit Refit extrem schnell und einfach umgesetzt. Das basiert auch auf dem HttpClient, macht aber 90% der Arbeit automatisch bzw. generiert Code dafür.
Das kann auch ohne Tasks arbeiten, verlangt stattdessen aber das Observer-Pattern, synchron arbeiten kann, will und soll es auch gar nicht.
In Tasks und deren Funktionsweise einzuarbeiten lohnt sich aber in jedem Fall, das wird immer wichtiger.

23.02.2022 - 18:59 Uhr

Und aus await kommt er nicht zurück.

Weil Du ein Deadlock hast.
Der Aufruf der Result-Property sorgt dafür, ist ganz normal (wenn man die Arbeitsweise verstanden hat) und kann nicht ohne andere problematische Umwege verhindert werden.
Das Ergebnis bekommst Du, wenn Du den Task awaitest und das solltest Du auch immer nutzen.

Da fehlen mir einige Grundlagen.

Dann solltest Du das ändern.
Das Thema, was Du hier anschneidest, ist nicht einfach und sprengt definitiv den Rahmen einer Frage in einem Forum.
Schau dir die Artikel dazu von Microsoft an, außerdem haben einige größere Namen in der Blogger-Welt (z.B. Stephen Toub) gute Artikel dazu geschrieben.
Es ist wichtig, dass Du verstehst, wie es funktionierst (nicht nur zwei/drei Regeln stumpf abarbeiten), damit Du effektiv damit arbeiten kannst.

Richtig wäre:


// Der CancellationToken ist nicht notwendig, sollte aber immer vorhanden sein, dann kannst Du leicht Abbruch-Funktionen nachrüsten.
private Task<bool> IfPdfAsync(CancellationToken cancellationToken)
{
    // Das sieht irgendwie falsch aus. Warum ist das eine Property? O.o
    // URL="https://cdn.mycsharp.de/forumpostattachments/attachment-1856.pdf";

    var url = "https://cdn.mycsharp.de/forumpostattachments/attachment-1856.pdf";

    return await DoesFileExistAsync(url, cancellationToken);
 }

Allerdings hast Du dann bei der Methode, die IfPdfAsync aufruft, das gleiche Problem nochmal.
Also wenn async, dann überall async (soweit möglich und notwendig), wenn Du anfängst zu mischen, hast Du früher oder später Probleme
Aber dafür solltest Du es wirklich begriffen haben und aus ein paar Beispielen ableiten, wie es funktioniert, reicht da nicht aus.

23.02.2022 - 00:45 Uhr

Ich finde es schade, dass man in WPF keine komplexeren Vererbungsketten aufbauen kann.

Kann man - nur eben nicht so 😉
Du musst schon dem folgen, wie WPF geplant war, wenn Du mit aller Kraft dagegen rennst, wirst Du nicht damit aufhören, gegen Wände zu rennen.

Ich arbeite gerne mit Vererbung, da zum einen es (wie schon erwähnt) Schreibarbeit verringert aber auch die Wartungsarbeiten.

Es kann die Wartungsarbeiten aber auch deutlich erschweren, weshalb ich Vererbung immer mit Vorsicht genießen würde.
Und es passiert leicht, dass Du plötzlich Mehrfachvererbung brauchst, was aus gutem Grund aus C# und Java verbannt wurde.

In dem Fall wollte ich einen Rahmen bauen der gewisse Grundfunktionen und Optische Eigenschaften festlegt. Der Erbe sollte somit den Rahmen besitzen und die eigenen Funktionen

Und wie willst Du einem bereits vorhandenen Control diese Grundfunktionalität bieten? Ein Control kann keine zwei Basisklassen haben.

Ich habe etwas ähnliches, aber als eine MasterDetailsView, auf der einen Seite Details zur anderen Seite und die Maße verschiebbar.
Aufgebaut ist es wie ein ContentControl, bloß mit einem weiteren Content + Template. Dazu ein Style, der dem Ganzen die Optik gibt.
So funktioniert es ohne riskante Vererbung und ich kann jedes beliebige Control mit den Funktionen von meinem Control ergänzen, ganz egal, wie komplex es wird.

So arbeitet WPF und wenn Du dir die anderen Controls von Microsoft anschaust, bieten viele davon genau diese Funktionalität.
Ein paar Beispiele: Border, Button, ScrollViewer, DataGridCell, Window, ... such dir was aus.

20.02.2022 - 12:50 Uhr

Dann lies doch mal den Inhalt von beiden Ifs ...

18.02.2022 - 14:10 Uhr

Mir geht's nicht um die Firmen oder Projekte, nur um die Visual Studio Features, die wollte ich mir anschauen 🙂

18.02.2022 - 10:17 Uhr

Auch gibt es viele Funktionen, dessen Nutzerkreis Du auch einfach nicht kennst: Microsoft arbeitet sehr viel mit großen Unternehmen zusammen, die 1000+ Lizenzen besitzen.

Hast Du Beispiele dazu? Also zu den Funktionen.

16.02.2022 - 10:02 Uhr

Ein Virus ist auch nur kompilierter Code.
Code ist nur Text.

Wenn Du Code bekommst, kann da natürlich auch ein Virus enthalten sein, wenn Du ihn ausführst, tut er, was er soll.
Oder Du schaust dir den Code an, bevor Du ihn ausführst.

15.02.2022 - 22:27 Uhr

Dann solltest Du dir das Grundwissen aneignen, wie man mit Events arbeitet.
Dann wird dir auch klar, dass "Wie ich dann auf das ParentViewModel komme" eine ziemlich unsinnige Frage ist, da Events ja extra für den umgekehrten Weg gedacht sind.

Wie gesagt:

WPF und MVVM sind halt kein Einsteiger-Thema, auch wenn diverse Video-Tutorials es gerne so aussehen lassen.
Wer effektiv mit WPF und MVVM arbeiten will, sollte die Grundlagen von C# und OOP drauf haben.

15.02.2022 - 22:00 Uhr

In dem ViewModel habe ich ja evtl. nicht nur Eigenschaften bei denen ich OnPropertyChanged abfeuern kann, sondern um das ViewModel auch nicht zu überladen wenn mal mehr Eigenschaften benötigt werden, auch einfach eine Klasse als Property, welche wiederum ja Properties hat die den OnPropertyChanged implementiert haben.

Mach das nicht nur, weil dir die Properties zu viel werden, sondern weil die Aufteilung auch inhaltlich Sinn ergibt.
Ansonsten würde ich so eine Property generell readonly machen - erspart das Behandeln der geänderten Instanzen.

OK das beantwortet eigentlich die Frage, aber ich weiss trotzdem nicht wie ich das machen kann.

Gar nicht.

Erfinde ein eigenes Event, das Du in der Property auslöst und reagiere im "Parent-ViewModel" darauf.
Oder reagiere auf Property-Änderungen anhand des PropertyChanged-Events, das es ja schon gibt.
Was nun besser ist - ich bevorzuge das eigene Event, dann ist immer offensichtlich: Da gibt's ein Event, das hat eine bestimmte Aufgabe und muss im Auge behalten werden.
Du willst nämlich nicht den Überblick über deine "Event ruft Event auf, was ein Event aufruft, ..."-Ketten verlieren, so ein Wirrwarr aufzuräumen, kann sehr grausig werden 😉

15.02.2022 - 21:55 Uhr

Wenn ich das aber so anschaue und ich mit 4 Feldern ja jede Situation abfangen müsste, wird der Filter irgendwo 24 Optionen haben

Deshalb im ViewModel.
ItemsSource => ViewModel
FilterExpression => ViewModel
ReloadCommand => ViewModel

Theoretisch könntest Du in der FilterExpression-Properts auch die Daten direkt laden, das Binding hat dafür eine IsAsync-Property.
Ich persönlich bin da aber kein Freund von, da die Property dann wieder zu viel tut.
Ohne generell asynchrone Abläufe wüsste ich aber auch keinen anderen Weg, wie man das machen könnte.
Oder der Filter greift eben erst, wenn man auf einen Reload-Button klickt, das wäre dann die super einfache (und meist ausreichende) Möglichkeit.

Wenn ich nun die 4 Eigenschaften nicht in einem Model liegen habe sondern im ViewModel, kann ich ja ganzu einfach anstelle von OnPropertyChanged, meine Filtermothode ansprechen (oder beide, was es nicht unbedingt braucht)

Kannst Du natürlich auch machen, aber als Command und nicht als Methode. Methoden gehen zwar auch, aber das ist ziemlich umständlich.
Ich war gedanklich bei einer Filter-TextBox, in der man alles filtern kann und das habe ich bei mir mit einem SQL-LIKE je Column gelöst.
Wenn dir ein InMemory-Filter reicht, kannst Du das natürlich auch im ViewModel machen, wie Du willst.

Von der Datenbank hole ich ja nur im Konstruktor vom ViewModel die Daten für die ObservableCollection. sonst mache ich da erstmal gar nichts.

Für den Konstruktor gilt das gleiche: Nichts aufwändiges mach.

Oder was meinst Du, wie der Designer an eine Instanz von dem ViewModel kommt?

Dazu mach ich dann noch Models oder evtl. arbeite ich dann dort auch mit den Entities der DB, die sind ja auch dumm.

Für kleine Projekte reichen vermutlich auch Entities, MVVM macht dazu jedenfalls keine Aussage.
Die Trennung in Entities und Models ergibt dann Sinn, wenn sich beides unterscheidet oder Du aus anderen Gründen die Daten-Schicht nicht mit der View-Schicht verbinden willst.

15.02.2022 - 20:56 Uhr

Die einfachsten Sachen habe ich das gefühl zerfressen mein Hirn in kleine Fetzen. Oder ich bin überarbeitet.

Tja 😜
WPF und MVVM sind halt kein Einsteiger-Thema, auch wenn diverse Video-Tutorials es gerne so aussehen lassen.
Wer effektiv mit WPF und MVVM arbeiten will, sollte die Grundlagen von C# und OOP drauf haben.

Bei jeder Tasteneingabe in einem der Texboxen soll in der "ItemsList" entsprechend alles gefiltert und umgehend angezeigt werden was den Eingaben der Textboxen entspricht. Im Grunde will ich das "TextChanged" event der Textboxen dafür nutzen, da dies ja bei jeder Eingabe reagiert und nicht erst beim verlassen der Textbox.

Das Binding hat eine UpdateSourceTrigger-Property. LostFocus ist der Default, daher das Verhalten.

Noch geiler wäre eine einzige Suchbox in der mit "*" als Platzhalter gesucht werden kann.

Das musst Du selber bauen, das hat nichts mit WPF oder MVVM zu tun.
Du bindest nur den Filter und das Filtern implementierst Du selber.

Wie die gefilterten Ergebnisse angezeigt werden, geht auf zwei Wege.
Einer wäre eine CollectionViewSource, ich persönlich nutze die aber eigentlich nur zum einfachen Sortieren.
Ich mache sowas eigentlich immer im ViewModel oder (je nach Anforderungen) in der Daten-Schicht um. Bei Letzterem kannst Du eventuell das LIKE hier missbrauchen, das kann ähnliche Funktionen.

Seit ich das ViewModel im Xaml als DataContext angebe sagt er mir, dass er keinen ConnectionString für meine Datenbank findet

Das liegt - wenn ich dich richtig verstehe - daran, dass der Designer eine Instanz von deinem ViewModel erzeugt und das Binding auch durchführt. So kann er Daten anzeigen und sicher sagen, wo es Probleme gibt.

Deshalb sollte man auch keine Logik in Properties umsetzen - Properties erlauben Zugriff auf Werte, sie validieren, setzen Zustände, etc. aber sie machen nichts mit einer Datenbank oder ähnlich komplexe Dinge.
Der vermutlich einfachste Umweg wäre, Du reagierst auf ein Event (z.B. Window.Loaded) und triggerst dann das Laden über einen Command. Das ViewModel ist also leer, bis das Window tatsächlich geladen wurde. Wenn der Designer auch was zum Anzeigen bekommen soll, musst Du ihm Dummy-Daten bieten, da gibt's Wege, herauszufinden, ob der Code vom Designer ausgeführt wird (DesignMode), oder ob das Programm tatsächlich läuft. Ich persönlich würde aber ganz auf den Designer verzichten (nur XAML - der Designer taugt eh nix), dann brauchst Du sowas auch nicht.

15.02.2022 - 17:09 Uhr

uns fehlt da einfach SQL Design Optimierungswissen

Mir auch - deshalb hoffe ich, hier ein paar Tipps mitnehmen zu können 😁

15.02.2022 - 15:53 Uhr

In vielen Unternehmen hast Du sogar noch 4:3 Monitore - kein Scherz.

Kann ich bestätigen.
Letztes Jahr erst, die halbe Firma hatte steinalte Monitore von 2006 oder früher, genau habe ich es nicht herausgefunden.
Ein Monitor alleine war schwerer als der gesamte PC 😁

Abgesehen davon:
Wenn Du in der Höhe mehr Platz haben willst, dann stell doch einen 19:9-Monitor hochkant auf?
Ein ehemaliger Kollege hatte das mit zwei von drei Monitoren gemacht und darauf geschworen.
Mein Fall war's nicht, dafür nutze ich zu gerne den horizontalen Platz für zusätzliche Tools.

Und wenn es nur um den Überblick in der Klasse geht, tut's ja auch ein zweites Fenster oder - was ich persönlich auch sehr mag - die Map-Mode ScrollBar.
Für alles, was zu groß ist, gehe ich immer erst mal durch die Methoden und klappt auf, was für mich relevant ist (sieht man dann auch in der ScrollBar). Bei Refactoring gehe ich auch gerne hin und splitte erst mal die Methoden auf

15.02.2022 - 15:42 Uhr

Meine Hoffnung war, dass man das mit dem vorherigen Zustand kombinieren könnte.
Oder anders: Es wird gar nicht geändert, wenn:

Wenn es vorher gelesen war, dann kam nach dem neuen Beitrag nur der neue Beitrag hinzu, daher: Es ist immer noch gelesen.
Wenn es vorher ungelesen war, dann hat der neue Beitrag daran nichts geändert, daher: Es ist immer noch ungelesen.

Aber klar, wenn das sowieso abgelöst werden soll, wäre das unnötig verschwendete Zeit.

Seit einigen Tagen sind wir (mit externer Hilfe) wieder an der Sache dran, da endlich nachhaltig eine Lösung zu finden.

Schreibst Du irgendwo, welche Lösung Ihr implementiert?
Würde mich interessieren, wie man ein solches doch ziemlich schwieriges Problem angehen könnte.

15.02.2022 - 00:01 Uhr

Bezüglich der Anzeige von ungelesenen Foren:

Du hattest da ja Performance-Probleme, ich weiß nicht, ob meine Frage das auch betrifft.

Wenn ich unter einer Frage eine Antwort schreibe und danach in die Forenübersicht gehe, ist das Forum, in dem die Frage steht, immer als ungelesen markiert.
Das klingt irgendwie überflüssig?

Oder ist das eine Dirty-Lösung, weil Du noch keinen performanteren Weg gefunden hast und lieber False Positives als False Negatives in Kauf nimmst?

14.02.2022 - 23:56 Uhr

In Visual Studio ist mir nichts derartiges bekannt.

In WPF könntest Du dir aber behelfen, indem Du die ScrollViewer aneinander "hängst":


scrollViewer.ScrollToVerticalOffset(previousScrollViewer.VerticalOffset + previousScrollViewer.ViewportHeight);

Visual Studio ist meines Wissens auch WPF, also kannst Du vielleicht sowas nach bauen.

Monitore bekommen immer mehr Auflösung in der Breite nicht so aber in der Höhe. So gibt es z. B. Monitore im Format 21:9 mit einer gebogenen Fläche. Sicher toll für Spiele aber nicht wirklich geeignet, um langen Programmcode darzustellen

Früher waren die Monitore kleiner als heute - was hat sich geändert, dass Du jetzt mit den größeren Monitoren nicht mehr auskommst?
Bei mir kommt es nur selten vor, dass die Methode überhaupt in die Nähe der Höhe des Monitors kommt.
Und auch Klassen sollten nicht so komplex sein, dass man mehrere Methoden gleichzeitig überblicken muss, um effektiv arbeiten zu können.
Im Gegenteil: Meine erste Amtshandlung in einer Klasse ist immer "Outlining => Collapse to Definitions" und in den meisten Fällen reicht das aus, um einen Überblick zu gewinnen.

PS:
Ich habe 32 Zoll, WQHD und immer auf 85% Zoom.

14.02.2022 - 11:08 Uhr

Ich habe hier natürlich schon etwas recherchiert und dabei bin ich auf einen Punkt gestoßen der mir immer wieder untergekommen ist - Sicherheitsschwächungen bei der Passwordbox wenn die PasswordHelper-Klasse genutzt wird.
Meine Frage dazu:
Gibt es ein Ansatz, mit dem in dem MVVM Design diese Schwächung vermeiden kann?

Du meinst, diese PasswordHelper-Klasse?

Ohne zu wissen, an welche Sicherheitsschwächungen Du denkst, nimmst Du damit natürlich in Kauf, dass das Passwort ungeschützt im RAM liegt.
Wenn also jemand weiß, wie Du deine Anwendung aufgebaut hast (im einfachsten Fall hat jemand Source oder Binaries), dann kann diese Person den RAM auslesen und so an das Passwort gelangen.
Ob das ein relevantes Sicherheitsrisiko ist, musst Du oder jemand über dir oder der Kunde entscheiden.

Das Problem kann man aber nicht so einfach umgehen, zumindest nicht, wenn Du irgendwann das Passwort als String brauchst, denn dann liegt es immer irgendwann im RAM.
Du kannst nur versuchen, diese Zeit, in der es im RAM liegt, möglichst kurz zu halten.

Die PasswordBox liefert ja einen SecureString und das kannst Du ganz normal nutzen.
Ich hole mir davon dann das Passwort und verarbeite es weiter, aber eben erst, wenn ich es auch braucht (wenn Login geklickt wird).
Ob das der optimale Weg ist, weiß ich nicht, aber für unseren Fall reicht das aus.

so das der Login komplett von dem eigentlichen Programm getrennt ist um die schwächung zu umgehen?

Und was würde das ändern?
Dann liest man das Passwort eben aus dem RAM des Login-Programms aus.
Wahrscheinlich erhöht das sogar das Risiko, da die allgemeine Komplexität höher ist, was mehr Raum für Fehler bietet.

Vielleicht gibt's Wege, das Problem generell zu umgehen, die kenne ich aber nicht und ich behaupte auch, dass das für die meisten Projekte gar nicht notwendig ist.

13.02.2022 - 00:24 Uhr

Du kriegst einen object-Parameter und willst über Array-Indices darauf zugreifen und typisierten Variablen zuweisen.
Das Thema liegt weit vor WPF und MVVM irgendwo bei den Grundlagen 😜

Du kannst aber auch ohne Parameter arbeiten und aus dem Command ein DependencyObject mit zwei DependencyProperties machen, die Du dann normal binden kannst.
Ich persönlich würde das eher mit einem Trigger lösen, aber das ist vermutlich Geschmackssache.

By the way:
Wozu der Converter?
Zum Debuggen oder umgeht der ein Problem?

11.02.2022 - 16:09 Uhr

Da hätte ich vielleicht erwähnen sollen, dass ich ein Flat UI-Design anstrebe und daher das Window kein Frame oder Kopfleiste mehr hat.

Ich hab sowas in der Art mit dem MetroWindow von MahApps gemacht. Ist etwas umständlich, aber dafür muss ich die Minimize, Close und Drag-Funktion nicht nachbauen.
Aber ja, als eigenständiger Command geht das natürlich auch, was nun besser ist, weiß ich nicht.

Da ich das aber so in der Form einmalig mache und die Menüleiste immer wieder verwende stelle ich mir die Frage wie tragisch diese Ausnahme ist und ob ich das einfach so lassen soll.

Das musst Du entscheiden, der Gedankengang ist zumindest richtig - also die Frage, ob das hier sinnvoll oder übertrieben ist.
Kein Pattern ist eine fixe Regel, es sind nur Empfehlungen und - noch wichtiger - Erklärungen der Ziele dahinter.
Du musst dir überlegen, wie weit Du der Regel folgen bzw. ab wann Du nicht mehr folgen magst und welche Vorteile oder Nachteile das mit sich bringt.

Also du meinst der Command selbst ist schon im ViewModel initialisiert, nur die Definition vom Command wird in einer eigenen Klasse definiert, worin dann auf die View zugegriffen wird, richtig?

Wenn irgendwelcher ViewModel-Code auf UI-Funktionen zugreift, die nicht abstrahiert werden kann, dann ist das eine MVVM-Verletzung.
In deinem Fall sorgt der Command dafür, dass ein direkter Zugriff auf ein Window erfolgt, damit zerstörst Du die Ziele hinter MVVM: Austauschbarkeit und Testbarkeit.
By the way, Du hast ja auch schon gemerkt: Window.GetWindow() will ein WPF-DependencyObject und kein ViewModel 😉 Da beginnt dann das Chaos, wie kommst Du an ein Control?

Ein Command muss aber nicht zum ViewModel gehören, er kann auch unabhängig sein.
In den Stackoverflow-Antworten gab's die Variante auch, dass die UI sich eine statische Command-Instanz besorgt, dessen Aufgabe es ist, den Parameter als Windows zu casten und zu schließen. Im XAML bindest Du darauf (x:Static) und als Parameter bindest Du an das Window (RelativeSource). Oder Du lässt den Parameter weg und nutzt einfach das MeinWindow.

Oder eben mit einer TriggerAction (ich hatte früher mal so eine CloseWindowTriggerAction) oder als Behavior, beide kennen das Control, für das sie angewendet werden und können dann auch Window.GetWindow() nutzen. Oder Du lässt das weg und nutzt einfach das MeinWindow.
Der Command ist vermutlich die einfachste Variante.

11.02.2022 - 13:50 Uhr

Close? Das kann doch das Window selber?
Wenn Du eine eigene Close-Optik haben willst, lohnt sich ggf. der Blick auf MahApps, das bietet eine eigene flexiblere Window-Ableitung.

Streng genommen ist deine Verletzung aber eine Verletzung von MVVM, da dein ViewModel direkten Zugriff auf die UI-Objekte benötigt.

Bevor ich jetzt alles doppelt und dreifach erkläre, hier wurde es schon erklärt:
how-to-bind-close-command-to-a-button/5634716
Bedenke: Das ist einige Jahre alt, vieles ist gleich, aber nicht alles, doch das Prinzip bleibt.

In jedem Fall sollte das ViewModel nichts mit dem Window anfangen. Es darf aber in einem Command passieren, nur darf der nicht im ViewModel sein.
Und ich würde alle Umsetzungen vermeiden, die CodeBehind benötigen, meine Favoriten sind also alle die, die den Code ausgelagert haben, entweder als Trigger, Command oder Behavior.

10.02.2022 - 19:39 Uhr

Usercontrol ist an sich ja keine View, auch wenn es optisch programmiert wird. In diversen Videos habe ich nun gesehen, dass die immer die komplette Logig von Buttons etc. in dem UC auch im Codebehinde abwickeln.

Deshalb mag ich solche Videos nicht.

Nein, ist natürlich nicht richtig - zumindest meistens.
Du musst zwischen UI-Logik und UI-Logik unterscheiden 😜 (Mir fallen keine geeigneten Namen ein. ^^

Zum Einen gibt es die UI-Logik, die einzig und allein für die UI notwendig ist, also z.B. Animationen oder dass in einer Tabelle immer das ausgewählte Item mittig angezeigt wird und so weiter. Diese Logik hängt direkt an der View und braucht häufig auch View-Objekte, um zu funktionieren.
Und dann gibt es die Logik, die tatsächliche Business-Beziehungen hat, wie z.B. wann ein bestimmter Dialog angezeigt werden soll, oder nach welchen Einstellungen welche Business-Funktion ausgeführt werden soll, oder welche Property-Änderung zu welcher Neu-Berechnung führt und so weiter.

Die "erste" UI-Logik "darf" im Code-Behind stehen, aber ich würde es möglichst vermeiden, da WPF nicht darauf ausgelegt ist und Du dann nur "gegen" WPF arbeitest. Außerdem geht es da leicht unter.
Es gibt aber zig andere Möglichkeiten, diese Logik passend in WPF zu integrieren, z.B. Converter, (Microsoft.Xaml.Behaviors.Wpf) Behaviors oder Trigger. Von Triggern gibt's allerdings eine Menge: Control-Trigger, Template-Trigger, Style-Trigger und "Behavior-"Trigger (Microsoft.Xaml.Behaviors.Wpf), die ersten drei sind die gleichen, aber in unterschiedlichen Situationen. Und dann gibt's noch MarkupExtensions (z.B. Binding oder StaticResource sind MarkupExtensions), da werden die Möglichkeiten erst recht kompliziert und man sollte es mit ihnen nicht übertreiben.
Alles in allem kein leichtes Thema, aber wenn man die Tricks kennt, kann man auf diese Weise sehr einfach solche Logik im XAML nutzbar machen und auch wiederverwenden.

Die zweite Art der UI-Logik ist häufig (nicht immer) besser im ViewModel aufgehoben, die erste Art darf da aber auch landen. Hierfür verwendet man hauptsächlich klassische Properties und Commands, es gibt aber noch ein paar kleine Tricks mehr.

Was nun der richtige Weg ist, muss man sich je Situation anschauen, das Hauptziel ist dabei Übersicht.
Deshalb sollte auch möglichst nichts im CodeBehind stehen, denn da geht es leicht unter und der C#-Code mit WPF-Objekten wird leicht unübersichtlich - und man kann es nur schwer wiederverwenden.
Wenn man View-Objekte braucht, sollte es aber auf keinen Fall ins ViewModel, das sollte möglichst unabhängig von der View bleiben. Für kleine Projekte könnte ich es aber auch verstehen, wenn man bei dem Punkt eine Ausnahme macht, ich würde es aber nicht machen.

Ja da werde ich nun ein BaseViewModel oder ObservableObject erstellen dafür

Guck dir Microsoft.Toolkit.Mvvm an, das verwende ich aktuell auch.
Perfekt finde ich sie nicht, aber das ist wohl eher Geschmackssache.

Mein Projekt ist wohl verhältnismässig auch zu klein.

Das ist ein häufiger Knackpunkt bei MVVM, da es sich hauptsächlich an größere Projekte richtet.
Zum Verstehen ist das natürlich ein Problem (deshalb auch mein Extrem-Beispiel mit den DTOs), mittlerweile würde ich aber auch bei kleinen Projekten MVVM nutzen, einfach weil es fast schon reflexartig funktioniert ^^

09.02.2022 - 18:52 Uhr

Es hat nichts damit zu tun, dass Du ein Application.Current-Objekt siehst oder nicht, sondern dass WPF das von sich aus macht.
Sie müssten schon die WPF eigene Start-Route nach entwickeln, um ohne Application-Objekt auszugehen und davon gehe ich mal nicht aus.

Und wenn Du nicht weist, wann es das Objekt gibt: Bau einen Button, der das Application-Objekt sucht und untersucht, den drückst Du dann, wenn alles fertig geladen ist.

09.02.2022 - 14:16 Uhr

Natürlich 🙂

@Spooner8:
Wirklich in einem Pattern oder einer Empfehlung definiert ist das Vorgehen bei dem Punkt nicht - oder ich kenne es nicht.
Es ist wichtig, dass die Zusammenhänge gewahrt werden, wie die Details dann aussehen, muss je Situation entschieden werden - das ist dann deine Aufgabe.
Das kann mit vielen DTOs sein, oder ein DTO je Entity oder mehrere DTOs je Funktion (nicht auf eine Entity beschränkt), etc. Oder Du gibst die Entities weiter, allerdings hat das auch ein paar versteckte Nachteile.

mehrere DTOs je Funktion (nicht auf eine Entity beschränkt)

Z.B. habe ich erst heute ein solches DTO geschrieben, in dem ich mehrere Daten aus verschiedenen Entities aufbereitet zusammenfasse.
Es muss also keine exakte Entity-Kopie sein, ein DTO sollte so aussehen, wie es der Aufrufer (in dem Fall das ViewModel) braucht.

Oder Du gibst die Entities weiter, allerdings hat das auch ein paar versteckte Nachteile.

Dabei denke ich z.B. an das Entity Framework.
Die Entities weiter zu geben ist da zwar kein Problem, aber im ViewModel kann dann ein unbedachter Zugriff auf eine Referenz-Property dazu führen, dass Daten nachgeladen werden (Stichwort: Lazy Loading) - je nachdem, wie Du das aufgebaut hast.
Ohne Entity hat der Business-Service oder das Repository die volle Kontrolle, wann welche Daten geladen werden und kann ggf. spezifisch optimieren.
Bei kleinen Projekten mit einfachen Daten und nur ein paar tausend Datensätzen ist das herzlich egal, aber wenn die Referenzen komplexer werden und es um ein paar Millionen Datensätze geht, dann kann daraus plötzlich ein echtes Performance-Problem werden.

Welchen Weg Du wählst, hängt am Ende davon ab, was Du brauchst.
Bei mir (mit den vielen DTOs) kommt das hauptsächlich daher, dass ich verhindern möchte, dass ein DTO missverstanden wird.
Alle Properties im DTO werden bei der jeweiligen Funktion auch gebraucht und alles, was nicht im Konstruktor enthalten ist, ist optional. Das kommt daher, dass ich schon ein paar Projekte gesehen habe, wo ich erst mal in der Implementierung nachlesen musste, was nun wichtig ist und was nicht und wie die Zusammenhänge sind und am Ende war es doch nicht richtig. Das möchte ich mit meinen DTOs eindeutig machen, dass keine Fragen offen sind. Dafür nehme ich den verhältnismäßig großen Aufwand in Kaufe, allerdings sind meine Daten auch verhältnismäßig einfach, sodass es nicht sehr ins Gewicht fällt.

Aber das ist nur mein Weg, Andere machen es anders, doch als Beispiel, um die Abläufe zu erklären, taugt es definitiv.

09.02.2022 - 13:02 Uhr

@Th69: Danke 🙂

Aber ja, die DTOs sollten ein Extrembeispiel sein, damit klar wird, was ich mein.

Zu viel Aufwand ist es aber nicht, zumindest nicht pauschal.
Bei kleinen Projekten, wo die DTOs im Grunde gleich sind: Ja, definitiv übertrieben.

Bei größeren Projekten, wo sich die DTOs unterscheiden, kann das aber sehr nützlich sein.
Aktuell arbeite ich auch an so einem Projekt, in dem alle Daten gelesen, aber nicht alle Daten beim Create angegeben oder beim Update geändert werden können.
Außerdem kann ich auf diese Wiese steuern, welcher Wert Optional ist (public set und nullable) und welcher nicht (Konstruktor), auch mit Blick auf Nullable reference types.

Man könnte auch mit Vererbung arbeiten, aber wie gut das funktioniert, hängt wohl vom Projekt ab.
Bei meinem aktuellen Projekt wäre das kein Vorteil.
Oder man splittet in mehrere Klassen auf und referenziert dann darauf.

08.02.2022 - 19:16 Uhr

Bezogen auf den Code in diesem Beitrag: Beitrag #3834228

Wenn ich die Article-Name-Property übersehe, könnte man das so machen, ja.
Dennoch würde ich einiges anders machen:

  1. OnPropertyChanged in eine Basisklasse oder eine Implementierung von einem MVVM-Framework
  2. Die ObservableCollection ohne Setter.
  3. Die Article-Referenz entfernen und stattdessen die Properties einzeln aufführen, dann musst Du auch keine Änderungen in der Article-Referenz selber behandeln
  4. Ich würde mehrere Models nutzen, möglichst viel readonly machen ((und sie Dto nennen)

Punk 1 habe ich nur wegen der Vollständigkeit erwähnt, im Forum würde ich das auch so schreiben, wie Du, um mögliche Verständnisprobleme zu vermeiden.
Punkt 2 ist nicht notwendig, beseitigt aber die Notwendigkeit, neue ObservableCollection-Instanzen beachten zu müssen, für den Fall, dass Du im C#-Code darauf reagieren muss.

Punkt 3:
Du könntest eine Article-Referenz als Variable behalten, um z.B. eine Undo-Funktionalität auf der Basis zu entwickeln.

Wenn die Properties nicht geändert werden können, würde ich mir das ganze OnPropertyChanged-Zeug sparen.
Es kann ja auch ReadOnly-ViewModels geben, die dann kein INotifyPropertyChanged brauchen, also z.B. so:


public class ArticleViewModel
{
    private readonly Article _article;

    public string Name
    {
        get { return _article.Name; }
    }

    // Oder die Kurz-Schreibweise:;
    public string Name => _article.Name;
}

Punkt 4:
Beispiel:


namespace MyProject.Data;

public class ArticleEntity
{
    public int Id { get; }
    public string Name { get; }

    public ArticleDto(ArticleEntity entity)
    {
        Id = entity.Id;
        Name = entity.Name;
    }
}

namespace MyProject.Business;

public class ArticleDto
{
    public int Id { get; }
    public string Name { get; }

    public ArticleDto(ArticleEntity entity)
    {
        Id = entity.Id;
        Name = entity.Name;
    }
}
public class ArticleUpdateDto
{
    public int Id { get; }
    public string Name { get; set; }

    public ArticleUpdateDto(int id)
    {
        Id = id;
    }
}
public class ArticleCreateDto
{
    public string Name { get; }

    public ArticleCreateDto(string name)
    {
        Name = name;
    }
}
public class ArticleCalculationDto
{
    public int Many { get; set; }
    public int Important { get; set; }
    public int Calculation { get; set; }
    public int Values { get; set; }
}

Der Aufbau ist aber - zugegeben - ziemlich aufwändig und wird eigentlich erst bei größeren Projekten nützlich.
Dafür kann ich so aber für jede einzelne Aufgabe (Read, Create, Update) spezifisch steuern, wie die Daten aufgebaut sind.

Im ViewModel hast Du einige Aufgaben:

  1. Daten Laden
    Du bekommst ArticleDto, erstellst damit ArticleViewModels und aktualisierst deine Liste.

  2. Daten ändern
    Für den geänderten Artikel erzeugst Du ein ArticleUpdateDto und gibst ihn an einen Business-Service weiter, der anhand der ID den Artikel sucht, ändert und wieder speichert.

  3. Daten erstellen
    Für einen neuen Artikel erzeugst Du ein ArticleCreateDto und gibst ihn an ein einen Business-Service weiter, der einen Artikel erstellt, speichert und dir ein ArticleDto zurück gibt, den Du dann deiner Liste hinzufügst.

  4. Daten löschen
    Dafür brauchst Du nur die Id.

  5. Berechnung aktualisieren
    Das kannst Du entweder per Button-Klick (nicht automatisch) machen, oder bei jeder Property-Änderung.
    Diese Berechnung macht aber auch nichts anderes, als ein ArticleCalculationDto zu erzeugen und an einen Business-Service weiter zu geben. Der wiederum antwortet dir mit einem Preis oder für komplexe Berechnungen mit einem ArticleCalculationResultDto, wo dann alle Ergebnisse drin stehen.

Du hast dann also fünf Business-Methoden, die laden, ändern, erstellen, löschen und berechnen, alle unabhängig von jeder UI-Komplexität.
Und die Entity-Klasse ist praktisch leer (bis auf die Daten-Properties) und macht keine Zicken beim Serialisieren.
Das ViewModel sieht aber nichts von den Entities, es tauscht Daten immer über die Dtos aus und arbeitet selber mit ViewModels.

Und dann zeichnen sich auch die Grenzen der Schichten genauer ab:
Ich versuche mal eine kleine Tabelle zu skizzieren (Abt, wir brauchen ein eine Tabellen-Funktion 🙂):

Schicht => Inhalt => Aufgabe
===============================
Daten => Entity-Klassen => Dumm, reines Daten-Layout
Business => Business-Service-Klassen => Arbeiten mit den Entities und den Dtos und enthalten die wertvolle Logik, wegen der es das Programm überhaupt gibt
Business/View => Dto-Klassen => "Vermittler" zwischen Business und View für jede Situation
ViewModels => ViewModels-Klassen => Logik "unter" der View, bereitet die Daten für die View auf und ruft Business-Methoden auf
View => XAML, HTML, etc. => Wird an ViewModels gebunden und arbeitet mit ViewModel-Commands

In MVVM wird das erste M als Model bezeichnet, ich habe sie hier aber Dto genannt, weil "Data Transfer Object" die Aufgabe mMn. besser beschreibt und der Unterschied im Lesefluss größer ist.
Außerdem wird gerne gesagt, dass so viele Dtos/Models "zu viel" sind. Das kann sein, aber nach Lehrbuch-MVVM gehört das so und bei größeren Projekten lohnt sich das auch. Ob man bei kleineren Projekten darauf verzichten sollte ... ich würde es nicht tun.

07.02.2022 - 23:39 Uhr

Die Datenklassen kommen wie gesagt aus der SQL und da habe ich keine Logik ergänz oder ähnliches. Die sind und bleiben Dumm.

Die XML-Klassen auch Daten-Klassen, nur dass sie nicht für ein SQL-Mapping sondern für ein XML-Mapping gedacht sind.
Die haben schon eine ganz entscheidende Aufgabe: Sie definieren, wie die Daten aussehen. Mehr Aufgaben sollte sowieso kaum etwas haben.

Es gibt aber nunmal auch Klassen wie meine die sehr viele Eigenschaften haben müssen, da es keinen Sinn macht die Klasse in viele Stücke zu zerkleinern

Das kommt sicher vor, aber ich behaupte, dass es eher selten wirklich keinen Sinn ergibt, die Daten aufzusplitten.
Auf Daten-Ebene (XML und SQL) gebe ich dir Recht: Nicht aufteilen, denn wie gesagt: Die sollen die Daten beschreiben - und zwar exakt so, wie sie auch wirklich aussehen sollen.
Auf Business- bzw. View-Ebene (Model und ViewModel) kann das aber durchaus nützlich sein, wenn Du die Daten inhaltlich trennst, einfach um es übersichtlicher zu machen.

Und Eigenschaften sollten auch generell nicht so viel tun. Die dürfen ein PropertyChanged-Event werfen, Daten prüfen, etc. - aber auf keinen Fall aufwäbdige Logik, Du verlierst sonst nur den Überblick.
Dann mach die Properties lieber readonly (oder private set) und biete eine Methode an, die irgendeine komplexere Aktion durchführt und dabei den eigenen Zustand aktualisiert
Und siehe da, Du hast die vielen komplexen Properties auf eine lange Liste langweiliger Auto-Properties (oder wie auch immer die NotifyPropertyChanged-Implementierung aussieht) reduziert. Wenn das nicht geht, weil zu komplex oder es würden zig verschiedene Methoden bei raus kommen, dann vermischst Du vermutlich wieder Aufgaben.

hätte ich die "Artikel" Klasse einmal also "Dumme" Klasse nur mit den Eigeschaften und ohne INotifyPropertyChanged erstellen sollen und einmal die quasi selbe Klasse aber mit den Methoden etc. die dann auch die Berechnungen durch führt.

Ja und nein. "Berechnungen" klingt nach etwas, was man wunderbar auslagern kann und - weil wichtige Business-Logik - auch sollte.
Du hast dann die dumme Models, die quasi dummen ViewModels und einen Business-Service, der z.B. den Preis berechnen kann.
Das dumme Model liefert die Daten, das ViewModel bekommt sie, füllt die eigenen Properties und fragt beim Service nach dem Preis. Wenn sich im ViewModel etwas ändert, fragt es wieder nach dem Preis und so weiter. Zwischen ViewModel und Service kann wieder ein anderes Model liegen, zumindest gehe ich davon aus, dass eine Preis-Berechnung nicht alle Daten (wie z.B. den Namen) braucht und vermutlich unterscheiden sich die benötigten Daten je Berechnung.
Das ganze machst Du dann für jede nötige Berechnung und die verbleibende Komplexität im ViewModel ist ein simples: Welche Property-Änderung erzwingt welche Berechnungen?
Das hat dann auch gleich den schönen Nebeneffekt, dass Du den Service leicht UnitTesten kannst und - um beim Beispiel zu bleiben - bei Preisen lohnen die sich definitiv.

Wie gesagt: Das ViewModel spielt Bindeglied zwischen Business und View, mehr nicht.
Im ViewModel hat keine komplexe Logik etwas verloren. Dort gehört so Logik rein wie "Welcher Dialog muss bei Ergebnis X angezeigt werden und welcher bei Y?"
Das ViewModel übernimmt das, was die Business-Services nicht können, weil sie nichts mit der UI tun dürfen.

Und ja, das INotifyPropertyChanged ist (meistens - kann ja auch anders genutzt werden) auch UI, denn dadurch greifst Du direkt in UI-Abläufe ein und sowas wie der aktuelle Thread wird plötzlich ein brandgefährliches Thema.

07.02.2022 - 21:20 Uhr

glaub mir ich kann meinen Code nicht wirklich schöneer Designen.

Das stimmt garantiert nicht - schon das Entwirren der verschiedenen Schichten wäre ein großer Punkt.

Es ist nur so, dass beim Deserialisieren mit jeder Eigeschaft die beschrieben wird, auch die "OnPropertyChanged" ausgelöst wird und bei gewissen Eigeschaften sind dabei auch weitere Methoden angesprochen.

sondern dass beim Deserialisieren einfach gewisse Methoden ausgelöst werden wovon eine halt ein Feld temporär ändert. Sobald ein weiterer Subartikel später hinzugefügt wird, korrigiert sich das von selbst.

Fällt dir nicht auf, wie Du die ganze Zeit gegen die Nachteile deiner Daten-Logik-Misch-Konstruktion ankämpfst?
Es hat einen Grund, warum jedem halbwegs erfahrenen Entwickler bei sowas das Gesicht einfriert. ^^

Der Rest ist bereits mit Bindings geregelt.

Das Binding alleine ist allerdings kein MVVM, sondern nur ein Werkzeug. Du kannst auch alles perfekt mit Bindings aufbauen und trotzdem grandios an MVVM vorbei schießen.

Binding ist die Trennung in drei Bereiche: Model, ViewModel und View.
Model
Dumme Transfer-Klassen (ich nenne sie oft "*Dto" für "DataTransferObject" oder stumpf "Model"), die die Daten je Situation passend aus der Business-Schicht zum ViewModel transportieren. Bei kleinen Projekten sieht man auch häufig, dass das direkt die Daten-Klasse aus der Daten-Schicht (meist "*Entity" genannt) sind, ob das gut oder MVVM-konform ist, kann man drüber streiten.
View
Reine View ohne Logik. Einzige Ausnahme: Reine UI-Logik, z.B. Fokus-Kontrolle oder Farb-Änderungen. Bei WPF geht das meist größtenteils in XAML.
ViewModel
Verbindung zwischen Model und View. Das ViewModel sieht häufig anders aus als das Model, übernimmt die Daten aus dem Model und implementiert so Automatismen, dir für die UI notwendig sind. Das könnte z.B. sein, dass die Änderung eines ausgewählten Artikels dafür sorgt, dass bei dem zuvor ausgewählten Artikel die Änderungen rückgängig gemacht werden.
Wenn das ViewModel Daten laden soll, bekommt es X Models und baut sich passende ViewModels dazu.
Wenn das ViewModel Daten speichern soll, nimmt es X ViewModels, baut die Models dazu und gibt sie weiter.
Außerdem ruft das ViewModel natürlich Business-Logik auf und zeigt ggf. Ergebnisse oder Fehler an.

Und dazu gibt die Business-Schicht mit Business-Services. Die bekommen die DTOs (oder geben sie zurück), setzen die Logik um, die nicht mit der UI zu tun hat und arbeiten ggf. mit den Entities und den tatsächlichen Daten. Bei kleinen Projekten wird das auch gerne mal weg gelassen, aber wie gesagt: Kann man drüber streiten.

Ich habe z.B. häufig drei Models pro ViewModel: ArticleViewModel, ArticleDto, ArticleCreationDto. ArticleUpdateDto. Die sind alle verschieden, vielleicht nur ein bisschen, aber alle perfekt auf das zugeschnitten, wofür ich sie brauche. Z.B. hat das ArticleDto alle Daten, das ArticleCreationDto hat keine ID und auch keine generierten Daten und das ArticleUpdateDto hat nur Daten, die man ändern können muss.
Und darunter gibt's dann noch eine ArticleEntity, die nochmal anders aussieht, damit zum richtigen Datenbank-Schema passt und EFCore damit arbeiten kann - oder in deinem Fall XML.

Wenn du einen guten Link zu einem Video (vorzugswiese deutsch) kennst, bin ich daran interessiert.

Kenne ich nicht, ich hab's auf die "harte Tour" gelernt, also zig Quellen und Meinungen lesen und zig verschiedene Wege ausprobieren, bis ich den Weg gefunden habe, der sich für mich bewehrt hat. Andere machen das sicher nochmal etwas anders, so hat jeder seinen Stil, aber solange die wichtigen Grundregeln eingehalten werden, funktioniert auch MVVM_

06.02.2022 - 23:09 Uhr

habe das in der #Region übersehen weil ich die Variablen meist zugeklappt habe

Ich hasse Regions und Du solltest sie dir ganz schnell wieder abgewöhnen - einen ganz wesentlichen Grund hast Du gerade selber erkannt 😉
Es gibt kein Grund für Regions, die man nicht mit einem besseren Code-Design lösen könnte und dabei muss man noch nicht mal fit mit Architekturen und Patterns sein.
Naja - einen Grund gibt es: Chaos-Projekte, die man nicht aufräumen kann, da können Regions helfen, nicht völlig im Chaos verloren zu gehen.

Und was deine Beschreibung angeht: Ich kann nicht folgen.
Für mich klingt das so, als würdest Du einen Dirty-Workaround um andere Dirty-Workarounds bauen, weil Du irgendwann am Anfang etwas falsch aufgebaut hast.
Eine Vermutung: Datenklassen sollten keine Logik haben, sie sollten nur die Daten beschrieben - was häufig von der Beschaffenheit der Logik abweichen kann.
Ein mögliche Lösung: Reine Datenbeschreibung und eine Klasse, die das Lesen und Schreiben der Daten realisiert, sie arbeitet ausschließlich mit Listen dieser reinen Daten-Klassen. In der UI liest Du darüber die Daten und erstellst auf der Basis dann deine ViewModels (wenn wir bei MVVM bleiben), auf der Ebene kannst Du dann auch Beziehungen zwischen Klassen erstellen, die es in den Daten nicht gibt oder nicht geben darf.

Wenn z.B. ein Artikel viele Sub-Artikel haben kann, die wiederum als normale Artikel aufgeführt sind:


<Articles>
    <Article Id="1" Name="Computer">
        <SubArticles>
            <ArticleRef Id="2" />
            <ArticleRef Id="4" />
        </SubArticles>
    </Article>
    <Article Id="2" Name="Graphics Card">
        <SubArticles>
            <ArticleRef Id="3" />
        </SubArticles>
    </Article>
    <Article Id="3" Name="GPU">
    </Article>
    <Article Id="4" Name="CPU">
    </Article>
</Articles>

Die Daten-Klassen sehen dann exakt genau so aus.
Auf UI-Ebene nimmst Du die Artikel-Liste und baust mit einem Dictionary ein ID-Artikel-Mapping auf.
Wenn Du mehr Infos zu den Sub-Artikeln brauchst, schau in dem Sub-Artikel nach und hole dir den tatsächlichen Artikel anhand der ID. Oder Du weist direkt die Referenzen zu, je nachdem.
Beim Speichern muss das ganze natürlich umgekehrt ablaufen.

06.02.2022 - 22:21 Uhr

Das hat mit MVVM erst mal nichts zu tun, sondern einer simplen Schichten-Architektur. Serialisierung gehört in die Daten-Schicht, MVVM beschäftigt sich ausschließlich mit der UI-Schicht.
Aber eine ObservableCollection ist technisch kein Problem, Du verbaust dir damit nur jede Möglichkeit, irgendwas in den UI-Daten anders zu behandeln, als in der Daten-Schicht, was - meiner Erfahrung nach - aber in ungefähr jedem Projekt nötig wird 😉 Außerdem hast Du durch das Vermischen von UI und Daten eine Abhängigkeit, die dem einen oder anderen Entwickler schon das Genick gebrochen hat - symbolisch gemeint.

Aus einer Methode kann es ja nicht stammen, da die nicht relevant sind für das XML oder?

Die Methoden sind für alles relevant.
Sie definieren auch die Daten, die Du serialisieren willst und da Du alles in einem Tops wirfst (s.o.), hat eine zirkuläre Abhängigkeit in einer Methode natürlich direkte Auswirkungen auf die Daten, die Du serialisieren willst. Stichwort: Referenz

Also wenn Artikel A einen Sub-Artikel B hat, der einen Sub-Artikel A hat und Artikel A zufällig die gleiche Instanz ist - wo hört er dann mit der Serialisierung auf? A hat B hat A hat B hat A hat B hat A hat B hat A hat B hat A ...

06.02.2022 - 20:51 Uhr

Vorweg:
ObservableCollection?
Die braucht man bei XML-Serialisierung normalerweise nicht, das deutet eher auf ein Vermischen der Schichten hin: View und Data

Plötzlich funktioniert das Serializen in die XML nicht mehr, da er mir sagt ich habe einen Zirkulären Verweis in der einen Klasse.

"Plötzlich" ändert sich sicher nichts 😉
Also hast Du irgendwas zwischen diesem "Plötzlich" und dem letzten funktionsfähigen Test gemacht, was zu dem Fehler führt.

Die Quelle des Fehler sollte aber auch in der Exception stehen - ggf. InnerException?
Darüber hinaus bleibt nur: Suchen. Oder Du kommentierst einzelne Teile aus und guckst, was passiert.
Es hilft auch, ein Klassendiagram zu zeichnen (ich glaube, Visual Studio kann sowas auch generieren), da fallen zirkuläre Referenzen schneller auf.
Aber "Subartikel" klingt, als könnte es eine Referenz auf "Artikel" haben.