I need to show a viewmodel/view without the previous navigationstack etc. The szenario is a logout which redirects to a fresh loginview.
For this, I try to start a new activity with the ActivityFlags.ClearTask and ActivityFlags.NewTask to reset navigation etc. My viewpresenter, which inherits from MvxAppCompatViewPresenter, creates the Intent from a viewmodel-request, adds the needed activity flags and starts it.
The activity and the related viewmodel are created and navigated to, but the Initialize()-method on the viewmmodel is never called.
The project uses mvvmcross 5.4.2 currently.
Example code:
var intent = base.CreateIntentForRequest(request);
intent.SetFlags(ActivityFlags.ClearTask | ActivityFlags.NewTask);
base.ShowIntent(intent);
What am I doing wrong?
The Initialize method was only called when using the MvxNavigationServiceuntil MvvmCross 5.5. Either update to that so it is fixed, or use the MvxNavigationService.
More information is in the documentation.
https://www.mvvmcross.com/documentation/fundamentals/viewmodel-lifecycle#tombstoning-saving-and-restoring-the-viewmodels-state
Related
I have a script that does a while loop and does some requests
<p>Result #Content</p>
private string Content;
public async Task StartAsync()
{
while(!taskCanel) {
Content = await webSocket.SendAsync(..);
StateHasChanged();
await Task.Delay(15000);
}
}
but whenever I switch another page with my navbar then go back to this page it reset everything like it creates a new instance of my razor page.
How can I navigate to the page without losing all states and content?
While you are navigating to another page all variables from the current page should be disposed. It is the standard procedure. The state management works with a different approach. If you want to retain the state of the current page then you have to subscribe to the changes in a global object and pull the change onInit hook or onafterrender hook. There is many more library for state management. Some of them are introduced here
You need to separate your data and your presentation. Your data, how you get it and manage it belongs in a scoped DI service. Your UI accesses this DI Service through inject and displays information from the service.
I have a Xamarin android application that has a main fragment activity (named Home) which hosts a view pager and then several fragments in the view pager.
The application gets data from a bluetooth device that passes the data via an event handler once I instantiate the class.
BTScanner scanner = new BTScanner();
scanner.onScanData += BTScanner_onScanData;
The issue is, I need this event handler to be active on two of the fragments in my view pager, when that page is active. In other words, I want to receive the scan data if I am on fragment1 or fragment2, However, I can't have it go to both at the same time, there is processing of the data received and the processing is different based on which fragment you are receiving the scan data in.
What I have so far is I created a custom interface on the activity and implemented it in the fragments. It will allow me to call a method on the fragments when the view pager switches pages. Based on these methods, I can de-init the event handler on one page and init it on the other. Here is an example on the fragment activity:
private void ViewPager_PageSelected(object sender, ViewPager.PageSelectedEventArgs e)
{
int position = (int)e.Position;
ICustomFragmentLifecycleForPager fragmentToResume = (ICustomFragmentLifecycleForPager)adapter.InstantiateItem(viewPager, position);
fragmentToResume.onResumePagerFragment(previousActiveFragment);
ICustomFragmentLifecycleForPager fragmentToPause = (ICustomFragmentLifecycleForPager)adapter.InstantiateItem(viewPager, previousActiveFragment);
fragmentToPause.onPausePagerFragment();
previousActiveFragment = position;
}
Therefore, with this, I have it working as needed because when I implement the interface on the fragments, I get onPausePagerFragment called on the fragment I can de-init, and an onResumePagerFragment on the fragment I can init.
All that said, the issue I am having is actually on first startup. On first startup, I am setting the fragment to show first and when I do that, the
ViewPager_PageSelected
is not called initially, thus not calling my custom interface methods to init or de-init the scanner.
One thing I tried already is to put a method call in the onCreate of the activity (also tried it in onStartup and onResume) that would in theory only be called one time when the application starts and would then check which fragment is active at startup and force the interface methods to fire appropriately. However, this feels clunky and is not working properly. In addition to not working properly, I am also getting null exceptions in the fragment, when the interface method does fire because I force it to in onCreate of the activity, the method that is called by the interface on the fragment, onResumePagerFragment, returns a null for the activity here:
var activity = (Home)Activity;
This worked before and continues to work in other places in the code so I suspect that this is happening because the activity has not fully started yet before the interface calls the method on the fragment and it attempts to get a reference to the activity. Again, I tried doing this in onResume and onStart on the activity but I still get a null.
With all that said, what is the best way in general to handle a scenario like mine where I have the main activity which starts but then is nothing more than a fragment/view pager container and I need to have one instance to the event handler active on a fragment at a time? Should I even need to do an interface or rather use a static class? If I use a static class, I know how to get the instance of the class on each but how do you then create the event handler AND make sure to un-register is as well when another fragment grabs the instance?
Thanks!
Mike
I could not show you C# code, but below my answer could give you a hint.
https://stackoverflow.com/a/30554102/361100
Note that Android natively provides EventBus and Xamarin I found is MessageBus component alternatively.
Secondly, you can make Service so that the service is act as a delegator of all data communication whenever events are fired.
In conclusion, we could think Fragment does not gurantee to give same fragment when it restored and especially it is garbage-collected at any time when it's used with ViewPager.
I'm implementing a custom presenter in my Mvvmcross application. What I wanted to accomplish is: regular navigation and fragment navigation.
In my main activity, I managed to embed several fragment views based on this example: https://github.com/i486dx400/MultiRegionPresenter
while the fragments are working, I also wanted to show regular activities, that aren't hosted as a fragment. Therefor I extended this presenter as shown in this snippet: https://gist.github.com/JelleDamen/7003702
The problem/bug:
When I show this second activity, it gets shown. But when I go back to the previous view (which is the host) and re-open the same activity again, it doesn't get shown. The output log says: "mvx:Warning: Cannot Resolve current top activity"
What am I doing wrong, or what should I do, to inform the framework what activity is the current top activity?
Thanks in advance!
What is going wrong?
The line of trace you have provided is shown from:
protected virtual void Show(Intent intent)
{
var activity = Activity;
if (activity == null)
{
MvxTrace.Warning("Cannot Resolve current top activity");
return;
}
activity.StartActivity(intent);
}
in https://github.com/MvvmCross/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross.Droid/Views/MvxAndroidViewPresenter.cs
So it would appear that when Show is called, then there is no current MvvmCross Activity shown.
... and looking at https://github.com/i486dx400/MultiRegionPresenter/blob/master/RegionEx.Droid/MainActivity.cs it does appear this is true - the main activity in the app is not adapted for MvvmCross, but is instead just a normal FragmentActivity.
what should an app do to inform the framework what activity is the current top activity?
MvvmCross normally tracks the "top activity" by intercepting Activity lifecycle events - specifically the Activity created, started, restarted, resumed and destroyed events. These are shown in the lifecycle diagram in http://developer.android.com/reference/android/app/Activity.html
MvvmCross:
hooks into these events via the MvxActivityAdapter in https://github.com/MvvmCross/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross.Droid/Views/MvxActivityAdapter.cs
these hooks call the extension methods in https://github.com/MvvmCross/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross.Droid/Views/MvxActivityViewExtensions.cs
these extension methods inform the lifecycle monitor about the lifecycle changes - see https://github.com/MvvmCross/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross.Droid/Views/MvxAndroidLifeTimeMonitor.cs#L35 -
All the built-in MvvmCross Activity types - MvxActivity, MvxFragmentActivity, etc - call these "automatically". These adaptions can be extended to other Activity types using steps like those described in ActionBarSherlock with latest MVVMCross, or your app can manually call some of these hooks if it prefers.
Personal opinion: I think you'd be better off not following https://github.com/i486dx400/MultiRegionPresenter too closely. The code in OnCreate in https://github.com/i486dx400/MultiRegionPresenter/blob/master/RegionEx.Droid/MainActivity.cs seems to try to Start the app every time that MainActivity is created - which can, of course, happen multiple times during the lifecycle of each app.
Instead, read that sample and others like http://motzcod.es/post/60427389481/effective-navigation-in-xamarin-android-part-1, https://github.com/jamesmontemagno/Xam.NavDrawer/tree/master/Mvx and http://enginecore.blogspot.ro/2013/06/more-dynamic-android-fragments-with.html - then implement something that suits your navigation needs.
MEF+DirectoryCatalog+OnDemand
I have a code Initialize() in ModuleB:
this.regionManager.AddToRegion("TabRegion", new Views.Container());
this.regionManager.AddToRegion("TabRegion", new Views.Container());
and click event on the Shell:
modulemanager.LoadModule("ModuleB");
as a result I see 2 tabs in the shell tabcontrol
for me the problem is to call again:
this.regionManager.AddToRegion("TabRegion", new Views.Container());
How to do that?
many thanks
PS
I've tried to find solution in the web, but I guess that my question is not proper.
If this code is called only from ModuleB constructor (or from some nested method), then the only way for you is to create another instance of ModuleB. Here is article that may help you: Dynamic Part Instantiation in MEF.
But if you can make that method static, then you can call it later as meny times as you need.
Using the EventAggregator:
Create Infrastucture project with
PublishModuleAgainer:CompositePresentationEvent class
Point Importing constructor to Shell initializing
public Shell(IEventAggregator _eventaggregator);
Call in runtime
eventaggregator.GetEvent().Publish("STOCK0");
In Initialize() of module subscribe to event
eventaggregator.GetEvent().Subscribe(CreateView);
Just create Action CreateView and call add2region inside it:
this.regionManager.AddToRegion("TabRegion", new Views.Container());
Link to the same question and an answer Loading a prism module view from the shell, using MEF
This is specifically a Caliburn.Micro question I think, as it has to do with how CB handles navigation in windows phone 7.
I have a view that has the option of launching a phone number chooser. Once the result comes back I store it and navigate away, only the navigation wont work. I assume this is because the Handle method is working with the task and not my view. I know I can stick a button down the end of the page to navigate after the handle is finished but I would like this to happen once the result comes back.
This is what I am doing.
public void Handle(TaskCompleted<PhoneNumberResult> message)
{
webtext.Recipient = message.Result.PhoneNumber;
webtext.RecipientDisplayName = message.Result.DisplayName;
CommitWebtextToStorage();
events.Unsubscribe(this);
navigationService.UriFor<ComposeViewModel>();
}
Which wont work. I also can't call a method inside that as that would be the same as what I am doing. I need to let the handle method exit and then call the navigation service.
Actually, the navigation should look like:
navigationService.UriFor<ComposeViewModel>().Navigate();
(note the final Navigate method)
If it was just a typo in the question, I guess the issue could have to do with the timing of application resuming (which occurs when you return back to the application after the chooser task is completed).
In that case, could you please create an issue for this?