Laden...

[Gelöst] - MVVM - SelectedItem für ItemsSource setzen (Memory Problem)

Erstellt von _Cashisclay vor 4 Jahren Letzter Beitrag vor 4 Jahren 2.943 Views
_
_Cashisclay Themenstarter:in
277 Beiträge seit 2014
vor 4 Jahren
[Gelöst] - MVVM - SelectedItem für ItemsSource setzen (Memory Problem)

Hallo zusammen,

ich hab aktuell das Problem das manche SelectedItems nicht gesetzt werden, weil anscheinend die ItemsSource zu spät geladen wird, wie kann ich denn am besten die Daten laden bevor der DataContext gesetzt wurde?

Grüße

709 Beiträge seit 2008
vor 4 Jahren

Hallo,
da die SelectedItems aus der ItemsSource-Auflistung stammen müssen, sind dir die doch eh erst bekannt, nachdem die Objekte der ItemsSource geladen wurden.
Wie meinst du das genau?

Gruß

_
_Cashisclay Themenstarter:in
277 Beiträge seit 2014
vor 4 Jahren

Hallo,

            public List<ClinicModel>                    ComboBoxMaterialPreparationProtocolsClinicItemsSource
            {
                get
                {
                    return _ComboBoxMaterialPreparationProtocolsClinicItemsSource;
                }
            }

            public ClinicModel                          ComboBoxMaterialPreparationProtocolsClinicSelectedItem
            {
                get { return Item.Clinic != null ? Item.Clinic : null; }
                set
                {
                    Item.Clinic = value;
                    Raise(nameof(ComboBoxMaterialPreparationProtocolsClinicSelectedItem));

                        Raise(nameof(LabelMaterialPreparationProtocolsAdditionalRequirementContent));
                }
            }

            public ICommand                             ComboBoxMaterialPreparationProtocolsClinicSelectionChangedCommand           { get; }


            public List<ClinicStaffModel>               ComboBoxMaterialPreparationProtocolsClinicStaffItemsSource
            {
                get { return _ComboBoxMaterialPreparationProtocolsClinicStaffItemsSource; }
                set
                {
                    _ComboBoxMaterialPreparationProtocolsClinicStaffItemsSource = value;
                    Raise(nameof(ComboBoxMaterialPreparationProtocolsClinicStaffItemsSource));

                        Raise(nameof(ComboBoxMaterialPreparationProtocolsClinicStaffSelectedItem));
                }
            }

            public ClinicStaffModel                     ComboBoxMaterialPreparationProtocolsClinicStaffSelectedItem
            {
                get { return Item.ClinicStaff != null ? Item.ClinicStaff : null; }
                set
                {
                    Item.ClinicStaff = value;
                    Raise(nameof(ComboBoxMaterialPreparationProtocolsClinicStaffSelectedItem));
                    
                        Raise(nameof(TextBoxMaterialPreparationProtocolsTimeShiftText));
                }
            }

Ich hab aktuell das Problem beim Laden meiner Eigenschaften das meine ClinicStaffItemsSource befüllt wurde, aber mein SelectedItem nicht richtig ausgewählt oder angezeigt wird.

301 Beiträge seit 2009
vor 4 Jahren

Ich hab aktuell das Problem beim Laden meiner Eigenschaften das meine ClinicStaffItemsSource befüllt wurde, aber mein SelectedItem nicht richtig ausgewählt oder angezeigt wird.

Was heißt denn "richtig" ?

Item.ClinicStaff = value;

Was ist diese "Item" ?

Und was hast du überhaupt bisher versucht und wobei kommst du nicht weiter?

709 Beiträge seit 2008
vor 4 Jahren

Ist Item.Clinic Teil von ComboBoxMaterialPreparationProtocolsClinicItemsSource?

Statt

get { return Item.Clinic != null ? Item.Clinic : null; }

kannst du auch

get { return Item.Clinic; }

schreiben.

T
461 Beiträge seit 2013
vor 4 Jahren
  
            public List<ClinicStaffModel> ComboBoxMaterialPreparationProtocolsClinicStaffItemsSource  
            {  
                get { return _ComboBoxMaterialPreparationProtocolsClinicStaffItemsSource; }  
                set  
                {  
                    _ComboBoxMaterialPreparationProtocolsClinicStaffItemsSource = value;  
                    Raise(nameof(ComboBoxMaterialPreparationProtocolsClinicStaffItemsSource));  
                    Raise(nameof(ComboBoxMaterialPreparationProtocolsClinicStaffSelectedItem));  
                }  
            }  
   

Liegt es vielleicht daran, daß du als ItemsSource z.Bsp. keine 'ObservableCollection' anstatt 'List' verwendest?

Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen... 😄

_
_Cashisclay Themenstarter:in
277 Beiträge seit 2014
vor 4 Jahren

Nicht richtig heißt das diese Property mir angezeigt wird :

<TextBox    Grid.Column="1"
                                                IsReadOnly="True"
                                                    HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
                                                        Opacity="0.5"
                                                            Text="{Binding MaterialPreparationOfProtocols.ComboBoxMaterialPreparationProtocolsClinicStaffSelectedItem.DateOfTraining, Mode=OneWay, StringFormat=dd.MM.yyyy, UpdateSourceTrigger=PropertyChanged}"
                                            Style="{StaticResource BaseTextBox}"/>

Mir aber mein SelectedItem in meiner ComboBox nicht ausgewählt wird.

Das Item entspricht meinem ObjectModel

_
_Cashisclay Themenstarter:in
277 Beiträge seit 2014
vor 4 Jahren

Ja, ist es.

@ThomasE. - Hat keinen Unterschied gemacht.

Grüße

Hinweis von Abt vor 4 Jahren

Keine Full Quotes
[Hinweis] Wie poste ich richtig?

_
_Cashisclay Themenstarter:in
277 Beiträge seit 2014
vor 4 Jahren

Also ich hab das Gefühl das es irgendwie daran liegt das die ItemsSource "später" geladen wird und das trotz Raise des SelectedItems es nicht ausreicht das die UI das Item auch setzt.

709 Beiträge seit 2008
vor 4 Jahren

Jetzt wurde noch MaterialPreparationOfProtocols eingeführt.
Wie steckt das da überall mit drin?

Edit:
Hast du mal mit Snoop oder ähnlichem nachgesehen, ob das Binding denn auch gültig ist oder ob es da irgendwo einen Fehler gibt?

_
_Cashisclay Themenstarter:in
277 Beiträge seit 2014
vor 4 Jahren

MaterialPreparationOfProtocols ist eigentlich nur das ViewModel nur steckt das noch in einer anderen Klasse mit drin vom Vorentwickler, hat darauf also keine Einwirkungen.

Nein, sagt mir persönlich auch nichts, also hab das noch nie versucht. Allerdings greift die TextBox ja korrekt auf den Datensatz zu. Und die ItemsSource ist auch befüllt und wird angezeigt.

Grüße

16.807 Beiträge seit 2008
vor 4 Jahren

Wenn Du die korrekten Typen wie ObservableCollection verwendest, spielt die Ladezeit prinzipiell keine Rolle.
Es wird aktualisiert, sobald die Items in der Collection sind (bezogen auf die Binding Direction).

Was soll denn dieses Raise() sein? Ist das eine eigene Implementierung von OnPropertyChanged() ?

Sieht mir so aus, dass das Rad an der Stelle neu erfunden wird.
Evtl. hier ein Fehler, dass die UI nicht korrekt benachrichtigt wird?

_
_Cashisclay Themenstarter:in
277 Beiträge seit 2014
vor 4 Jahren
        public virtual void Raise(string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

            if (IsTrackingChanges) _hasChanged = true;
        }

Es klappt ja auch mit anderen ComboBoxen nur mit dieser nicht, obwohl ich nichts anders mache, nur das die Daten erst zur Laufzeit geladen werden.

301 Beiträge seit 2009
vor 4 Jahren

Mir persönlich ist die ganze Geschichte etwas zu undurchsichtig beschrieben. Man sieht nicht wo / wann und wie du dein "Item" im Code setzt. Ich vermute das es damit zusammen hängt.

Dein Combobox Binding fehlt leider auch, denn wie du beschrieben hast scheint dein SelectedItem schon falsch zu sein?

Ich würde auch allgemein davon abraten im Xaml Vekettungen von Properties im Binding zu nutzen. Setz deinen Datacontext an deinem Steuerelement möglichst explizit so dass du maximal einen Punkt, besser aber gar keinen Punkt im Binding nutzen musst.

M
177 Beiträge seit 2009
vor 4 Jahren

Setze einen Haltepunkt nach dem Laden der Oberfläche und gib den DataContext vom ComboBox Control aus. Hast du im Output irgendwelche Binding Errors?

Wahrscheinlich passt ein Binding nicht.

Das könnte vlt auch helfen https://get-the-solution.net/blog/ItemsSource%20zu%20UserControl%20hinzuf%C3%BCgen

3.170 Beiträge seit 2006
vor 4 Jahren

Hallo,

bist Du ganz sicher, dass das SelectedItem, dass Du initial setzen willst, die selbe Instanz ist, die auch in der ItemsSource der ComboBox steckt?

Wenn Du das Item z.B. zweimal aus den selben Daten generierst, einmal für die Nutzung als SelectedItem und einmal für die Verwendung in der ItemsSource, dann hättest Du nicht die selbe Instanz und dann ergäbe sich genau Deine Fehlerbeschreibung.

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

T
461 Beiträge seit 2013
vor 4 Jahren

bist Du ganz sicher, dass das SelectedItem, dass Du initial setzen willst, die selbe Instanz ist, die auch in der ItemsSource der ComboBox steckt?

natürlich im Zusammenhang mit

Wenn Du die korrekten Typen wie ObservableCollection verwendest, spielt die Ladezeit prinzipiell keine Rolle.
Es wird aktualisiert, sobald die Items in der Collection sind (bezogen auf die Binding Direction).

zudem auch gut zu wissen wie

… wo / wann und wie du dein "Item" im Code setzt. Ich vermute das es damit zusammen hängt.

um es mal zusammenzufassen. 😉

Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen... 😄

_
_Cashisclay Themenstarter:in
277 Beiträge seit 2014
vor 4 Jahren

bist Du ganz sicher, dass das SelectedItem, dass Du initial setzen willst, die selbe Instanz ist, die auch in der ItemsSource der ComboBox steckt?

Ja, bin ich. Hab es auch gerade nochmal geprüft.

… wo / wann und wie du dein "Item" im Code setzt. Ich vermute das es damit zusammen hängt.

Im Konstruktor

<ComboBox   Grid.Column="1"
                                            Margin="4"
                                                ItemsSource="{Binding MaterialPreparationOfProtocols.ComboBoxMaterialPreparationProtocolsClinicStaffItemsSource, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                                    DisplayMemberPath="Name"
                                                        SelectedItem="{Binding MaterialPreparationOfProtocols.ComboBoxMaterialPreparationProtocolsClinicStaffSelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
W
955 Beiträge seit 2010
vor 4 Jahren

OK, dann rate ich auch einmal: wird die Collection im richtigen Thread aktualisiert? Läuft es im GUI-Context?

709 Beiträge seit 2008
vor 4 Jahren

Ohne im Debugger sichtbare und übereinstimmende Object-ID könnten es immer noch nur gleiche Objekte sein.

_
_Cashisclay Themenstarter:in
277 Beiträge seit 2014
vor 4 Jahren

Also das sind alle Daten die ich zu den Objekten im Debugger sehen kann, wenn ich noch irgendwas ergänzen kann oder noch irgendwo nachgucken kann, klär mich bitte auf.

Im Konstruktor werden die fehlenden Daten der ItemsSource geladen, das SelectedItem ist zu der Zeit schon bekannt, ich hatte ja immer gedacht daran liegt es, aber ihr meintet Ja der Eintrag wird gesetzt sobald die ItemsSource vorhanden ist.

_
_Cashisclay Themenstarter:in
277 Beiträge seit 2014
vor 4 Jahren

Ohne im Debugger sichtbare und übereinstimmende Object-ID könnten es immer noch nur gleiche Objekte sein.

Hm ...

bist Du ganz sicher, dass das SelectedItem, dass Du initial setzen willst, die selbe Instanz ist, die auch in der ItemsSource der ComboBox steckt?

🤔

Ich hab gerade mal

ComboBoxMaterialPreparationProtocolsClinicStaffSelectedItem = ComboBoxMaterialPreparationProtocolsClinicStaffItemsSource.Where(x => x.Id == ComboBoxMaterialPreparationProtocolsClinicStaffSelectedItem.Id).SingleOrDefault();

als Zeile ergänzt .. Und der Eintrag wird jetzt angezeigt .. Ich würde mich gern in einem Loch vergraben. Wie kann es denn sein das ich ein Item anhand der ID aus der Datenbank vom gleichen Objekttyp Lade was 100% von den Werten zur ItemsSource gehört, nicht als Member anerkannt wird?

            public ClinicStaffModel                     ComboBoxMaterialPreparationProtocolsClinicStaffSelectedItem
            {
                get { return ComboBoxMaterialPreparationProtocolsClinicStaffItemsSource != null ? Item.ClinicStaff != null ? ComboBoxMaterialPreparationProtocolsClinicStaffItemsSource.Where(x => x.Id == Item.ClinicStaff.Id).SingleOrDefault() : null : null; }
                set
                {
                    Item.ClinicStaff = value;
                    Raise(nameof(ComboBoxMaterialPreparationProtocolsClinicStaffSelectedItem));
                    
                        Raise(nameof(TextBoxMaterialPreparationProtocolsTimeShiftText));
                }
            }

Das kann ja nicht die Lösung des Problems sein ..

_
_Cashisclay Themenstarter:in
277 Beiträge seit 2014
vor 4 Jahren

bist Du ganz sicher, dass das SelectedItem, dass Du initial setzen willst, die selbe Instanz ist, die auch in der ItemsSource der ComboBox steckt?

Tatsächlich lag hier der Hund begraben. Das SelectedItem war vom selben Typ wie die ItemsSource. Die Daten haben 1 & 1 übereingestimmt, trotzdem kann man ein geladenes Object aus der ItemsSource nicht damit vergleichen.

Meine Lösung sah nun so aus das ich in der Klasse von dem Typ der ItemsSource folgendes verändert habe :

        public override bool Equals(object obj)
        {

            if (obj == null || !(obj is ClinicStaffModel))
                return false;

            return ((ClinicStaffModel) obj).Id == this.Id;

        }

        public override int GetHashCode()
        {
            
            return Id.GetHashCode();

        }
T
461 Beiträge seit 2013
vor 4 Jahren
        public override bool Equals(object obj)  
        {  
            ClinicStaffModel csm = obj as ClinicStaffModel;  
            return csm != null ? csm.Id == this.Id : false;  
        }  
  

Etwas aufhübschen 😉

Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen... 😄

16.807 Beiträge seit 2008
vor 4 Jahren

Etwas aufhübschen 😉

Extremer aufhübschen:

  public override bool Equals(object obj) => obj is ClinicStaffModel csm && csm.Id == Id;

😉

_
_Cashisclay Themenstarter:in
277 Beiträge seit 2014
vor 4 Jahren

Danke fürs aufhübschen, aber kann die Lösung vielleicht noch jemand bestätigen aus eigener Erfahrung? :rtfm: 🙂

301 Beiträge seit 2009
vor 4 Jahren

Aus eigener Erfahrung kann ich sagen:

  • Arbeite entweder mit denselben Objektinstanzen ( Sprich keine Notwendigkeit Equals zu überschreiben ). Dadurch hältst du dich auf jeden Fall daran deine Objektinstanzen anständig weiter zu reichen und zu verwerfen.

ODER

  • Arbeite mit Objekt ID's ( Sprich DisplayMember + ValueMember ) -> ValueMember ist hier deine ID damit das Steuerelement selbst das passende Objekt aus der Itemssource holt anhand ihres values.

Einen Mix aus beidem kann ich nicht empfehlen. Die Frage die du dir auch stellen solltest. Warum hast du überhaupt 2x eine Instanz vom selben Objekt? Bzw. mit demselben Value. Und wenn ja woher kommt es. Du ignorierst dieses potentielle Problem grade.

W
955 Beiträge seit 2010
vor 4 Jahren

Warum hast du überhaupt 2x eine Instanz vom selben Objekt? Bzw. mit demselben Value. Und wenn ja woher kommt es. Sowas passiert beispielsweise wenn der Client sich Objekte für eine ComboBox besorgt um eine 1:n-Beziehung abzubilden und am zu bearbeitenden Modellobjekt ein bereits ein solches Objekt hängt, was die ComboBox darstellen soll.
IEquatable<TModel> bzw Equals(TModel) zu überschreiben ist gefährlich wenn das Objekt gerade angefügt wurde, gespeichert wird und die ID sich ändert. (wenn der Client z.B. nicht mit einer GUID stempelt)