I have 2 classes and want to send object using Messenger while navigating from page to another and it works but only when navigate to the page and come back then try again not from first try.
ManivViewModel code:
public void GoToDetial(object parameter)
{
try
{
var arg = parameter as ItemClickEventArgs;
var item = arg.ClickedItem as Item;
Messenger.Default.Send<Item>(item, "Mess");
_navigationService.Navigate(typeof(DescriptionPage));
}
catch { }
}
DescriptionViewModel code:
public DescriptionViewModel(IRSSDataService rssService, INavigationService navigationService, IDialogService dialogService)
{
_dataService = rssService;
_navigationService = navigationService;
_dialogService = dialogService;
load();
LoadCommand = new RelayCommand(load);
GoToUrlCommand = new RelayCommand<object>(GoToUrl);
ShareSocialCommand = new RelayCommand(ShareSocial);
}
private void load()
{
Messenger.Default.Register<Item>(
this,
"Mess",
selectedItem =>
{
Item = selectedItem;
// Prepare content to share
RegisterForShare();
GetFromHTML(Item.Link);
});
}
I found it. I just need to pass in "true" to the Register call in the SimpleIoc to create the instance of the DescriptionViewModel immediately like this
SimpleIoc.Default.Register<DescriptionViewModel>(true);
Related
I'm currently learning F# by re-doing a simple mobile application I did in C# and Xamarin.forms which has forgoal to connect a user with facebook and get his profile and posts.
I almost finish everything but I'm blocked. To do my connection to the facebook API in C#, I used the Xamarin.Auth library and I want to reuse this library in F#.
Here is my code for my LoginPage ViewModel in C#:
public class LoginPageViewModel : BaseViewModel
{
private readonly INavigationService _navigationService;
private readonly IConfiguration _config;
private LoginLogic _loginLogic;
public ICommand NavigateCommand { get; set; }
public OAuth2Authenticator MyAuthenticator;
public ICommand ConnectVerification { get; set; }
public bool CanSkipPage { get; set; }
public LoginPageViewModel(INavigationService navigationService, IConfiguration configuration)
{
if (navigationService == null) throw new ArgumentNullException("navigationService");
_navigationService = navigationService;
if (configuration == null) throw new ArgumentNullException("Configuration");
_config = configuration;
NavigateCommand = new RelayCommand(() => { _navigationService.NavigateTo(Locator.FacebookProfilePage); });
MyAuthenticator = new OAuth2Authenticator(
_config.facebookAppId,
_config.scope,
new Uri(_config.facebookAuthUrl),
new Uri(_config.facebookRedirectUrl),
null);
MyAuthenticator.Completed += OnAuthenticationCompleted;
MyAuthenticator.Error += OnAuthenticationFailed;
_loginLogic = SimpleIoc.Default.GetInstance<LoginLogic>();
this.ConnectVerification = new AsyncCommand(() => TokenVerification());
}
public async Task TokenVerification()
{
IsLoading = true;
if (await _loginLogic.CheckToken())
NavigateCommand.Execute(null);
IsLoading = false;
}
async void OnAuthenticationCompleted(object sender, AuthenticatorCompletedEventArgs e)
{
IsLoading = true;
var authenticator = sender as OAuth2Authenticator;
if (authenticator != null)
{
authenticator.Completed -= OnAuthenticationCompleted;
authenticator.Error -= OnAuthenticationFailed;
}
await _loginLogic.SetTokenAsync(e.Account.Properties["access_token"]);
loginLogic.SetTokenAsync(e.Account.Properties["access_token"]);
NavigateCommand.Execute(null);
IsLoading = false;
}
void OnAuthenticationFailed(object sender, AuthenticatorErrorEventArgs e)
{
var authenticator = sender as OAuth2Authenticator;
if (authenticator != null)
{
authenticator.Completed -= OnAuthenticationCompleted;
authenticator.Error -= OnAuthenticationFailed;
}
}
}
My problem is to use Xamarin.Auth . I have to create a OAuth2Authenticator property that I initialize in the constructor of my class and then subscribe both EventHandler .Complete and .Error of this property to the two events OnAuthenticationCompleted and OnAuthenticationFailed in my class constructor and I've no idea how to do that in F#.
Right now, my F# class looks like this :
open Xamarin.Auth
open System
open GalaSoft.MvvmLight.Views
type LoginPageViewModel(navigationService: INavigationService) =
inherit ViewModelBase()
let mutable isLoading = false
let authenticator = new OAuth2Authenticator(config.facebookAppId,
config.scope,
new Uri(config.facebookAuthUrl),
new Uri(config.facebookRedirectUrl),
null)
member this.MyAuthenticator
with get() = authenticator
member this.IsLoading
with get() = isLoading
and set(value) =
isLoading <- value
base.NotifyPropertyChanged(<# this.IsLoading #>)
member this.TokenVerification() =
this.IsLoading <- true
if loginLogic.CheckToken()
then
navigationService.NavigateTo("FacebookProfilePage")
this.IsLoading <- false
But I don't know :
First, Where I should create my two methods OnAuthenticationCompleted and OnAuthenticationFailed, are they supposed to be methods of the class or not ?
Second, How To subscribe my OAuth2Authenticator.Complete to OnAuthenticationCompleted and OAuth2Authenticator.Error to OnAuthenticationFailed methods in my class constructor
You can add even handlers to your authenticator object using the following syntax:
let auth = OAuth2Authenticator("clientId", "scope", Uri("??"), Uri("??"))
auth.Error.Add(fun err ->
printfn "Error: %A" err)
auth.Completed.Add(fun res ->
let at = res.Account.Properties.["access_token"]
printfn "%A" at)
If you want to be able to add and remove the handler, then you need to create an explicit EventHandler value first:
let auth = OAuth2Authenticator("clientId", "scope", Uri("??"), Uri("??"))
let handler = EventHandler<AuthenticatorCompletedEventArgs>(fun _ res ->
let at = res.Account.Properties.["access_token"]
printfn "%A" at)
auth.Completed.AddHandler(handler)
auth.Completed.RemoveHandler(handler)
That said, if you simply translate the C# code to F#, then you probably won't gain much in this case. Your logic is quite imperative and things like the mutable isLoading field and adding/removing of event handlers is going to make your F# code quite ugly. If you want to develop mobile applications with F#, then I would instead recommend looking at Fabulous, which lets you write nice functional code.
I am trying to write a xamarin app that will display a login page before a master detail page but I am running into issues.
Right now I have my app.xaml calling an appbootstrapper as follows:
public App()
{
this.InitializeComponent();
RxApp.SuspensionHost.CreateNewAppState = () => new AppBootstrapper();
RxApp.SuspensionHost.SetupDefaultSuspendResume();
this.MainPage = RxApp.SuspensionHost
.GetAppState<AppBootstrapper>()
.CreateMainPage();
}
/// <summary>Gets the Router associated with this Screen.</summary>
public RoutingState Router { get; } = new RoutingState();
With the app bootstrapper as follows:
public class AppBootstrapper : ReactiveObject, IScreen
{
public AppBootstrapper(IMutableDependencyResolver dependencyResolver = null)
{
SetupLogging();
this.RegisterParts(dependencyResolver ?? Locator.CurrentMutable);
this.Router.Navigate.Execute(new LoginPageViewModel(this));
}
/// <summary>Gets the Router associated with this Screen.</summary>
public RoutingState Router { get; } = new RoutingState();
public Page CreateMainPage()
{
return new RoutedViewHost();
}
private static void SetupLogging()
{
var logger = new Logger { Level = LogLevel.Debug };
Locator.CurrentMutable.RegisterConstant(logger, typeof(ILogger));
}
private void RegisterParts(IMutableDependencyResolver dependencyResolver)
{
dependencyResolver.RegisterConstant(this, typeof(IScreen));
dependencyResolver.Register(() => new LoginPage(), typeof(IViewFor<LoginPageViewModel>));
dependencyResolver.RegisterConstant(new LoginService(), typeof(ILoginService));
}
}
This gets me to my login screen no problem, and I can perform my login operation. Then, once login is successful, I try to navigate to the master detail page, but this is where I run into issues.
public LoginPageViewModel(IScreen screen)
{
this.loginService = Locator.Current.GetService<ILoginService>();
this.HostScreen = screen ?? Locator.Current.GetService<IScreen>();
this.PrepareObservables();
}
........................................................
private void PrepareObservables()
{
...
this.LoginCommand = ReactiveCommand.CreateFromTask(
async execute =>
{
var loginSuccessful = await this.loginService.Login(this.Username, this.Password);
if (loginSuccessful)
{
this.HostScreen.Router.NavigateBack.Execute().Subscribe();
}
}, canExecuteLogin);
...
You can see that my login command is trying to perform a navigate and reset to go to the Main Page (which is my master detail page). This is not working and is resulting in an unhandled exception stating:
An object implementing IHandleObservableErrors has errored, thereby breaking its observable pipeline. To prevent this, ...>
Does anyone know what to do here? I need a good pattern for handling the use case of Login -> Master Detail Page in Xamarin Forms using ReactiveUI. Thanks.
this.LoginCommand = ReactiveCommand.CreateFromTask(
async execute =>
{
var loginSuccessful = await this.loginService.Login(this.Username, this.Password);
if (loginSuccessful)
{
this.HostScreen.Router.NavigateBack.Execute().Subscribe();
}
}, canExecuteLogin);
The above code is navigating back on successful login. I think you mean to use Router.NavigateAndReset.Execute(new MainPageViewModel()).Subscribe();
I'm using VS 17 for Xamarin Forms. I've set up Prism in my Xamarin.Forms app and I just added a reference to my Api interface (in ViewModel Constructor) and it makes the app stop navigation to the second page. I need to do this in order to pass parameters etc. I followed this guide:
https://blog.qmatteoq.com/prism-for-xamarin-forms-basic-navigation-and-dependency-injection-part-2/
This is what I did to make the navigation stop working:
private readonly IService _Service;
private ObservableCollection<TodoItem> _topSeries;
public ObservableCollection<TodoItem> TopSeries
{
get { return _topSeries; }
set { SetProperty(ref _topSeries, value); }
}
This is the constructor:
public SecondPageViewModel(IService Service, INavigationService navigationService)
{
_Service = Service;
_navigationService = navigationService;
}
So I cant even reach the above viewmodel because of the above code that I added. I tried to put break points on the DelegateCommand (on first ViewModel) but it just stops after InitializeComponent(); and then nothing happens. No error messages! Thanks!
Update:
My Service class that fetches data:
public class Service : IService
{
public List<TodoItem> TodoList { get; private set; }
HttpClient client;
Service()
{
client = new HttpClient();
client.MaxResponseContentBufferSize = 256000;
}
public async Task<List<TodoItem>> DataAsync()
{
TodoList = new List<TodoItem>();
var uri = new Uri(string.Format(Constants.RestUrl, string.Empty));
try
{
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
TodoList = JsonConvert.DeserializeObject<List<TodoItem>>(content);
Debug.WriteLine(content);
}
}
catch (Exception ex)
{
Debug.WriteLine(#"ERROR {0}", ex.Message);
}
return TodoList;
}
}
This is my App.Xaml.cs
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<View.MainPage, MainPageViewModel>();
containerRegistry.RegisterForNavigation<View.SecondPage, SecondPageViewModel>();
containerRegistry.Register<IService, Service>();
}
My Interface:
public interface IService
{
Task<List<TodoItem>> DataAsync();
}
This is how I navigate (click from listview):
private EventItem _selectedEvent { get; set; }
public EventItem SelectedEvent
{
get { return _selectedEvent; }
set
{
if (_selectedEvent != value)
{
if (Device.RuntimePlatform == Device.iOS)
{
_selectedEvent = null;
}
else
{
_selectedEvent = value;
}
NavigationParameters navParams = new NavigationParameters();
navParams.Add("PassedValue", _todoItem.name);
_navigationService.NavigateAsync("SecondPage", navParams);
}
}
}
Edit:
When I debug without the ApiService code the command is taking me to new new constructor in the new viewmodel. With the code it does not reach the contructor.
According to your code you have declared constructor like this:
Service()
{
// ...
}
You didn't set access modifier, therefore the default one is internal. Here is the definition:
Internal types or members are accessible only within files in the same
assembly.
Most likely you have your Service.cs declared in another Assembly and Prism can't access its constructor.
Your navigation doesn't work because dependency injection fails. To fix it, just change your access modifier to public:
public Service()
{
// ...
}
We are trying to use Touch Id with iOS using our Xamarin Forms application.
In our Xamarin Forms Application, in the App.Xaml.cs constructor we are using an interface to reference the iOS native touch id implementation:
bool _authenticatedWithTouchID = DependencyService.Get<ITouchID>().AuthenticateUserIDWithTouchID();
if (_authenticatedWithTouchID)
{
MainPage = new NavigationPage(new MainPage());
}
else
{
MainPage = new NavigationPage(new LoginPage());
}
This is the interface signature within Forms Application:
public interface ITouchID
{
bool AuthenticateUserIDWithTouchID();
}
This is the implementation of the interface within the iOS project:
[assembly: Dependency(typeof(TouchID))]
namespace GetIn.iOS
{
public class TouchID : ITouchID
{
public bool AuthenticateUserIDWithTouchID()
{
bool outcome = false;
var context = new LAContext();
if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out NSError AuthError))
{
var replyHandler = new LAContextReplyHandler((success, error) => {
Device.BeginInvokeOnMainThread(() => {
if (success)
{
outcome = true;
}
else
{
outcome = false;
}
});
});
context.EvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, "Logging with touch ID", replyHandler);
};
return outcome;
}
}
}
We get a response from the outcome variable (which is true if user is authenticated) but that is not being passed back to the forms application.
We have also tried using async tasks with no luck.
Is there a recommended way we do this? We are trying to keep this as simple as possible.
You need to change your code to handle asynchronous behavior.
public class TouchID : ITouchID
{
public Task<bool> AuthenticateUserIDWithTouchID()
{
var taskSource = new TaskCompletionSource<bool>();
var context = new LAContext();
if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out NSError AuthError))
{
var replyHandler = new LAContextReplyHandler((success, error) => {
taskSource.SetResult(success);
});
context.EvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, "Logging with touch ID", replyHandler);
};
return taskSource.Task;
}
}
Remember add the using on top
using System.Threading.Tasks;
And change your interface declaration
public interface ITouchID
{
Task<bool> AuthenticateUserIDWithTouchID();
}
And finally your Xamarin.Forms code...
var touchId = DependencyService.Get<ITouchID>();
var _authenticatedWithTouchID = await touchId.AuthenticateUserIDWithTouchID();
if (_authenticatedWithTouchID)
{
MainPage = new NavigationPage(new MainPage());
}
else
{
MainPage = new NavigationPage(new LoginPage());
}
Managed to get this working by using the async changes above (although you can do this without using the async method), and then doing the following:
By moving and adapting the following code from the app.xaml.cs to our MainPage.xaml.cs (our tabbed page) constructor.
var touchId = DependencyService.Get<ITouchID>();
var _authenticatedWithTouchID = await
touchId.AuthenticateUserIDWithTouchID();
if (_authenticatedWithTouchID)
{
//Do Nothing as on this page
}
else
{
//Go back to login page
Navigation.InsertPageBefore(new LoginPage(), this);
await Navigation.PopAsync();
}
I do requests to Github Api so I have async methods, these do this job. Before it, I always called they in method, that calls from command(actually DelegateCommand). But now I wanna do request in ViewModel because I need to display list on page. I am using Prism to wire view and viewmodel.
Because I can't make viewmodel async, I can't use await word, so I tried to do something like gets result from task, or task.wait. But with this I have the same result. My app stop works with white display when it did request. I read some info about that and I understood that call async method in not async method is bad, and it causes deadlock, but I don't know what to do with this. And I think deadlock causes that app stop works.
Here is method where app die:
public async Task<IEnumerable<RepositoryModel>> GetRepositoriesAsync()
{
try
{
var reposRequest = new RepositoryRequest { Sort = RepositorySort.FullName };
var gitHubRepos = await _gitHubClient.Repository.GetAllForCurrent(reposRequest); //async request, don't say about name convention, it is not my method.
var gitRemoteRepos = new List<RepositoryModel>();
foreach ( var repository in gitHubRepos )
{
var repos = new RepositoryModel();
repos.RepositoryTypeIcon = GetRepositoryTypeIcon(repository);
gitRemoteRepos.Add(repos);
}
return gitRemoteRepos;
}
catch ( WebException ex )
{
throw new Exception("Something wrong with internet connection, try to On Internet " + ex.Message);
}
catch ( Exception ex )
{
throw new Exception("Getting repos from github failed! " + ex.Message);
}
}
And here is viewmodel:
public class RepositoriesPageViewModel : BindableBase
{
private INavigationService _navigationService;
private readonly Session _session;
public ObservableCollection<RepositoryModel> Repositories { get; }
private readonly RepositoriesManager _repositoriesManager;
public RepositoriesPageViewModel(INavigationService navigationService, ISecuredDataProvider securedDataProvider)
{
_navigationService = navigationService;
var token = securedDataProvider.Retreive(ConstantsService.ProviderName, UserManager.GetLastUser());
_session = new Session(UserManager.GetLastUser(), token.Properties.First().Value);
var navigationParameters = new NavigationParameters { { "Session", _session } };
_repositoriesManager = new RepositoriesManager(_session);
var task = _repositoriesManager.GetRepositoriesAsync();
//task.Wait();
Repositories = task.Result as ObservableCollection<RepositoryModel>;
}
}
I recommend using my NotifyTask<T> type, which provides a data-bindable wrapper around Task<T>. I explain this pattern more completely in my article on async MVVM data binding.
public class RepositoriesPageViewModel : BindableBase
{
private INavigationService _navigationService;
private readonly Session _session;
public NotifyTask<ObservableCollection<RepositoryModel>> Repositories { get; }
private readonly RepositoriesManager _repositoriesManager;
public RepositoriesPageViewModel(INavigationService navigationService, ISecuredDataProvider securedDataProvider)
{
_navigationService = navigationService;
var token = securedDataProvider.Retreive(ConstantsService.ProviderName, UserManager.GetLastUser());
_session = new Session(UserManager.GetLastUser(), token.Properties.First().Value);
var navigationParameters = new NavigationParameters { { "Session", _session } };
_repositoriesManager = new RepositoriesManager(_session);
Repositories = NotifyTask.Create(GetRepositoriesAsync());
}
}
private async Task<ObservableCollection<RepositoryModel>> GetRepositoriesAsync()
{
return new ObservableCollection<RepositoryModel>(await _repositoriesManager.GetRepositoriesAsync());
}
Note that with this approach, your data binding would use Repositories.Result to access the actual collection. Other properties are also available, most notably Repositories.IsCompleted and Respositories.IsNotCompleted for showing/hiding busy spinners.