myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
» Regeln
» Wie poste ich richtig?
» Forum-FAQ

Mitglieder
» Liste / Suche
» Wer ist wo online?

Ressourcen
» openbook: Visual C#
» openbook: OO
» Microsoft Docs

Team
» Kontakt
» Übersicht
» Wir über uns

» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Entwicklung » GUI: WPF und XAML » Wie kann ich ein Event werfen, wenn alle Properties initial gebunden wurden?
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | Thema zu Favoriten hinzufügen

Antwort erstellen
Zum Ende der Seite springen  

Wie kann ich ein Event werfen, wenn alle Properties initial gebunden wurden?

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
_Cashisclay _Cashisclay ist männlich
myCSharp.de-Mitglied

Dabei seit: 29.10.2014
Beiträge: 274


_Cashisclay ist offline

Wie kann ich ein Event werfen, wenn alle Properties initial gebunden wurden?

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo zusammen,

ich wusste mich schon nicht richtig beim Threadnamen auszudrücken.

Beim Laden meiner Ansicht werfe ich das Event Raise(String.Empty) um nachträglich nochmal alle Propertys zu durchlaufen um mein IDataErrorInfo auch beim neuladen der Daten immer aktuell zu halten.

Nun hab ich das Problem das dadurch auch zum Beispiel die Events für Checkboxen ausgelöst werden und dadaruch Werte von mir verändert werden. Wenn ich mir ein Flag im Loaded Event setze hilft mir das leider nicht, gibt es eine Möglichkeit herauszufinden ob aktuell irgendwelche PropertyChanged Events geraised werden?

Grüße

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von _Cashisclay am 14.02.2020 15:05.

13.02.2020 16:11 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 14.180
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Warum denn wieder Events statt MVVM? Wurdest doch schon mehrfach drauf hingewiesen :-)
13.02.2020 17:27 Beiträge des Benutzers | zu Buddylist hinzufügen
_Cashisclay _Cashisclay ist männlich
myCSharp.de-Mitglied

Dabei seit: 29.10.2014
Beiträge: 274

Themenstarter Thema begonnen von _Cashisclay

_Cashisclay ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Also ist es eigentlich gängig gar keine Events zu nutzen?
Und so etwas nur im MVVM im Setter umzusetzen?

*Edit : Aber mal abgesehen davon gibt es trotzdem eine Möglichkeit das überhaupt so umzusetzen? Rein interessehalber

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von _Cashisclay am 14.02.2020 08:51.

14.02.2020 08:10 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Palladin007 Palladin007 ist männlich
myCSharp.de-Mitglied

avatar-4140.png


Dabei seit: 03.02.2012
Beiträge: 1.306
Entwicklungsumgebung: Visual Studio 2019
Herkunft: NRW


Palladin007 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zwecks Übersicht solltest Du das nur im Setter machen.
Verteilst Du das überall sonst, musst Du sonst bei jeder Änderung an den Properties erst suchen, wo überall das Event geworfen wird.
Natürlich gibt es auch Ausnahmen, aber die müssen dann schon gut sein :D

Und ob das Geht: Beinahe alles geht ;)
Ein paar Ideen:
  • Du könntest ein geeignetes Event vom Control suchen und ans ViewModel durchreichen.
  • Oder Du registrierst ein Info-Event und wartest dann, bis das ausgelöst wird, z.B. mit einer der WaitHandle-Ableitungen oder der TaskCompletionSource.
    Der Nachteil ist aber, dass dieses Info-Event als letztes registriert werden muss, denn nur dann hast Du halbwegs eine Chance, dass es auch als Letztes ausgeführt wurde und selbst das ist nicht 100% sicher.
  • Ich persönlich würde mir eine ViewModelBase-Klasse schreiben, mit passenden GetValue- und SetValue-Methoden, die sich dann auch um das PropertyChanged-Event kümmern. Dort könntest Du dann auch ein weiteres Event (z.B. PropertyChangedFinished) einbauen, das dann definitiv nach dem PropertyChanged ausgeführt wird.
Ich behaupte aber, dass man in den meisten Fällen auch eine Alternative finden kann.
Ein klarer Programm-Ablauf wäre z.B. die einfachste Lösung, denn wenn das PropertyChanged-Event (z.B. im Setter) fertig ausgeführt wurde, sind auch alle Listener ausgeführt und die View aktualisiert - für Letzteres würde ich aber nicht die Hand ins Feuer legen :D

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Palladin007 am 14.02.2020 10:10.

14.02.2020 10:09 Beiträge des Benutzers | zu Buddylist hinzufügen
_Cashisclay _Cashisclay ist männlich
myCSharp.de-Mitglied

Dabei seit: 29.10.2014
Beiträge: 274

Themenstarter Thema begonnen von _Cashisclay

_Cashisclay ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Danke erstmal für die Antwort.

Okay, also wenn das gänig so ist das man solche Events im Setter steuern sollte dann würde ich das mal abändern, das hilft mir schon mal und löst natürlich auch erstmal das Problem.

Und zu der anderen Sachen, durch PropertyChange(String.Empty) löse ich ja nochmal alle Properties aus um die Ansicht zu aktualisieren, allerdings hilft mir da auch kein Event weiter um zu wissen wann auch das letzte Property durchlaufen ist. Und dazu fällt mir auch persönlich keine gute Lösung ein.

Mit deinem zweiten Punkt meins du dann wahrscheinlich ich registiere ein Event oder setze ein Flag quasi beim auslösen des Ganzen und muss das dann selber beim letzten Property dann wieder ändern? Werden die Properties von oben nach unten durchlaufen?

Ich hätte es auch am liebsten in meiner ViewModelBase Klasse allerdings fällt mir grad kein Weg ein wie ich selber herausfinde das gerade das letzte Property durchlaufen ist in der Klasse.
14.02.2020 10:19 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
_Cashisclay _Cashisclay ist männlich
myCSharp.de-Mitglied

Dabei seit: 29.10.2014
Beiträge: 274

Themenstarter Thema begonnen von _Cashisclay

_Cashisclay ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ich hab mal den Threadnamen nochmal angepasst.

Ich suche also quasi eine Lösung eine Möglichkeit mitzubekommen wenn ich mit Raise(String.Empty) nochmal alle Properties durchlaufe, wann dieser Prozess fertig ist.
14.02.2020 15:06 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
MrSparkle MrSparkle ist männlich
myCSharp.de-Team

avatar-2159.gif


Dabei seit: 16.05.2006
Beiträge: 5.453
Herkunft: Leipzig


MrSparkle ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von _Cashisclay:
durch PropertyChange(String.Empty) löse ich ja nochmal alle Properties aus um die Ansicht zu aktualisieren

Warum sollte man das tun? Die Ansicht wird doch automatisch durch das DataBinding aktualisiert. Wenn nicht, dann hast du irgendwo einen Fehler gemacht.
14.02.2020 15:34 Beiträge des Benutzers | zu Buddylist hinzufügen
_Cashisclay _Cashisclay ist männlich
myCSharp.de-Mitglied

Dabei seit: 29.10.2014
Beiträge: 274

Themenstarter Thema begonnen von _Cashisclay

_Cashisclay ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Möchte ich nicht ausschließen wenn ich einen Fehler gemacht habe, eventuell finden wir ihn ja.

Wie erkläre ich das am besten, ich habe verschiedene Nummern hinter jeder Nummer steckt ein Model mit verschiedenen Daten drinne innerhalb der Ansicht kann ich die Nummer ändern, dann werden auch die Daten aus dem Model aktualisiert, aber das ViewModel wurde ja schon instanziert und die UI wurde ja auch bereits fertig geladen, deswegen dache ich bzw. hab das dann mit dem Raise(String.Empty) gelöst.
14.02.2020 15:37 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
MrSparkle MrSparkle ist männlich
myCSharp.de-Team

avatar-2159.gif


Dabei seit: 16.05.2006
Beiträge: 5.453
Herkunft: Leipzig


MrSparkle ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von _Cashisclay:
innerhalb der Ansicht kann ich die Nummer ändern, dann werden auch die Daten aus dem Model aktualisiert

Die Ansicht ändert nur die Daten aus dem ViewModel. Das ViewModel wird dann erst (bei Bedarf) das Model ändern. Aber ohne Änderung im ViewModel funktioniert das DataBinding nicht.

Schau dir auch mal zur Veranschaulichung das Beispiel-Projekt in  [Artikel] MVVM und DataBinding an.
14.02.2020 15:42 Beiträge des Benutzers | zu Buddylist hinzufügen
_Cashisclay _Cashisclay ist männlich
myCSharp.de-Mitglied

Dabei seit: 29.10.2014
Beiträge: 274

Themenstarter Thema begonnen von _Cashisclay

_Cashisclay ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ich guck nebenbei schon im Beispielprojekt nach.



Ja, so passiert das bei mir ja auch, nur beim wechsel der Nummer, ändert sich bei mir das Model im Hintergrund wodurch das ViewModel ja nicht weiß das sich was geändert hat.
14.02.2020 15:47 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
MrSparkle MrSparkle ist männlich
myCSharp.de-Team

avatar-2159.gif


Dabei seit: 16.05.2006
Beiträge: 5.453
Herkunft: Leipzig


MrSparkle ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Die automatische Aktualisierung der Ansicht bei Änderungen kann nur funktionieren, wenn dadurch das PropertyChanged-Ereignis ausgelöst wurde. Und u.a. genau zu diesem Zweck gibt es das ViewModel. Wenn du Bindings auf das Model setzt, oder in einem Command nur das Model (nicht aber das ViewModel) änderst, dann kann es nicht funktionieren.

Die Lösung ist dann aber nicht ein Aufruf von PropertyChange(String.Empty), sondern ein geeignetes ViewModel zu implementieren.

Im Prinzip muß das ViewModel den gesamten aktuellen Zustand der UI repräsentieren. Das Aktualisieren des Models passiert dann zu einem bestimmten Zeitpunkt (z.B. beim Klick auf "Speichern") völlig unabhängig davon.
14.02.2020 16:39 Beiträge des Benutzers | zu Buddylist hinzufügen
Palladin007 Palladin007 ist männlich
myCSharp.de-Mitglied

avatar-4140.png


Dabei seit: 03.02.2012
Beiträge: 1.306
Entwicklungsumgebung: Visual Studio 2019
Herkunft: NRW


Palladin007 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Für die Zusammenarbeit zwischen Model und ViewModel habe ich immer ReadModel- und WriteModel-Methoden - oder eine passendere Bezeichnung bzw. Definition, je nach Anwendungsfall.

Wenn die Model-Instanz egal ist:

C#-Code:
public MyModel CreateModel() { /* ... */ }
public void UpdateModel(MyModel model) { /* ... */ }
public void ReadModel(MyModel model) { /* ... */ }

Wenn die Model-Instanz wichtig ist, könnte man sich entweder irgendwoanders Model und dazu passendes ViewModel merken, oder z.B. anhand einer ID zusammenführen.

Oder das ViewModel bekommt eine Referenz auf das Model und es gibt Methoden wie:

C#-Code:
public void UpdateModel() { /* ... */ }
public void ReloadModel() { /* ... */ }

Das ViewModel enthält dann sämtliche Daten, die für die UI relevant sind und verwaltet auch die Änderungen daran, bis man sie dann speichert oder wieder verwirft.

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Palladin007 am 14.02.2020 16:43.

14.02.2020 16:42 Beiträge des Benutzers | zu Buddylist hinzufügen
_Cashisclay _Cashisclay ist männlich
myCSharp.de-Mitglied

Dabei seit: 29.10.2014
Beiträge: 274

Themenstarter Thema begonnen von _Cashisclay

_Cashisclay ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ich muss ja an irgendeiner Stelle auf dem Schlauch stehen oder etwas falsch machen.

Ich hab das jetzt abgeändert das mein Model im ViewModel abgeändert wird und das jeweilige PropertyChanged Event ausgelöst wird.

C#-Code:
public PreparationOfPreparationIdModel  PreparationOfPreparationId
            {
                get { return _PreparationOfPreparationId; }
                set { _PreparationOfPreparationId = value; Raise(nameof(PreparationOfPreparationId)); }
            }

public string                       TextBoxEditorOfDistributionSamplingText
                {
                    get { return PreparationOfPreparationId.EditorOfDistributionSampling; }
                    set { PreparationOfPreparationId.EditorOfDistributionSampling = value; Raise(nameof(TextBoxEditorOfDistributionSamplingText)); }
                }

XML-Code:
                                            <mah:DateTimePicker Grid.Column="1"
                                                                    HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
                                                                        SelectedDate="{Binding PreparationOfPreparationId.EndOfPreparation.DateTimePickerDateOfDistributionSamplingSelectedDate, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
>

                                                <i:Interaction.Triggers>

                                                    <i:EventTrigger EventName="GotMouseCapture">

                                                        <i:InvokeCommandAction Command="{Binding PreparationOfPreparationId.EndOfPreparation.DateTimePickerDateOfDistributionSamplingGotMouseCaptureCommand}"/>

                                                    </i:EventTrigger>

                                                </i:Interaction.Triggers>

                                            </mah:DateTimePicker>

Allerdings aktualisiert er nach Änderung des Models eben nicht die einzelnen Propertys im View ab und mein IDataErrorInfo geht damit auch flöten.


Grüße

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von _Cashisclay am 18.02.2020 16:42.

18.02.2020 16:42 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
FZelle
myCSharp.de-Poweruser/ Experte

Dabei seit: 23.04.2004
Beiträge: 9.851


FZelle ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Und du bist ganz sicher das DateTimePickerDateOfDistributionSamplingSelectedDate in EndOfPreparation in PreparationOfPreparationId auch ein PropertyChanged auslöst?
18.02.2020 18:40 Beiträge des Benutzers | zu Buddylist hinzufügen
Palladin007 Palladin007 ist männlich
myCSharp.de-Mitglied

avatar-4140.png


Dabei seit: 03.02.2012
Beiträge: 1.306
Entwicklungsumgebung: Visual Studio 2019
Herkunft: NRW


Palladin007 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Diese Property-Namen sind eindeutig zu lang ...
Man schreibt im ViewModel auch nicht in den Namen, für welches Control das ist, sondern nur, was der Wert bedeutet und reduziert dann das weg, was z.B. anhand des Klassennamens schon eindeutig ist.

Wegen der Model-Property:
Die würde ich entfernen, das verleitet dazu, die zu nutzen und die eigentlich sinnvollen Properties daneben zu umgehen.

Und genau das tust Du nämlich auch:
Du bidest an die Property vom Model, nicht vom ViewModel und das Model kümmert sich vermutlich nicht um das PropertyChanged.

Daher, Fautregel:
Die View sollte nix von dem Model sehen können, das bleibt private oder wird gar nicht erst gespeichert.

Wenn Du es nicht speicherst, dann steckst Du die Model-Instanz von außen in die Speichern/Laden-Methoden rein. Das ViewModel hat dann für jede Property normal ein private Field, wo jeder Wert drin steht und die Speichern/Laden-Methoden arbeiten dann damit und mit dem Model.
Das hat auch den Vorteil, dass Du einen InMemory-Zustand hast, den Du leicht rückgängig machen kannst.

C#-Code:
public class MyViewModel
{
    private MyModel _lastModel;
    private int _field1;
    private string _field2;

    public int Field1
    {
        get => _field1;
        set { _field1 = value; Raise(nameof(Field1)); }
    }
    public int Field2
    {
        get => _field2;
        set { _field2 = value; Raise(nameof(Field2)); }
    }

    public void Load(MyModel model)
    {
        Field1 = model.Field1;
        Field2 = model.Field2;
        _lastModel = model;
    }
    public void Save(MyModel model)
    {
        model.Field1 = Field1;
        model.Field2 = Field2;
        _lastModel = model;
    }
    public void Reset()
    {
        // Sofern notwendig...
        // Wenn nicht, dann diese Methode und "_lastModel" entfernen.

        if (_lastModel != null)
            Load(_lastModel);
    }
}
19.02.2020 00:56 Beiträge des Benutzers | zu Buddylist hinzufügen
_Cashisclay _Cashisclay ist männlich
myCSharp.de-Mitglied

Dabei seit: 29.10.2014
Beiträge: 274

Themenstarter Thema begonnen von _Cashisclay

_Cashisclay ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Funktioniert super, danke.
19.02.2020 15:58 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
_Cashisclay _Cashisclay ist männlich
myCSharp.de-Mitglied

Dabei seit: 29.10.2014
Beiträge: 274

Themenstarter Thema begonnen von _Cashisclay

_Cashisclay ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Wäre es semantisch falsch wenn ich im Set zusätzlich auch noch in mein Model schreibe und das quasi nicht erst über eine extra Funktion wie Save() handhabe?

Weil ich in meinem IDataErrorInfo direkt mit den Models arbeite, weil ich dort ja zuvor auch immer die Daten aktuell hatte und das jetzt dann erst der Fall ist wenn ich die Save() Funktion aufgerufen habe.

Beispiel :

C#-Code:
    public int Field1
    {
        get => _field1;
        set { _field1 = value; model.Field1 = value; Raise(nameof(Field1)); }
    }
21.02.2020 13:20 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 14.180
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Musst Du selbst wissen.

Findest es ne gute Idee, wenn automatisch gespeichert wird?
Die meisten Anwender finden das nicht so toll.

Zusätzlich:
Wenn Du Mechanismen wie Reactive Extensions hast, um Modelle/Änderungen zu propagieren, dann informierst Du dauernd das "System" über änderungen - und alle darauf folgende Events und deren Event Handler.
Meistens will man das nicht.
21.02.2020 13:32 Beiträge des Benutzers | zu Buddylist hinzufügen
_Cashisclay _Cashisclay ist männlich
myCSharp.de-Mitglied

Dabei seit: 29.10.2014
Beiträge: 274

Themenstarter Thema begonnen von _Cashisclay

_Cashisclay ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Naja so gesehen würde er nicht permanent speichern bzw. er würde sie zwar immer ins Model schreiben, aber das Model schreibt nicht bei jeder Änderung in die Datenbank.

Mich persönlich würde es aktuell nicht stören, ich würde es sogar begrüßen.

Frag nur nach bevor ich in 2 Wochen irgendein Problem habe und mir jeder sagt was ist das denn für ein Müll verwundert
21.02.2020 13:42 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Palladin007 Palladin007 ist männlich
myCSharp.de-Mitglied

avatar-4140.png


Dabei seit: 03.02.2012
Beiträge: 1.306
Entwicklungsumgebung: Visual Studio 2019
Herkunft: NRW


Palladin007 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Natürlich kannst Du das machen, dann brauchst Du auch das private Feld nicht.

Du hast dadurch viele Stellen, die sich um Model-Instanzen kümmern müssen, z.B. musst Du dich dann auch darum kümmern, dass dein ViewModel damit umgehen kann, dass es plötzlich ein neues Model gibt, weil Du von der DB neu geladen hast. Außerdem könntest Du kein ViewModel ohne Model haben, Du kettest dich also für alle Abläufe an das Model-Layer, oder Du bringst den ViewModels bei, wie sie sowohl mit als auch ohne Model arbeiten können - wieder mehr Komplexität.

Lässt Du die Instanz raus, musst Du zwar eine Stelle schaffen, die zum Model das ViewModel finden und dann Laden/Speichern kann, allerdings ist das dann die einzige Stelle. Alle ViewModels sind im Grunde nur ein UI-Zustand, die keinerlei für die DB relevanten Objekte enthalten und daher sehr einfach getrennt vom Rest erstellt oder entfernt werden können.

Glaub mir, ich hab das auch schon ein paar Mal gemacht und jedes Mal bereut :D
So Load- und Save-Methoden sind genau zwei Methoden, mehr nicht. Wenn man es abstrahieren will, kann man auch Mapper-Frameworks ausprobieren.
Wenn Du die Model-Instanz im ViewModel hast, reicht das nicht mehr aus, das ViewModel wird komplexer. Außerdem lässt sich ein Methoden-Aufruf viel leichter abstrahieren, als ein Konstruktor-Parameter ;)

Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von Palladin007 am 21.02.2020 15:14.

21.02.2020 14:41 Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum Der Startbeitrag ist älter als 7 Monate.
Der letzte Beitrag ist älter als 7 Monate.
Antwort erstellen


© Copyright 2003-2020 myCSharp.de-Team | Impressum | Datenschutz | Alle Rechte vorbehalten. | Dieses Portal verwendet zum korrekten Betrieb Cookies. 21.09.2020 20:47