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.
Related
I am trying to understand Event Tracing in Windows (ETW). What I want to do is capture which menu item was selected in Notepad. If I click Word Wrap, I want ETW to tell me this.
I have looked at the samples on GitHub. One of the Event Producers is the demo:
namespace TraceEventSamples
{
// In these demos, we generate events with System.Diagnostics.Tracing.EventSource and read them with ETWTraceEventSource
//
// Normally the EventSource and the ETWTraceEventSource would be indifferent processes, however, we
// don't do this here to make the scenario really easy to run. The code works in the multi-process case, however.
namespace Producer
{
[EventSource(Name = "Microsoft-Demos-SimpleMonitor")] // This is the name of my eventSource outside my program.
class MyEventSource : EventSource
{
// Notice that the bodies of the events follow a pattern: WriteEvent(ID, <args>) where
// ID is a unique ID starting at 1 and incrementing for each new event method. and
// <args> is every argument for the method.
// WriteEvent then takes care of all the details of actually writing out the values complete
// with the name of the event (method name) as well as the names and types of all the parameters.
public void MyFirstEvent(string MyName, int MyId) { WriteEvent(1, MyName, MyId); }
public void MySecondEvent(int MyId) { WriteEvent(2, MyId); }
public void Stop() { WriteEvent(3); }
// Typically you only create one EventSource and use it throughout your program. Thus a static field makes sense.
public static MyEventSource Log = new MyEventSource();
// You don't need to define this override, but it does show you when your eventSource gets commands, which
// is helpful for debugging (you know that your EventSource got the command.
protected override void OnEventCommand(EventCommandEventArgs command)
{
EventGenerator.Out.WriteLine("EventSource Gets command {0}", command.Command);
}
// We could add Keyword definitions so that you could turn on some events but not others
// but we don't do this here to keep it simple. Thus you either turn on all events or none.
}
This is creating events for the demo, but how can I wire this to Notepad instead? Can ETW be used for this type of logging, knowing what someone clicked on a menu of an application?
I have looked at various SO questions, but they did not help. One generic was, Is there a Microsoft (built-in) ETW Provider for tracing ETW lifecycle events?
I considered this because it does not require C#, C++ Event Tracing for Windows (ETW) wrapper but didn't understand how it helped me.
This makes me think I cannot do it with managed code if I didn't write the original program:
https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.tracing.eventsource?redirectedfrom=MSDN&view=netcore-3.1
What I am attempting to do is capture which menu item is selected, and take action with my program that caught the event. The reason for this is an old VB6 program. I cannot do what I need to do, so my last resort is to capture the menu item and get my application to do what I want. I wanted to start with a simple notepad.exe.
VB6 user clicks "View X." My C# application does "View Y".
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
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.
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?
I developed a C# class library, some of their methods shows information of its processing progress because they read and write millions of records, and the user asked for knowing how the process is going and the time they should wait.
Using dependency injection to avoid the "if console app write progress on console else if WPF app display progress bar", (1) I have got the displaying on the console the time for every one million records processed if the method is invoked from a console application and (2) I have got the displaying a progress bar on a GUI if the method is invoked from a WPF application.
The question here is, is it a good practice what I am doing or, is there better/correct alternative to this matter?
My best regards.
Please don't do this. If you are building a class library, you should make zero assumptions about the UI is interacting with the user.
Your solution sounds like it might work if you have a console window or a WPF application, but what if it's being called from a website or inside a service? I've seen many a service get brought down beause some rogue class library was trying to display a dialog but there was nobody around to click OK.
The better solution is to simply raise an event whenever you want to report some progress, and let the consuming UI application worry how it wants to display that progress to the user.
See how the BackgroundWorker class works for a good model of this: http://msdn.microsoft.com/en-us/library/8xs8549b.aspx
I wouldn't expect a class library to display the progress itself. I'd expect it provide hooks - probably in the form of events - so that whatever using the class library can display that information in the most appropriate form.
Quite how much control you want to give over that (e.g. report to me on every item or every N items) is a matter you'll have to work out for yourself - but it should be fairly easy for a handler to work that sort of thing out for itself.
Here's an example of raising events, this code will go in your class that is doing the work on the background thread. The MessageEventsArgs derives from EventArgs (MessageEventArgs : EventArgs) so custom information can be passed to the caller. This isn't required, one could use EventArgs e as well.
public delegate void SchemaProcessorMessageEventHandler(object sender, MessageEventArgs e);
public event SchemaProcessorMessageEventHandler SchemaProcessorMessage;
protected virtual void OnSchemaProcessorMessage(MessageEventArgs e)
{
if (SchemaProcessorMessage != null)
{
SchemaProcessorMessage(this, e);
}
}
Now in your caller (UI) set up the event listener. Remove the event listener -= when finished.
_SchemaProcessor = new ServerSchemaUtilityFramework.SchemaProcessor();
_SchemaProcessor.SchemaProcessorMessage += new ServerSchemaUtilityFramework.SchemaProcessor.SchemaProcessorMessageEventHandler(sp_SchemaProcessorMessage);
void sp_SchemaProcessorMessage(object sender, ServerSchemaUtilityFramework.MessageEventArgs e)
{
//Update the UI, if on background will need to (!this.Dispatcher.CheckAccess())
}