Comments (38)
@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.
It only took 1.5 years, but we finally have this feature :)
from prism.
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.
Whoops, didn't mean to close, just meant to comment :0)
from prism.
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.
Oh, I understand now, thanks for the clarification. We will think about how we can enable this scenario.
from prism.
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.
Unfortunately, we haven't had time to look into this.
from prism.
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 inOnNavigatedTo
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.
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.
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.
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.
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.
Luckily for us there is one area in which a new journal entry is made:
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.
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.
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.
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.
@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.
@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.
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.
You can have a PR ready, but it will be awhile before this will be reviewed.
from prism.
@brianlagunas OK let's wait for the response from topic starter and other interested parties.
Callbacks or awaitables implementation or both?
from prism.
@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.
@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.
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.
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.
@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.
@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.
@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.
@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.
@dvorn I am ready to look at this for the upcoming v7.0 release. Is your branch still valid?
from prism.
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.
Yes it is still valid. Tests needs to be added (when I have time).
from prism.
Is there any additional documentation effort required?
from prism.
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.
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.
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.
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)
- [BUG] Prism.Navigation getParameters failed (empty parameters) HOT 1
- [BUG] Expected Navigation Failed. No Root Window has been created. HOT 1
- [BUG][MAUI] EventToCommandBehavior with RelativeSource binding throws InvalidOperationException HOT 4
- [BUG] Content of a ContentPage doesn't occupy full height when page is pushed to PrismNavigationPage HOT 4
- [BUG] When using iOS ModalPresentationStyles that can be automatically dismissed, navigation service gets out of sync & stops working with NavigationPage HOT 2
- [BUG] Unable to unregister the service using the Prism.Maui HOT 1
- No Root Window has been created HOT 1
- [Feature Request] Add support Keyed Service for IServiceCollection HOT 2
- Resolving with Unity works, not with DryIoc HOT 3
- [Enhancement] GetNavigationUriPath For Maui HOT 3
- [BUG] Android "hardware" back button closes the app when app contains a tabbed page with a navigation page as root page for one of the tabs HOT 1
- [BUG] Getting exception while navigating in Release mode on iOS HOT 2
- [BUG] [Android] Open app from url scheme crashes app HOT 3
- [BUG] Navigation Title view is not getting displayed on IOS HOT 2
- [BUG] Navigation Title view is not getting displayed with .net 8.0 on Andriord and IOS HOT 2
- [BUG] BindingContext cleared when performing absolute navigation HOT 2
- [Enhancement] NavigateFrom
- [Enhancement] Add ability to switch tabs and navigate
- [BUG] Android "hardware" back button closes the app when app contains a tabbed page with a navigation page as root page for one of the tabs, Take 2 HOT 1
- [BUG] Popup Dialogs are hidden/covered by calling Page when called after returning from MAUI FilePicker
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from prism.