GithubHelp home page GithubHelp logo

Comments (38)

dvorn avatar dvorn commented on May 22, 2024 1

@roy-t In my previous post I was not exactly precise. So, in order not to confuse anybody, little more details:

Grab IRegionNavigationJournal, rename it to IMyRegionNavigationJournal , and add modified RecordNavigation method.

Grab RegionNavigationJournal, rename it to MyRegionNavigationJournal, make it inherit from IMyRegionNavigationJournal and implement RecordNavigation.

Grab RegionNavigationService, rename it to MyRegionNavigationService, change the constructor argument type to IMyRegionNavigationJournal and implement functionality using a call to new RecordNavigation method on a new IMyRegionNavigationJournal interface.

In the application bootstrapper:

Override registration of IRegionNavigationService by registering MyRegionNavigationService.

Leave registration of IRegionNavigationJournal alone - it will not be used.

Add registration of IMyRegionNavigationJournal with implementation in MyRegionNavigationJournal.

from prism.

brianlagunas avatar brianlagunas commented on May 22, 2024 1

It only took 1.5 years, but we finally have this feature :)

from prism.

brianlagunas avatar brianlagunas commented on May 22, 2024

If you are using ViewModel first, then you are navigating with VMs and not views in the journal. If you are using View first then you are navigating to Views and dealing with Views in the NavigationJournal.

To answer your question, you would simply call RequestNavigate("SomeView") in your ItemViewModel. Then you will be navigated to the current instance of the "SomeView". If you want to navigate to a new instance of "SomeView", then you would return false in INavigationAware.IsNavigationTarget.

from prism.

brianlagunas avatar brianlagunas commented on May 22, 2024

Whoops, didn't mean to close, just meant to comment :0)

from prism.

roy-t avatar roy-t commented on May 22, 2024

I'm not sure I understand. What I mean is that in some case you would like to remove (not record) items from the navigation journal. Its not a matter of managing the life time of the underlying view models, but not showing screens that do not make sense in a GoBack() action.

For example, in my case, navigating back from ItemView to CreateItemView would mean the user being prompted to create another item, instead going back to the an over view of items. I cannot use requestNavigate("SomeView") from the ItemView because a user could've gotten there from multiple views.

(Hope this makes more sense :) ).

from prism.

brianlagunas avatar brianlagunas commented on May 22, 2024

Oh, I understand now, thanks for the clarification. We will think about how we can enable this scenario.

from prism.

roy-t avatar roy-t commented on May 22, 2024

Hey Brian,

I was wondering if you already have an idea how to enable this scenario? Can you maybe give me some pointers where I could best implement this? I'll share the code of course :)

from prism.

brianlagunas avatar brianlagunas commented on May 22, 2024

Unfortunately, we haven't had time to look into this.

from prism.

wldevries avatar wldevries commented on May 22, 2024

I have a workaround to get the behavior you want using navigation identifiers. Every time you navigate to a view you provide a Guid to identify that request. When the journal browses back to a view that uses these ids it will pass in the same Guid of the original request. The destination view will then be able to decide whether the journal should roll back one more or not.

Example code is listed below. There are some caveats though:

  • Every view that navigates to the view that needs to skip the journal needs to add a navigation id.
  • You need to make sure that you don't mix up the navigation id with the decision making in IsNavigationTarget.
  • Calling Journal.GoBack() directly in OnNavigatedTo will result in a StackOverflow, that is why I call in asynchronously using the Dispatcher.

Navigation example of passing a navigation id:

    var navParms = new NavigationParameters().AddNavigationId();        
    navigationService.RequestNavigation("Page", navParms);

OnNavigatedTo example of a view that should be skipped when the journal browses back:

    var navId = navigationContext.Parameters.GetNavigationId();
    if (this.prevNavId == Guid.Empty || this.prevNavId != navId)
    {
        this.prevNavId = navId;
        .. take action ..
    }
    else
    {
        Dispatcher.CurrentDispatcher.BeginInvoke(() => navigationService.Journal.GoBack());
    }

Helper extension methods:

    public static NavigationParameters AddNavigationId(this NavigationParameters navigationParameters, Guid? navigationId = null)
    {
        navigationParameters.Add(SharedNavigationParameters.NavigationId, navigationId.HasValue ? navigationId : Guid.NewGuid());
        return navigationParameters;
    }

    public static Guid GetNavigationId(this NavigationParameters navigationParameters)
    {
        var id = navigationParameters[SharedNavigationParameters.NavigationId];
        return (Guid)id;
    }

from prism.

roy-t avatar roy-t commented on May 22, 2024

Although that is not a completely satisfactory work-around, due to the caveats you mentioned, it will do the job. Would be great if we have some more control over the Journal so that we do not have to use this.

Thanks for the help!

from prism.

brianlagunas avatar brianlagunas commented on May 22, 2024

So I stared looking into this, and was thinking of the best way to accomplish this. I think simply adding some methods on the NavigationJournal would be the best, and least disruptive, approach. Something like:

Remove(string Uri, NavigationParameters parameters = null)
RemoveAt(int index)
RemoveLast()

Would that work?

Keep in mind that the navigation framework depends on the URI, so we aren't dealing with object instances here. We will look up the view to remove using either the URI, or a combination of the URI and matching parameter set. Even this approach will require rewriting how the NavigationJournal functions by replacing the Stack<T> variables with a different data type.

Thoughts?

from prism.

roy-t avatar roy-t commented on May 22, 2024

Hey Brian,

Sorry for getting back to you so much later.

I've been thinking about the implication of adding these methods to the navigation journal. They might solve the issue, but I';m not sure about the order of events.

I was thinking that a viewmodel should be able to remove itself from the journal in the OnNavigatedFrom method. But I'm not sure if the current view model is, at that moment, already present in the navigation journal.

The reason why I think it should be done there is because that is usually the last possible moment for something to change in the view model, so in most cases it will have all the information required to determine if it should be skipped when going back or not.

(In this case you would only need the RemoveLast() method so you would not need to rewite the backend of the NavigationJournal).

from prism.

brianlagunas avatar brianlagunas commented on May 22, 2024

Good idea. I am trying to get all the docs in order, and then I will start looking at larger features like this.

from prism.

brianlagunas avatar brianlagunas commented on May 22, 2024

Luckily for us there is one area in which a new journal entry is made:

https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Wpf/Regions/RegionNavigationService.cs#L269

Well, technically 3, but the other two are in the RegionNavigationJournal and they are exsiting entries, so as long as we prevent the entry from being made in the RegionNavigationService in the first place, it will never show up in the journal and we don't have to worry about it. So we have a couple of options that I can think of off the top of my head:

  • Use a hard coded navigation parameter that is checked. Is this intuitive, error prone?
  • Use an attribute. I don't like the idea of using reflection for every single navigation call.
  • Use an interface. This is faster than an attribute and more explicit.

On thing I am worried about implementing this feature is that is opens up the developer to be confused and application bugs. Let's say we navigated like this:

ViewA -> ViewB -> ViewC

Now ViewC is marked to not be included in the journal. Now in ViewC I call GoBack(). What view did I just navigate to? Well we will go to ViewA, because the current entry in the journal is ViewB since ViewC did not get added. I can see this being a problem.

What are your thoughts?

from prism.

brianlagunas avatar brianlagunas commented on May 22, 2024

Just thinking here, but I am wondering if adding an optional parameter on GoBack that would allow you to skip pages altogether. So basically all pages get added to thr backstack as expected, but I say GoBack(2) meaning go back 2 pages without ever actually navigating to the 1st one. So, given this scenario:

ViewA -> ViewB -> ViewC -> ViewD

I call GoBack(2), and then this is what my stack looks like now:

ViewA -> ViewB

But I never actually navigated ViewC, or maybe we do still navigate to VIewC, but you won't wind up there when the navigation is done. What do you think about that?

EDIT: actually, that's probably not a good idea. Might as well call GoBack twice to get the same result.

from prism.

roy-t avatar roy-t commented on May 22, 2024

Sorry for the late reply. I was under the weather.

How about always entering a view in the navigation journal before 'OnNavigatedTo' (I think this is roughly where it happens now). This means that GoBack() will work as expected (from C to B). Then optionally removing the view from the navigation stack after 'OnNavigatedFrom'. I like the idea of an interface for this. We already have 'INavigationAware' and 'IKeepAlive', etc... We could add 'IJournalAware' or something like that with 'bool PersistInHistory()'.

I don't think using GoBack(2) or calling GoBack() multiple times is a good idea. It puts the responsibility of knowing if a view should be skipped at the wrong view. For example.

A->B->C->D

Say C is an optional 'log in' form to access a secure part (D) of the application. After having logged in once you will be logged in for the entire session. C knows this. However D is just another part of the application. Having to do GoBack(2) in D requires D to know that this time the user just logged in.

from prism.

brianlagunas avatar brianlagunas commented on May 22, 2024

We can't add the entry before OnNavigatedTo in GoBack/GoForward because it relies on the navigation callback that is invoked after OnNavigatedTo in order to add an entry.

from prism.

AngusMcIntyre avatar AngusMcIntyre commented on May 22, 2024

@rot-t I'm also in favour of an IJournalAware interface that you describe. That was where I searched for this feature after discovering i needed it. However, the view is deliberately separated from the journal entry. This allow us to use IsNavigation target to change the handling view at any point. To coordinate the 'skip' flag between views I would end up crowbarring something into NavigationParameters. Is that appropriate? It feels good to me.

from prism.

dvorn avatar dvorn commented on May 22, 2024

@brianlagunas @roy-t @Gusdor @wldevries

Since I am currently deep involved in WPF region navigation, here is my take on this issue:

Define the interface (as suggested above)

public interface IJournalAware
{
    bool PersistInHistory();
}

which may be implemented by either View or View Model.

Modify the RegionNavigationJournal 's method RecordNavigation to take another parameter

void RecordNavigation(IRegionNavigationJournalEntry entry, bool savePrevious = true);

When savePrevious parameter is false, the method will set RegionNavigationJournal.CurrentEntry to entry, but will not push previous value of the CurrentEntry onto back stack.

The RegionNavigationService.RequestNavigate method will query the currently active view for implementation of IJournalAware (as it currently does for IConfirmNavigationRequest) and use the value obtained by PersistInHistory as a parameter to RecordNavigation.

The only issue here is that, generally speaking, there may be several currently active views. How to handle this?

If you like it and still need it I may prepare a PR.

from prism.

dvorn avatar dvorn commented on May 22, 2024

BTW, this can be done without modifications to the library: create custom implementations of IRegionNavigationService and IRegionNavigationJournal implementing the above and register them in the container instead of default ones.

from prism.

brianlagunas avatar brianlagunas commented on May 22, 2024

You can have a PR ready, but it will be awhile before this will be reviewed.

from prism.

dvorn avatar dvorn commented on May 22, 2024

@brianlagunas OK let's wait for the response from topic starter and other interested parties.

Callbacks or awaitables implementation or both?

from prism.

roy-t avatar roy-t commented on May 22, 2024

@dvorn the problem with creating your own journal is that you have to cast it everywhere in the application. (Since you get the journal from the OnNavigatedTo method you can't just inject IMySpecialJournal into the view model. It would have no history or the history of the entire application, not just the specified region).

However, the method you describe with changing RecordNavigation seems like a good idea. That would give me the exact behavior that I want :). I've never worked with multiple active views in one region so I don't have much to say about that.

from prism.

dvorn avatar dvorn commented on May 22, 2024

@roy-t Seemingly you misunderstood what I suggested.

RegionNavigationJournal is registered in the container by the default Prism bootstrapper: https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Unity.Wpf/UnityBootstrapper.cs#L155-156 . You need to grab the source code for RegionNavigationJournal and RegionNavigationService from the Prism, rename the classes and implement the functionality. Then you need to override the registration for IRegionNavigationJournal and IRegionNavigationService with your custom classes in your application's bootstrapper.

In your application code, other than bootstrapper, you do not have to deal with the journal directly other than implementing IJournalAware interface. The Prism itself will use your custom implementations.

from prism.

dvorn avatar dvorn commented on May 22, 2024

As for multiple active views, it may well happen that this feature is just incompatible with them. We need to consider use cases of multiple active views.

Generally speaking, there are the following approaches:

  • Query only the first active view
  • Query all views; drop the current entry if all views requested dropping.
  • Query all views; drop the current entry if any of the views requested dropping.

For a single active view instance those are all equivalent.

from prism.

dvorn avatar dvorn commented on May 22, 2024

I created a branch with initial attempt to implement the feature: https://github.com/dvorn/Prism/tree/PersistInHistory

@roy-t Do not expect to have this feature in Prism release very soon. We need to agree on what to do with multiple active regions, we need to write tests for the new feature, and also, more importantly, we need to decide how to maintain backward compatibility which is broken in this commit. I even won't create a PR.

But it should be sufficient for your needs if you use custom implementations as described above.

Please grab the source from the branch and let us know whether it works for you.

from prism.

dvorn avatar dvorn commented on May 22, 2024

@brianlagunas As I noticed above, changing the IRegionNavigationJournal's method RecordNavigation will be a breaking change. It will not break ordinary programs, but if somebody created a custom implementation of IRegionNavigationJournal, it will break. Is this acceptable? If not, I suggest the following approach:

Create new interface IRegionNavigationJournalEx which derives from IRegionNavigationJournal and adds an overload for RecordNavigation with additional parameter. Stock RegionNavigationJournal will implement IRegionNavigationJournalEx but it the default bootstrapper it will be registered for IRegionNavigationJournal interface.

RegionNavigationService will query the View for IJournalAware and, if discovered that the view do not want to be present in history, the service will query its instance of IRegionNavigationJournal for implementation of IRegionNavigationJournalEx and, if present, use is to drop the view from the history.

Now if somebody has a custom implementation of IRegionNavigationJournal it will work as expected - only the capability of dropping a view from the history will not be available. But the user's program does not expect this capability anyway. If they want to be able to drop the views, they will need to modify their implementation of IRegionNavigationJournal to support IRegionNavigationJournalEx (and, if they also have a custom implementation of IRegionNavigationService, modify it too).

What do you think?

from prism.

roy-t avatar roy-t commented on May 22, 2024

@dvorn ah I now understand your solution. That's actually pretty clever. Since we do not have multiple active views the issue for me is solved. Should I leave this ticket open as a general feature request to make this behavior more accessible?

from prism.

dvorn avatar dvorn commented on May 22, 2024

@roy-t Since there were several people with the interest in the feature I would leave it open until maintainers decide what to do.

from prism.

YounesCheikh avatar YounesCheikh commented on May 22, 2024

@dvorn I tried the solution you suggested but i have a lot of issues of mismatch usages between MyRegionNavigationJournal and usages of RegionNavigationJournal, Do i've to create a new IMyRegionNavigationService ? because this interface has a property IRegionNavigationJournal

from prism.

brianlagunas avatar brianlagunas commented on May 22, 2024

@dvorn I am ready to look at this for the upcoming v7.0 release. Is your branch still valid?

from prism.

brianlagunas avatar brianlagunas commented on May 22, 2024

Also, I think that we can say that this feature is not support for a multiple active view scenario. Honestly, I'm not sure opting out of the journal makes sense in that scenario.

I also think it is acceptable to introduce a slight break for anyone using a custom IRegionNavigationJournel. The interface will tell them how to fix the break. Also, I doubt there are many custom navigation journals out there.

from prism.

dvorn avatar dvorn commented on May 22, 2024

Yes it is still valid. Tests needs to be added (when I have time).

from prism.

AngusMcIntyre avatar AngusMcIntyre commented on May 22, 2024

Is there any additional documentation effort required?

from prism.

brianlagunas avatar brianlagunas commented on May 22, 2024

Yes, but it will be the last thing on my mind. It would be nice to have a section added to the current nav topic. Could you help us out?

from prism.

AngusMcIntyre avatar AngusMcIntyre commented on May 22, 2024

I'd love to. I'll need to familiarise myself with the change as I ignored this issue for a while.. Is there anything that needs words in addition to IJournalAware? I see references to some of the navigation service interfaces above but I can't imagine that most users will require that level of documentation.

from prism.

brianlagunas avatar brianlagunas commented on May 22, 2024

Nope, all you need to do is have the view or VM implement IJournalAware and return false for PersisInHistory and it will be removed from the backstack.

from prism.

lock avatar lock commented on May 22, 2024

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

from prism.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.