Autor Beitrag
Yankyy02
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 135
Erhaltene Danke: 21

Win 10 x64
C# VS 2017 Pro
BeitragVerfasst: Mo 04.09.17 20:07 
Hallo liebe Gemeinde der Entwickler-Ecke,
ich möchte meine nächste Anwendung in verbindung mit dem MVVM Light Toolkit umsetzen. Der Einstieg fällt einem nicht schwer da es ja mehrere Tutorials im Netz dazu gibt. Ich möchte allerdings jedesmal wenn ich eine View (Fenster) aufrufe das mir eine neue Instanz des zugehörigen ViewModels erstellt wird. Auch dazu gibt es einige Ansätze im Netz allerdings wirken diese nicht recht überzeugend auf mich da es eher Beispiele alla ... ich würde es halt so machen ... sind! Meine Frage ist nun ob jemand mit dem Toolkit bereits eine Anwendung umgesetzt hat bzw. vor dem selben Problem gestanden hat und mir nun sagen kann wie ich am besten vorgehen soll. Oder ist es doch so das man die Instanz des jeweiligen ViewModels über die ganze Laufzeit der Anwendung aufrecht erhalten sollte?

Im Anhang findet Ihr ein Beispielprojekt wie ich es mir vorgestellt bzw. aktuell umgesetzt habe.
Die relevanten Codeteile sind folgende .....

ausblenden XML-Daten
1:
2:
3:
4:
5:
6:
DataContext="{Binding PersonViewModel, Source={StaticResource Locator}}">
    <interactivity:Interaction.Triggers>
        <interactivity:EventTrigger EventName="Closing" >
            <command:EventToCommand  Command="{Binding Path=OnClosingCommand}"/>
        </interactivity:EventTrigger>
    </interactivity:Interaction.Triggers>

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
public RelayCommand OnClosingCommand => _closingCommand ?? (_closingCommand = new RelayCommand(OnClosing));

        private void OnClosing()
        {
            SimpleIoc.Default.Unregister<PersonViewModel>();
            SimpleIoc.Default.Register<PersonViewModel>();
        }


Bei jedem schließen des Fensters wird das ViewModel entfernt und wieder neu-registriert. Es gibt Ansätze wo beim erzeugen im ViewModelLocator eine eindeutige ID dem jeweiligen ViewModel "mitgegeben" wird und anhand dieser ID genau diese Instanz wieder entfernt wird. Da ich mit sicherheit immer nur eine Instanz benötige galube ich das ich diesen Ansatz nicht verfolgen brauche. :?:
Ich hoffe ich konnte einigermaßen beschreiben um was es mir geht und hoffe das mir jemand den richtigen Weg zeigen kann.

Vielen Dank!
Einloggen, um Attachments anzusehen!
_________________
the quiter you become, the more you are able to hear
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4076
Erhaltene Danke: 845

Win7
C++, C# (VS 2015/17)
BeitragVerfasst: Di 05.09.17 15:35 
Hallo,

ich habe zwar noch nicht mit MVVM Light gearbeitet, aber laut Internetrecherche scheint dein Weg wohl so üblich zu sein.
In Creating a new instance of an object each time method is called wird aber auch der Weg mittels des eindeutigen Keys bzw. sogar der Einsatz eines anderen IoC-Containers vorgeschlagen (bei vielen anderen kann man "transiente" Instanzen registrieren, anstatt nur Singletons).
Yankyy02 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 135
Erhaltene Danke: 21

Win 10 x64
C# VS 2017 Pro
BeitragVerfasst: Di 05.09.17 20:27 
Hey,
danke für deine Antwort. Ich habe mir den Thread im Link durchgelesen. Hier scheint es so als ob sein Problem meine Lösung wäre da er ja genau den umgekehrten Weg haben möchte und zwar immer eine Instanz des ViewModels in jeder neu erzeugten View was ja ohne hin der Fall ist außer man erzeugt mehrere Views(Windows).
Was ich nicht möchte ist das mischen mehrere Frameworks in einem Projekt zumindest was das MVVM Pattern angeht. Ich dachte es gäbe da Best Practice Wege um das umzusetzen. Ich suche noch ein bisschen den roten Faden, wenn du verstehst was ich meine.
Siehst du grundsätzlich ein Problem in der Umsetzung wie ich es aktuell habe? Was ich nicht rausfinden konnte ob beim entfernen und neu registrieren das ViewModel auch tatsächlich "zerstört" wird oder ob es noch irgendwo vorgehalten wird! :?:

LG

_________________
the quiter you become, the more you are able to hear
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4076
Erhaltene Danke: 845

Win7
C++, C# (VS 2015/17)
BeitragVerfasst: Mi 06.09.17 08:35 
Ein Problem sehe ich dabei nicht, nur etwas Schreibarbeit je ViewModel (evtl. kann man das aber in eine Basisklasse auslagern - mit generischem Parameter).

Und beim Unregister wird nur der Typ aus der internen Liste gelöscht (sowie der Cache geleert), die evtl. davon erzeugte Instanz dann aber normal über den Garbage Collector (GC) verwaltet (es wird also nicht Dispose() dafür aufgerufen).

Du kannst dir den Source-Code dazu aber auch anschauen: SimpleIoc.cs
Yankyy02 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 135
Erhaltene Danke: 21

Win 10 x64
C# VS 2017 Pro
BeitragVerfasst: Mi 06.09.17 22:33 
Vorerst sorry für die verspätete Rückmeldung .... dein Tip war glaub ich goldrichtig. Ich hätte mir den Source-Code eher ansehen sollen aber im Forum nachzufragen war natürlich gemütlicher. :oops:
In der Klasse SimpleIoc gibt es eine Methode die immer eine neue Instanz des benötigten ViewModels liefert ohne es zu cachen.
ausblenden C#-Quelltext
1:
2:
3:
4:
public object GetInstanceWithoutCaching(Type serviceType)
    {
        return DoGetService(serviceType, _defaultKey, false);
    }

In der View habe ich nun das Unloaded Event aboniert und im ViewModel IDisposable implementiert. Dispose() wird dann aufgerufen wenn Unloaded feuert.
ausblenden XML-Daten
1:
2:
3:
4:
5:
    <interactivity:Interaction.Triggers>
        <interactivity:EventTrigger EventName="Unloaded" >
            <command:EventToCommand  Command="{Binding Path=OnClosingCommand}"/>
        </interactivity:EventTrigger>
    </interactivity:Interaction.Triggers>


ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
public class PersonViewModel : ViewModelBase, IDisposable
    {
        private ObservableCollection<Person> _persons;
        private ListCollectionView _personsView;
        private RelayCommand _closingCommand;
        private bool _disposed = false;

        public PersonViewModel()
        {
            _persons = new ObservableCollection<Person>(Data.LoadPersons());
            _personsView = new ListCollectionView(_persons);
        }

        public ListCollectionView Persons => _personsView;

        public RelayCommand OnClosingCommand => _closingCommand ?? (_closingCommand = new RelayCommand(OnClosing));

        private void OnClosing()
        {
            this.Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _persons = null;
                    _personsView = null;
                }

                _disposed = true;
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~PersonViewModel()
        {
            Dispose(false);
        }

    }

Nun sollten doch keine unnötigen Instanzen des ViewModels mehr herumschwieren sobald ich die View (Fenster) schließe und der GC aufräumt... :?:

Ich weis du hast eingangs erwähnt das du mit dem Toolkit noch nicht gearbeitet hast aber da du allerhand Erfahrung hast kannst du sicher besser einschätzen ob da noch irgendwelche Gefahren auf mich lauern.

LG

_________________
the quiter you become, the more you are able to hear
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4076
Erhaltene Danke: 845

Win7
C++, C# (VS 2015/17)
BeitragVerfasst: Do 07.09.17 13:29 
Solange du nicht "unmanaged resources" in deinem ViewModel hast, würde ich auf IDisposable und Dispose komplett verzichten (es bläht m.E. nur den Code auf und erschwert die Wartung - und der GC erledigt auch so seinen Job).
Und wenn, wie ich oben schon schrieb, würde ich das in eine Basisklasse packen.

Ich habe dazu mal ein paar Links rausgesucht:
Shall I implement IDisposable within ViewModel?
how to cleanup view model properly?
Model-View-ViewModel (MVVM) – Part 2 – IDisposable
Define base class for ViewModel Revisited

Für diesen Beitrag haben gedankt: Yankyy02
Yankyy02 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 135
Erhaltene Danke: 21

Win 10 x64
C# VS 2017 Pro
BeitragVerfasst: Do 07.09.17 22:30 
Danke für deine Mühen die Links rauszusuchen die sind natürlich nützlich. Grundsätzlich würde ich sagen das ich das Disposable-Pattern verstanden habe. Das ViewModel so wie es hier ist ist natürlich nicht das produktive bzw. eines der Anwendung, sondern dient nur als Beispiel. In der Anwendung selbst kommt zusätzlich das Entity Framework zur Anwendung und da dachte ich wenn ich in den ViewModels IDisposable implementiere hier Dispose für den DBContext aufrufe und wer weiß was sonst noch so dazukommt. Mir geht es grundsätzlich darum das nicht unzählige ViewModels durch den Ioc im Speicher gehalten werden da es dadurch sicher früher oder später zu Fehlern kommen kann und beim laden der View immer die aktuellen Daten abgerufen werden und nicht der letzte Stand beim schließen des Fensters bzw. der View angezeigt wird. Genau dieses Vorgehen und eventuelle Fallstricke wird in keinem der Tutorials oder in der Doku beschrieben.
Durch den Aufruf von
ausblenden C#-Quelltext
1:
SimpleIoc.Default.GetInstanceWithoutCaching<PersonViewModel>();					

wird mir nun immer eine neue Instanz des jeweiligen ViewModels erzeugt. Was mir jedoch sorgen macht ist (und nicht beschrieben ist) ob diese erzeugten Instanzen irgendwo von mir disposed werden müssen wenn die View geschlossen wird.
ausblenden C#-Quelltext
1:
public static void Cleanup(){}					

wird ja nur aufgerufen, wenn die Anwendung beendet wird um alle registrierten ViewModels und Ressourcen freizugeben zumindest habe ich das so verstanden.

LG

_________________
the quiter you become, the more you are able to hear