First of all, I'm not sure if I am even going about this the best way in terms of design, but I'm a little new to Android and very new to Xamarin.Forms. My setup is a series of ContentPages in a Xamarin.Forms PCL that all deal with information from a REST service. This service, upon logging in, grants a WorkToken that the application then holds onto in order to perform further operations. I'm storing this in an ISessionManager.
My problem is a workflow issue. I have a LoginPage that I want to present to the user if their session has ended/faulted/whatever; basically if they aren't logged in and try to do anything, I want to push this page on the stack and force them to attempt to log in and if they provide valid credentials let them continue where they left off. All this is taking place, for the most part, in the PCL, but I've created an Android implementation of the ISessionManager that is queried on each ContentPage's overridden OnAppearing method. Each page calls ISessionManager's CheckLogin which will do just that, and if they aren't logged in, it will create an Intent and start the LoginActivity through that. LoginActivity essentially just wraps around the PCL's LoginPage.
The problem I am running into is that, doing it this way, the LoginActivity is run asynchronously on top of whatever page called it. This calling page is in a broken state, however, because it has not initialized properly through the REST service yet. I imagine there are two possible solutions. Either run the Intent synchronously (which I don't think is possible) or rethink my design. Here's the Intent setup:
var intent = new Intent(_context, typeof(LoginActivity));
intent.AddFlags(ActivityFlags.NewTask);
_context.StartActivity(intent);
I've checked several Xamarin forums for related posts on this, but have not found much beyond basic navigation workflows in Xamarin.Forms.
Edit: I've found something analogous to what I'm trying to do here: How to pass variables from a new intent back to the class that created it in android, but within the context of Xamarin.Forms. The startActivityForResult would solve my problem if it existed in Forms.Context, but there doesn't appear to be an equivalent.
Why are you creating a LoginActivity? is it because you need to use something native from android?
My first thought it's that after you check for the token status on the OnAppearing, you that can decide to push or not the modal for the login page, after the user login is done and the token is save, you can pop the modal and it will show the previous page the user was.
Related
Problem
I recently replaced a System.Windows.Forms.WebBrowser-based control with one that relies on Microsoft.Web.WebView2.WinForms.WebView2 instead. This has been working well, except that my users want to have 2 instances of their application running at once, with both instances occasionally having that WebView2 form open at the same time. When they try this, one of the two instances fails to load the WebView2 control until the other one is closed.
Learned / attempted so far:
I've found WebView2 to be extremely finicky, so I've looked into alternatives like CefSharp, but haven't wanted to dedicate time to implementing them until I know they can solve the problem. So far as I can tell, CefSharp and WebView2 do very similar things as far as launching a Chromium-based browser, rendering to a WinForms control.
This blog post (read: advertisement) for paid alternative DotNetBrowser indicates that
You can initialize and use several Chromium engines simultaneously with different configurations [in DotNetBrowser], which is not possible in CefSharp.
I'm wondering if this same limitation applies to WebView2, and is the cause of my users' inability to have 2 instances of that same form loaded at once.
Context
I'm supporting an ancient desktop CRM that's set to be retired in favor of a modern alternative, but in the mean time, the old standard needs to integrate with new processes we're bringing in. One of those new processes is a web page from an external service that our call center users enter data into, then expect the CRM to pull entered data out of the web page.
The part of their process I did not know about is the 2-instance bit from above: they're used to being able to copy-and-paste from one embedded browser window to the other.
#Poul Bak in the comment on the question had it right: providing settings that differ per-instance of the application fixed the problem.
The setting I changed per-instance was the path of the user data folder: I was able to pass different values for the userDataFolder argument of the CoreWebView2Environment.CreateAsync method, branching based on a variable that holds different values between the instances that my users are running.
I am trying to implement a Xamarin storage provider as per the example here.
https://github.com/xamarin/monodroid-samples/tree/master/StorageProvider
The issue is that I want the user to log in before I render the root folders.
The method used to query the roots is the following:
public override ICursor QueryRoots(string[] projection)
This is called when the app first launches, but does not seem to be called subsequently.
If I log in (hard coded credentials) straight away, this works fine. However if I open the app so I can add some login details manually then this does not get called again and therefore does not re-render the correct roots.
Has anyone got any ideas how I force Android to re-call this method again to refresh this list?
You could try to call the below method to notify the system that the status of our roots has changed
Activity.ContentResolver.NotifyChange (DocumentsContract.BuildRootsUri (AUTHORITY), null, false);
you could see the explanation here https://github.com/xamarin/monodroid-samples/blob/0b301e8fd2da65ff442b5f1ed236c73ba3b963c2/StorageProvider/MyCloudFragment.cs#L66.
I am writing an Xamarin Forms application using MVVM Cross. I'm using the Master-Detail presentation model.
I'm attempting to wire up my application to support tombstoning. To do so, I configured my android device so that Don't Keep Activities = true. This has the correct desired affect in that when I switch to another application, my application activities are destroyed and when I switch back my application reloads.
However, the methods SaveStateToBundle are never called and subsequently, ReloadFromBundle is never called. So, I'm stuck on finding a way for my application to restore itself in the tombstoning workflow.
Has anyone had any success doing this? Complicating matters is that I not only want to remember the state of my detail page, but i also need to remember which detail page was actually loaded. But at this point I haven't found any way to save my state in the first place.
thanks to all who can help!
Michael
First of all, we are in C#, WPF, desktop application (can be .NET 3.5).
I need to show login window when application starts, if login succeeds, hide login window and show main app window. In addition, when the main window is closed, one should see login window again. If login window is closed, application quits.
Bonus to make it harder: when "Remember user" option checked, login window is skipped and user is logged in automatically, showing the main window instatly. (please do not think about how the option is stored, assume you just know it and have the value in variable).
What is meant to be the application startpoint (means which window is considered to be in app.xaml StartupUri property)?
How would you solve architecture? Is the best way to use App_Startup event and show some window from that point?
What type of App.ShutdownMode would you use? And how would that work? You can use "OneLastWindowClose", "OnMainWindowClose" or "OnExplicitShutdown". What is your choice and why?
In general, I am interested in "what calls what, what is application 'root', what invokes closing the application".
I already have a solution for my problem that works, but I am interested, if any of you have met this problem and how have you solved it? I will gladly share my approach, but I don't want to limit your ideas in the first place.
Thank you for your contribution.
P.S.: I am using MVVM framework, that makes it just a little more messy, but the point stays. I also use Ninject as IoC, but this issue still of course stays on. I did not mention these information to make the question as clear as possible. I also have to handle exceptions (communication with server or db during the login may fail), and to make it really interesting, I must show interactive 'splashwindow' indicating what application does (loading, communication with server, autologin). But please stay away from these requirements at the moment, we can discuss them later.
I'm going to repeat your questions as I understand them, you can correct me where I am missing something. Also, I'm rather new to the whole M-V-VM thing, so take my advice with a grain of salt. The best way I know how to learn is to throw out my ideas and have them corrected.
You want to know things
1) Where should the logic exist to decide where to show the login or main window
2) Which shutdown mode to use
3) What is application root
1)
I believe the decision to show a login or auto login is business logic, thus should live in the Model of your M-V-VM framework. Once that logic is implemented, the view can display whichever window is required by querying the Model.
I assume your application has an App.xaml and App.cs file which runs when the application is executed. You can override OnStartup within App.cs and display whichever window is necessary, based on the results of the business logic (which are learned from using some object in your Model).
2)
For shutdown model, I'd probably go with OnLastWindowClose, but I have no idea how many windows your application is using. I assume only the two you mentioned (login and main).
3)
What is application root? I would argue that your Model is really the application root, in that it holds all of the important stuff (view is user interface to the model, view model is state for the view). So, when it comes to what is the root or essence of your application, I would argue it is some set of objects in your model.
What calls what? That will all depend on what you're trying to accomplish. In general, I avoid having the view model know anything about the view. Both view and view model can be aware of the model. In certain cases, you'll want to make use of Ninject as a service manager in order to inject a view into the view model (without forcing the view model to depend on the view).
An entirely different approach would be to avoid a log in window altogether and display a login method in the main window. You'll still need some Model object to tell you when to show the login prompt, but you won't have to worry about juggling windows. Honestly, I'd probably go that route. WPF provides us with a lot of interesting and sexy ways to accomplish that task.
The Application object is the root of any WPF project. You are correct that you can handle the App_Startup event - depending on what you need to do at startup, of course.
Depending on whether you need a more complex navigation framework or the application will be forever limited to the scenario you described, you can go a multitude of ways from there.
What I'm doing in the applications I work on is I create a Navigator object that handles navigation both between different windows and inside windows, then I simply call Navigator.Navigate(new MyViewModel(), NavMode.ReplaceWindow); which closes the previous 'main' window (if any is open) and displays a new one, setting the contents to the instance of my ViewModel. The ViewModel is then displayed using the correct templates. I can also use NavMode.ReplaceUserControl which replaces the contents of the current 'main' window, or NavMode.OpenModalWindow which displays a modal dialog. My ShutdownMode is set to OnLastWindowClose, since I can always call App.Shutdown() if I want to close regardless of the open windows.
Of course, when you replace windows you need to make sure to instantiate (not show, just instantiate) the new window before closing the old one, to avoid application shutdown because you have no more windows.
You could do something similar, but it's just an idea and it's tailored specifically for my requirements.
I am (still) writing a small application which requires me to use several windows forms to show to the user.
Some of the forms just show progress messages while the application performs tests using several external devices.
The forms will usually be used in order (see below) but there may be some errors picked up from the devices, in which case an Error Reporting form will be used. The user will have the option to go back to the beginning or to the 2nd test (the 1st test takes 30 mins to perform). The error report can be invoked from any other form.
Also, the final form has the option to go back to the beginning to perform the tests on a new device.
Obviously this would cause the suite of forms to get rather tangled up. if it were used for several devices with errors etc.
So am I have a few questions.
Am I using the forms correctly and if so, how do I pass control from one form to the next one without having to go back to the original form, if that makes sense ?
Can I still have access to all variables created in preceding forms, or should I create all the variables in the initial form setup ?
Or should I have all the processing within one parent form and simply "show" the other forms as part of the procedure ?
I hope this doesn't sound too stupid, but I havent used multiple forms in C# yet. The steps are ALSO dependent on each other.
The following is the usual flowchart of forms, with PRGERREP being called from any form (more or less).
PRGSTART
PRGDEFAULT
PRGTEST1
PRGTEST2
PRGTEST3
PRGTEST4
PRGMANUAL
PRGFINALE
PRGERREP
Any help or advice would be most appreciated.
Please try to focus more on the question, not the context.
Even though i don't know exactly what you want, you should check out MDI Applications.
Basically you have a parent form with several child forms. Should your tests be finished you can BringToFront() the corresponding child window from the parent form.
I question the requirement to use multiple forms for this. Most applications show all information for a task; progress, tests, errors, messages, et al. in a single window, not multiple windows. Web browsers and office applications are very complex programs and manage to show all task information in a single window.
The fact that you are trying to "pass control" between multiple forms makes me suspect using multiple windows is a bad design. Look at the windows on your screen now; each one is a self-contained environment and does not need to "pass control" between each other.
If you are trying to make the user do something in sequence, handling errors before going on to the next step and allowing the user to "go back to the beginning", a wizard-like design may be better.
Create a single form with fields and buttons for the first task. Clicking a button redraws the form with fields and buttons for the next task, and buttons to go back or start over. This can be done more easily than creating a bunch of separate forms and trying to synchronize data between them.
Consider inheriting from an ApplicationContext to implement this logic in your application. See here and here.