Prism TabRegion hinzufügen von mehrmals der selben View mit Parameterübergabe

  • WPF

Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von sronny.

    Prism TabRegion hinzufügen von mehrmals der selben View mit Parameterübergabe

    Einen schönen guten Abend in die Runde,

    nachdem ich hier mir sehr viele Anregungen holen konnte, wende ich mich wieder mal an Euch, auch, wenn es um C# und WPF / Prism und nicht um VBgeht.

    Ich verwende WPF und Prism 6.

    Ich habe in einem Modul in einer View ein TabControl mit dazugehörigem TabRegionAdapter. Funktionert.

    Jetzt möchte ich aus einem ViewModel heraus die dazugehörige View erneut in der TabRegion öffnen (als komplett neue, eigenständige View) - und Parameter übergeben.

    In der Modulklasse habe ich die View registriert:

    C#-Quellcode

    1. _container.RegisterType<ITabItemView,AddPersonView>("AddPersonView");
    .

    In dem entsprechenden Viewmodel kann ich die View einfach neu registrieren und dahin wechseln mit

    C#-Quellcode

    1. _regionManager.RegisterViewWithRegion("AdressenTabRegion",typeof(AddPersonView));


    Damit ist aber keine Parameterübergabe möglich.

    Dann habe ich folgendes gefunden:

    C#-Quellcode

    1. NavigationParameters param = new NavigationParameters();
    2. param.Add(...);
    3. (1) var addPersonView = _container.Resolve<AddPersonView>("AddPersonView", new ParameterOverride("Parasiten", param).OnType<AddPersonViewModel>());
    4. _regionManager.Regions["AdressenTabRegion"].Add(addPersonView);
    5. (2) _regionManager.RequestNavigate("AdressenTabRegion","AddPersonView", param);


    Zur Erklärung unter (1) kann ich mit new ParameterOverride eine Property aus dem dazugehörigen ViewModel vorbelegen (also könnte ich da die Parameter hinschieben und daraus abrufen). Unter (2) ganz normal mit
    RequestNavigate Parameter übergeben, welche in den INavigationAware - Methoden ausgewertet werden. Ich habe nun eben mit beiden probiert, daher stehen auch beide Varianten da, denn das Problem was ich habe ist:

    Übergebe ich in beiden Varianten die Parameter bekommt das ViewModel, aus welchem ich den Navigation-Request heraus aufrufe, die Parameter (und überschreibt mir meine Daten) und das neue ViewModel bleibt leer.

    Ich habe dann gedacht, biste clever und registriere die View zusätzlich wie folgt

    _container.RegisterType<ITabViewItem,AddPersonView>("AddKostentraeger") und rufe dann die View unter diesem Namen ab, aber das funktioniert auch nicht wie gewünscht.

    Nun wie gesagt meine Frage - wie kann ich diese View in der TabRegion neu öffnen und Parameter an das neue ViewModel übergeben? Oder soll ich eine neue Constructorinjektion hinzufügen und damit dann die Parameter übergeben?:

    C#-Quellcode

    1. _container.RegisterType<Object,Parameterklasse>(new ContainerLifeTimeManager());
    2. ...
    3. public ViewModel(Parameterklasse params ...)
    4. { if (params !=null) ...}


    Danke für hilfreiche Anregungen und ein hübsches Wochenende

    //Edit:

    Ist zwar nicht wirklich elegant, aber habe nun kurz vor dem Einschlagen mich für die ConstructorInjektion entschieden. Dazu habe ich eine neue Klasse erstellt

    C#-Quellcode

    1. public class Parameters
    2. {
    3. public NavigationParameters Parameters {get; set;}
    4. }


    diese habe ich registriert:

    C#-Quellcode

    1. _container.RegisterType<Parameters>(new ContainerLifeTimeManager());


    und dann im Anschluss als Übergabeparameter in den View - Constructor gehängt:

    C#-Quellcode

    1. public ViewModel(Parameters parameters ....)
    2. {
    3. if (parameters !=null)
    4. {
    5. if (parameters.parameters != null)
    6. {
    7. mache was mit den Parameters;
    8. parameters.parameters = null;
    9. }
    10. }


    und vor dem NavigationRequest fülle ich die Parameter...

    wie gesagt, nicht elegant. Aber jetzt müde, morgen kommt die Frau wieder und dann ist die Zeit zum spielen wieder vorbei...

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „sronny“ ()

    Du kannst einfach dein TabItem bei deinem Container registrieren

    C#-Quellcode

    1. _container.Register<object, ViewA>("ViewATabItem");

    dann kannst du einfach mit

    C#-Quellcode

    1. _regionManager.RequestNavigate("AdressenTabRegion", ViewATabItem);


    ein TabItem auf deinem TabControl erstellen bzw wenn er schon existiert zu diesem navigieren!

    Um dazu jetzt auch noch Parameter zu senden musst du in deinem ViewModel einfach das INavigationAware Interface implementieren.

    das Interface verlangt von dir das du 3 Methoden implementierst : OnNavigatedTo(NavigationContext navigationContext), public bool IsNavigationTarget(NavigationContext navigationContext) und OnNavigatedFrom(NavigationContext navigationContext)

    OnNavigatedTo
    Das hier wird ausgeführt wenn du das erste mal dein Tabitem erstellst, hier kannst du Parameter empfangen !

    IsNavigationTarget
    Hier kannst du entscheiden ob es multiple Instanzen dieser Klasse geben darf (return false = multiple Tabitems)
    Dateien

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Majomi“ ()

    Danke dir,

    die einzelnen Tabs sind eigentlich nur UserControls, welche ich im Unity-Container registriert habe. Mittels TabRegionAdapter werden die dann dem TabControl hinzugefügt.

    Wenn ich jetzt unter

    C#-Quellcode

    1. NavigationParameters param = new NavigationParameters;
    2. param.Add(...);
    3. _regionManager.RequestNavigate("AdressenTabRegion", ViewATabItem,[color=#FF0000]param[/color]);


    die Parameter übergebe, dann kommt es eben zu dem Phänomen, dass das aufrufende ViewModel die Parameter erhält und nicht das neue ViewModel. Warum auch immer ?(

    Habe die von dir genannten INavigationAware - Methoden schon eingebunden. Öffne ich die View das erste Mal, nimmt er auch brav die übergebenen Sachen, beim zweiten Öffnen dann das genannte Phänomen. Ich vermute, dass Prism noch nicht das neue ViewModel eingebunden hat und da es ja schon ein entsprechendes gibt, Prism denkt dieses kann ja die Sachen bekommen...
    Versuch mal das Projekt das ich angehängt habe zu verstehen, dort wird zum Beispiel gar kein TabRegionAdapter verwendet.

    Wie verbindest du View mit ViewModel? Wenn du den ViewModelLocator von prism verwendest sollte der sich eigentlich darum kümmern!

    desweiteren solltest du sicherstellen das du auch den UnityContainer benutzt -> das tut Prism meines wissens nach nicht von alleine!

    Du benötigst etwas wie ​ViewModelLocationProvider.SetDefaultViewModelFactory(type => Container.Resolve(type)); damit du dann auch wirklich deine ViewModels von deinem Container beziehen kannst, ansonsten versucht Prism einfach durch Activator.CreateInstance(type) eine Instanz zu bekommen.
    Hallo Majomi,

    danke noch einmal für deine Erläuterungen.

    Ich habe mir unter anderem deine Solution angeschaut. Gestern kam ich leider nicht mehr dazu.

    Dabei ist mir folgendes aufgefallen:

    erstens, du verwendest als IoC DryIoC und nicht UnityContainer. Welche Vorteile hat der denn gegenüber dem UnityContainer?
    Ich verwende derzeit den UnityContainer.

    Zweitens, bei mir hat das mit dem direkt einfügen in das TabControl ohne TabRegionAdapter nicht funktioniert - warum auch immer. Das werde ich noch einmal probieren. Funktioniert das von Haus aus?

    Drittens, beim Vergleich zwischen ViewA und ViewB fiel mir auf, dass der Unterschied bei ​NavigationTarget zu finden ist:

    In ViewA ist ​NavigationTarget return True und bei ViewB ​NavigationTarget = false - damit funktioniert es denn jetzt nämlich :)

    Ich bin bisher davon ausgegangen, dass NavigationTarget bestimmt, ob navigiert werden kann oder nicht- ähnlich wie CanExecute bei DelegateCommands... Habe auch nichts weiter darüber gefunden. Wie sich zeigt, bestimmt wohl das wohl, ob eine neue View geöffnet wird und die alte bleibt oder ob eine neue Instance davon erzeugt wird.

    Also damit funktioniert das jetzt auch, wie ich das möchte. Hoffe ich, habe es noch nicht im Detail getestet.

    Eine zweite Frage, die ich in ähnlichem Zusammenhang stellen möchte:

    Nutze Mahapps (Mahapps.Com bzw. github.com/MahApps/MahApps.Metro). Damit ist es möglich, im TabControl in den Tab-Headern einen Closebutton anzeigen zu lassen (​MetroTabItem. Da ich ja die Tabs nur indirekt über UserControls einbinde habe ich keine Einstellung für den Close-Button. Gibt es eine Möglichkeit den trotzdem anzeigen zu lassen und die Command-Property zu binden?

    sronny schrieb:

    Welche Vorteile hat der denn gegenüber dem UnityContainer?

    Ich experimentiere im Moment ein wenig mit verschiedenen IOC-Containern, im Moment ist halt DryIoc dran, Prism macht es auch sehr einfach den Container auszutauschen indem man einfach im Bootstrapper die Basisklasse von UnityBootstrapper zum Beispiel zu DryIocBootstrapper ändert, dann gibt es nur noch ganz kleine Anpassungen die man vornehmen muss z.B Container zu Kernel ersetzen.


    sronny schrieb:

    Funktioniert das von Haus aus?

    Sollte von Haus aus funktionieren, das ist genau der Einsatzbereich für den "benannte" Einträge im Container möglich sind.

    Zu dem close Problem lies dir mal stackoverflow.com/a/42654216 durch.

    // Edit:

    In meinem Beispiel hast du im MainWindow einen Style für die TabItems

    C#-Quellcode

    1. <Window.Resources>
    2. <Style TargetType="{x:Type metro:MetroTabItem}">
    3. <Setter Property="Header" Value="{Binding DataContext.Title}" />
    4. </Window.Resources>


    den kannst du einfach erweitern für Dinge die du brauchst:

    C#-Quellcode

    1. <Window.Resources>
    2. <Style TargetType="{x:Type metro:MetroTabItem}">
    3. <Setter Property="Header" Value="{Binding DataContext.Title}" />
    4. <Setter Property="CloseButtonEnabled" Value="{Binding DataContext.CloseButtonEnabled}" />
    5. <Setter Property="CloseTabCommand" Value="{Binding DataContext.CloseTabCommand}" />
    6. </Style>
    7. </Window.Resources>


    Jetzt kannst du im ViewModel einfach eine Property erstellen die CloseButtonEnabled benannt ist und vom Typ bool ist public bool CloseButtonEnabled => true; alternativ: public bool CloseButtonEnabled {get;set;} = true;

    Und noch ein DelegateCommand um das TabItem zu schließen ​public DelegateCommand<FrameworkElement> CloseTabCommand { get; }.
    In dem Command musst du dann die View aus der TabRegion löschen mit ​ _regionManager.Regions["AdressenTabRegion"].Remove(tabItem.DataContext);

    mfg

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Majomi“ ()