2016-09-29 3 views
0

Ich benutze Prism 5.0, und ich habe Probleme, es zu konfigurieren, um vorhandene Ansichten wiederzuverwenden. Immer wenn IRegionManager.RequestNavigate(string regionName, Uri source) aufgerufen wird, erstellt es eine neue Ansicht, statt die Ansicht zu verwenden, die zuvor erstellt wurde. Seltsamerweise gibt CLRProfiler auch an, dass der Regionsmanager von Prism Verweise auf alle zuvor erstellten View-Instanzen hält, was zu einem Speicherleck führt.verhindern, dass Prism eine neue Ansicht für die Navigation erstellt

Meine Ansicht Modelle implementieren INavigationAware, und true in IsNavigationTarget() zurückgeben, aber die Methode wird nie aufgerufen. Ich habe versucht, es mit demselben Ergebnis auch auf der Ansicht zu implementieren.

Als Test habe ich implementiert IActiveAware auf der Ansicht, die zeigt, dass es deaktiviert ist, sobald ich zu einer anderen Ansicht navigieren (ich bin mir nicht sicher, ob das relevant ist).

Ich habe diese Frage gefunden: PRISM WPF - Navigation creates new view every time aber meine V-VM Namenskonventionen entsprechen denen der Antwort (ich benutze AutoFac, übrigens).

Ich habe nur eine Problemumgehung gefunden: Entfernen der aktiven Ansicht aus der Region mit NavigationContext.NavigationService.Region.Remove() in der Implementierung des Ansichtsmodells INavigationAware.OnNavigatedFrom(). Wenn ich das mache, gibt der Regionsmanager von Prism den Verweis auf die Ansicht frei. Dies funktioniert, aber es scheint ineffizient zu sein, die Ansicht immer neu zu erstellen, wenn sie benötigt wird.

Fast alle verwandten Fragen zu SO fragen, wie man eine neue Sicht auf ein Navigationsereignis erstellt, also nehme ich an, dass das Standardverhalten ist, dass Ansichten wiederverwendet werden. Ich brauche Hinweise hier.

EDIT

Wir verwenden Autofac der AutofacExtensions.RegisterTypeForNavigation<T>(this ContainerBuilder builder, string name = null) die Ansichten zu registrieren. Wir verwenden IRegionManager.RequestNavigate() für die Navigation zwischen Ansichten. INavigationAware ist in ViewModels implementiert. Während jedoch INavigationAware.OnNavigatedTo() und OnNavigatedFrom() aufgerufen werden, wird IsNavigationTarget() nie aufgerufen (das letztere wird nicht aufgerufen, selbst wenn die Ansichten INavigationAware implementieren).

Ich kann erkennen, dass eine neue Ansicht erstellt wird, indem Sie einen Haltepunkt im Ctor der Ansicht festlegen. Ein CLRProfiler-Heap-Dump zeigt außerdem, dass der Regionsmanager so viele Instanzen der Ansicht enthält, wie oft er navigiert wurde. Die ViewModels werden nur einmal erstellt, da sie in AutoFac als Einzelinstanz registriert sind.

Als vorübergehende Maßnahme haben wir die Ansichten implementiert IRegionMemberLifetime, wobei KeepAlivefalse zurückgibt. Dies ist nicht sehr effektiv, da die Ansichten jedes Mal neu erstellt werden, wenn sie benötigt werden. Es verhindert jedoch, dass der Regionsmanager an früheren Ansichten festhält.

+0

müssen Sie IRegionMemberLifetime und KeepAlive-Eigenschaft verwenden. –

+0

@AyyappanSubramanian Wenn die Ansicht IRegionMemberLifetime implementiert und KeepAlive true zurückgibt, behält der Regionsmanager einen Verweis auf die Ansicht bei, erstellt jedoch beim Navigieren einen neuen Verweis (dies muss der Standardwert sein, wenn die Ansicht IRML nicht implementiert). Wenn KeepAlive den Wert false zurückgibt, wird die Ansicht verworfen und muss neu erstellt werden. Meine Frage war, wie Prism die ursprüngliche Ansicht wiederverwenden kann, also ist dies keine Lösung. – Drew

+0

Ich habe derzeit das gleiche Problem, aber nicht auf alle meine Ansichten. Es scheint irgendetwas unter einer bestimmten Seite zu sein, was mir Probleme bereitet. – user3265613

Antwort

0

Wenn IRegionManager.RequestNavigate (string region, Uri Quelle) genannt wird, erzeugt es eine neue Ansicht, anstatt die Ansicht verwenden, die zuvor

erstellt wurde ich dieses Verhalten nicht duplizieren. Wenn Sie das Navigationsframework verwenden, verwendet Prism standardmäßig jede Ansicht, unabhängig davon, ob Sie die INavigationAware-Schnittstelle verwenden. Dies bedeutet, dass die Ansichten im Regionsmanager beibehalten werden (kein Speicherleck, dies erfolgt absichtlich). Wenn Sie Sichten nicht erneut verwenden möchten, müssen Sie die IRegionMemberLifetime-Schnittstelle oder das IRegionMemberLifetime-Attribut verwenden und Prism mitteilen, dass Sichten nicht erneut verwendet werden dürfen. In diesem Fall wird die Ansicht aus dem Regionsmanager entfernt und eine neue erstellt.

Meine Ansichtsmodelle implementieren INavigationAware und geben true in IsNavigationTarget() zurück, aber die Methode wird nie aufgerufen. Ich habe versucht, es mit demselben Ergebnis auch auf der Ansicht zu implementieren.

Wenn dies der Fall ist, verwenden Sie nicht RequestNavigate. Möglicherweise fügen Sie der Region mithilfe von IRegion.Add manuell Ansichten hinzu, oder Ihr ViewModel wird möglicherweise nicht als DataContext festgelegt. Wenn dies der Fall ist, werden diese Methoden nicht aufgerufen.

Wie bestimmen Sie, ob eine neue Ansicht erstellt wird? Legen Sie einen Unterbrechungspunkt in der Ctor Ihrer Views und ViewModels fest? Registrierst du deine Ansichten richtig für die Navigation?

+0

Danke für die Antwort. Ich habe den Beitrag bearbeitet, um einige Dinge zu klären. – Drew

+0

Ich hasse es, alte Fragen wieder auferstehen zu lassen, aber ich habe genau das gleiche Problem. Es betrifft jedoch nur bestimmte Ansichten. Gibt es eine Möglichkeit, die Prismenquelle zu debuggen, während ich meine Hauptanwendung ausführe? – user3265613

+0

@ user3265613 Ich schlage vor, Sie greifen die Quelle von der Prismlibrary Github Seite und verweisen auf die Projektdateien anstelle des NuGet-Pakets. https://github.com/PrismLibrary/Prism/tree/master/Source/Wpf – Gusdor

0

Ich weiß, das ist eine alte Frage. Aber ich habe gerade dieses Problem selbst, und wenn andere Leute es haben, dann wird es wahrscheinlich irgendwann wiederkommen.

Das Problem, wie ich es hatte, war meine Seitennamen.

Um die Navigation zu erleichtern, habe ich eine PageNames-Klasse mit Inhalt wie folgt erstellt.

public static class PageNames 
{ 
    public const string MyPage= "MyPageView"; 
} 

und wenn Seiten Registrierung und Navigation meine Klassen waren etwas wie die folgende

Kernel.RegisterTypeForNavigation<MyPageView>(PageNames.MyPage); 

Die meisten meiner Seiten als ihre Klasse den gleichen Namen hatte, IE MyPageView = MyPageView. Bei einigen Seiten fehlte jedoch der View-Teil des Seitennamens.

Wenn PRISM überprüft, ob in einer Region eine Seite vorhanden ist, wird Folgendes ausgeführt.

protected virtual string GetContractFromNavigationContext(NavigationContext navigationContext) 
{ 
    if (navigationContext == null) throw new ArgumentNullException(nameof(navigationContext)); 
    var candidateTargetContract = UriParsingHelper.GetAbsolutePath(navigationContext.Uri); 
    candidateTargetContract = candidateTargetContract.TrimStart('/'); 
    return candidateTargetContract; 
} 

Dies gibt den Seitennamen zurück. MyPageView. Der Inhaltslader der Anforderungsnavigation ruft dann diese Methode auf, um zu prüfen, ob die Seite innerhalb der Region existiert.

return region.Views.Where(v => 
    string.Equals(v.GetType().Name, candidateNavigationContract, 
StringComparison.Ordinal) || 
    string.Equals(v.GetType().FullName, candidateNavigationContract, StringComparison.Ordinal)); 

, die ich (nicht sicher, wie direkt PRISM zu debuggen) glauben, ist zu prüfen, ob der Name der Seite, den Klassennamen übereinstimmt, oder den vollständigen Namen einschließlich Namespace. Da der Seitenname nicht mit dem Klassennamen übereinstimmt, kann er nicht gefunden werden und fügt dem Regionsmanager eine neue Instanz dieser Seite hinzu.

Zusammenfassend ist die beste Lösung, um sicherzustellen, dass Ihr Seitenname = Klassenname.

Verwandte Themen