BindingContext not working for navigation in a button clicked event - xamarin

I am attempting to navigate to a registration page from a login page using a button_clicked event. A new view model is created during the event and set as the BindingContext. However it does not appear to work
When I debug on the registration page I have an ICommand in the registration page view model that is attached to a button. The code is
public ICommand RegisterAppUser
{
get
{
return new RelayCommand(RegisterUser, () => Email == ConfirmEmail && Password == ConfirmPassword);
}
}
private async void RegisterUser()
{
var userDto = new RegistrationUserDto(this);
App.User = await App.PlayerManager.RegisterUserAsync(userDto);
}
I assumed the the "this" would represent the view model attached to the view. However when I reach the RegistrationUserDto(this) line I can see that the "this" is an unintialized instance of the registration view model
public partial class LoginPage : ContentPage
{
public LoginPage()
{
InitializeComponent();
}
private void Button_Clicked(object sender, EventArgs e)
{
Navigation.PushModalAsync(new RegistrationView() { BindingContext = new RegistrationViewModel() }); ;
}
}
No compilation errors but the action clearly can't work with no data

Related

Is there a way to come back on last page in Navigation stack, and not execute OnApearing method in Xamarin?

So, I have Page1, with some input fields that my user already filled, and, there is a button that opens a new Page (let's call it Page2).
This is the code I used to go on Page2.
Application.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(new Page2()));
This is the code I used to came back on Page1.
private async void GoBackButton_Clicked(object sender, EventArgs e)
{
await Application.Current.MainPage.Navigation.PopModalAsync();
}
Now, I'd like to, somehow, when user finishes what he had on Page2, he presses the button, called GoBack, then he comes back on the Page1, and the OnApearing method of Page1's ViewModel is NOT getting executed. Is this doable?
Not sure if important, but I'm using VS22, on Windows10.
This is what I'm using in some of my apps. The logic is: Page1 loads data on the first load and then only reloads data when reload requested.
Here's an example:
Page1.xaml.cs
public partial class Page1 : ContentPage
{
private bool _isFirstLoad = true;
private bool _isReloadRequested;
public Page1()
{
InitializeComponent();
}
private async Task OpenPage2Async()
{
//navigate to Page2
await Application.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(new Page2(() => _isReloadRequested = true)));
}
protected override void OnAppearing()
{
if (_isFirstLoad)
{
_isFirstLoad = false;
ReloadData();
return;
}
if (_isReloadRequested)
{
ReloadData();
_isReloadRequested = false;
}
}
private void ReloadData()
{
//reload data...
}
}
Page2.xaml.cs
public partial class Page2 : ContentPage
{
private readonly Action _requestReload;
public Page2(Action requestReload)
{
InitializeComponent();
_requestReload = requestReload;
}
private async Task GoBackAsync()
{
//invoke callback to set Page1's _isReloadRequested to true
_requestReload?.Invoke();
//go back to Page1
await Application.Current.MainPage.Navigation.PopModalAsync();
}
}

Why does await _navigationService.NavigateAsync not work in OnNavigatedTo function

Been at this for days. Very simple logic.
Jump to Home page if bla = true skipping the login page.
I've stripped down the code to demo this.
Login button navigates as normal.
However Login page should actually be skipped as OnNavigatedTo has same NavigateAsync command.
Side affect which makes no sense is HomeViewModel code gets run when the OnNavigatedTo function runs.
public class LoginViewModel : AppMapViewModelBase
{
private readonly INavigationService _navigationService;
public DelegateCommand LoginCommand { get; private set; }
public LoginViewModel(INavigationService navigationService) : base(navigationService)
{
LoginCommand = new DelegateCommand(LoginUserAsync);
_navigationService = navigationService;
}
private async void LoginUserAsync()
{
//This works as expected
await _navigationService.NavigateAsync("/MasterDetail/NavigationPage/Home");
}
public override async void OnNavigatedTo(INavigationParameters parameters)
{
base.OnNavigatedTo(parameters);
//This Executes the code on Home but does not Jump there.
await _navigationService.NavigateAsync("/MasterDetail/NavigationPage/Home");
}
}
The HomeViewModel code which gets run somehow without ever going to the Home page.
public HomeViewModel(INavigationService navigationService) : base (navigationService)
{
Debug.WriteLine("............HomeViewModel Loaded.................................");
}
Changing the Main OnInitialized in App from
await NavigationService.NavigateAsync("NavigationPage/Login");
to
await NavigationService.NavigateAsync("/Login");
stops NavigateAsync("/..... from being passed through App-OnInitialized
No idea why but it works.

access and open DisplayActionSheet from view model

i have a toolbar in my content page where there is one item called add , on clicking over add i want to open DisplayActionSheet
i have created ContentPage Toolbar in xaml and attached ICommand to it in view model. Now DisplayActionSheet is accessible only in View hence i am not sure how will i able to access it and render it from view model.
xaml file
<ContentPage.ToolbarItems>
<ToolbarItem Name="" Icon="ic_add.png" Order="Primary" Priority="0" Command="{Binding OnAddContactCommand}"/>
<ToolbarItem Name="" Icon="ic_search.png" Order="Primary" Priority="1" Command="{Binding OnContactSearchCommand}" />
</ContentPage.ToolbarItems>
View model
public ICommand OnContactSearchCommand => new Command(OnContactSearch);
public ICommand OnAddContactCommand => new Command(OnAddContactSearch);
events
private async void OnAddContactSearch()
{
//var action = await DisplayActionSheet(AppResources.select_contact_source, AppResources.cancel, null, AppResources.manual, AppResources.phonebook);
}
private void OnContactSearch()
{
Debug.WriteLine("OnContactSearch");
}
Like #Alessandro said Application.Current.MainPage works fine for action sheets and alerts as well. To hide view specific stuff from view model I created an IMessageBoxService which is injected into the view models' contructors that need it. Note that I am using the Autofac IoC container. For Xamarin's DependencyService you have change the constructors and look up the service in code.
IMessageBoxService.cs
public interface IMessageBoxService
{
void ShowAlert(string title, string message, Action onClosed = null);
// ...
Task<string> ShowActionSheet(string title, string cancel, string destruction, string[] buttons = null);
}
MessageBoxService.cs
public class MessageBoxService : IMessageBoxService
{
private static Page CurrentMainPage { get { return Application.Current.MainPage; } }
public async void ShowAlert(string title, string message, Action onClosed = null)
{
await CurrentMainPage.DisplayAlert(title, message, TextResources.ButtonOK);
onClosed?.Invoke();
}
public async Task<string> ShowActionSheet(string title, string cancel, string destruction = null, string[] buttons = null)
{
var displayButtons = buttons ?? new string[] { };
var action = await CurrentMainPage.DisplayActionSheet(title, cancel, destruction, displayButtons);
return action;
}
}
AppSetup.cs
protected void RegisterDependencies(ContainerBuilder cb)
{
// ...
cb.RegisterType<MessageBoxService>().As<IMessageBoxService>().SingleInstance();
}
Usage
public class EditProductViewModel : AddProductViewModel
{
private IMessageBoxService _messageBoxService;
public ICommand DeleteCommand { get; set; }
public EditProductViewModel(IPageNavigator navigator, IMessenger messenger,
IMessageBoxService messageBoxService, TagDataStore tagDataStore) : base(navigator, messenger, tagDataStore)
{
_messageBoxService = messageBoxService;
DeleteCommand = new Command(DeleteItem);
}
...
private async void DeleteItem()
{
var action = await _messageBoxService.ShowActionSheet(TextResources.MenuTitleDeleteProduct,
TextResources.ButtonCancel, TextResources.ButtonDelete);
if (action == TextResources.ButtonDelete)
{ } // delete
If you are doing viewmodel first navigation (s. Xamarin or Jonathan Yates' blog) you may chose to make this part of the Navigator service. It's a matter of taste
try with
Application.Current.MainPage.DisplayActionSheet();

Navigate from Fragment to Activity MVVMCross

I am struggling to perform a navigation from a Fragment to an Activity at ViewModel level. I have an Activity with a DrawerLayout and this Activity has a FrameLayout to display different Fragments selected from the DrawerLayout. That navigation is perform by the ViewModel of this Activity and it is calling the ViewModel of each Fragment to display. In one of the Fragments I added a button binding a IMvxCommand method to perform the navigation from the Fragment to a new Activity and here is where I have the problem because when I click on the button nothing happens.
Find below my code.
ViewModel of Fragment
public class MainFrameViewModel : ContentViewModel
{
readonly IMvxNavigationService navigationService;
public MainFrameViewModel(IMvxNavigationService navigationService) : base(navigationService)
{
this.navigationService = navigationService;
}
public IMvxCommand GoMoreInfo
{
get
{
IMvxCommand navigateCommand = new MvxCommand(() => navigationService.Navigate<MoreInfoViewModel>());
return navigateCommand;
}
}
}
Fragment code
[MvxFragmentPresentation(typeof(ContentViewModel), Resource.Id.frameLayout)]
[Register("mvvmdemo.droid.views.fragments.MainFrameFragment")]
public class MainFrameFragment : MvxFragment<MainFrameViewModel>
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.Inflate(Resource.Layout.MainFrame, container, false);
}
}
Activity to navigate
[MvxActivityPresentation]
[Activity(Label = "MoreInfoActivity")]
public class MoreInfoActivity : MvxAppCompatActivity<MoreInfoViewModel>
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.MoreInfoLayout);
}
}
ContentViewModel is the ViewModel of the Activity containing the FrameLayout and the DrawerLayout.
Your bindings aren't working because you are using the default inflater, which knows nothing about MvvmCross bindings. You could solve this problem by using the MvvmCross inflater inside OnCreateView. Change return inflater.Inflate(Resource.Layout.MainFrame, container, false); call to return this.BindingInflate(Resource.Layout.MainFrame, null);
Also, you are ignoring the async part of the IMvxNavigationService. It would be an improvement to change from IMvxCommand to IMvxAsyncCommand and await or return the Task returned by IMvxNavigationService.Navigate()

Windows Phone Page navigation in MVVM

I am using MVVM in one of the app. I have created different project for Model, View and View Model.
I need to navigate to another XAML from ViewModel. I found some solution using MVVM light. Is there any way of implementing navigation from view model without using MVVM light.
Simple as that,
IF you want to navigate from page1 to page2,
private void MoveToPage2FromPage1()
{
NavigationService.Navigate(new Uri("/Page2.xaml", UriKind.Relative));
}
How to perform page navigation on Windows Phone 8
You can store current page url on a notify property of Shared ViewModel in App. After that, it is easy to catch the change of this url and navigate to the correct url by observing it.
public class AppViewModel : INotifyPropertyChanged
{
public string CurrentPageURL { get; set; }
private string _currentPageURL;
public string CurrentPageURL
{
get { return _currentPageURL;}
set
{
if (_currentPageURL==value)
return; // to prevent reload the same page.
_currentPageURL = value;
NotifyPropertyChangedCurrentPageURL
}
}
// INotifyPropertyChanged implementations
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
// Store in static singleton instance of AppViewModel
public class App : Application
{
private static Lazy<AppViewModel> _ViewModel=new Lazy<ViewModel>();
public static AppViewModel ViewModel { get { return _ViewModel.Value; } }
....
public App()
{
AppViewModel.PropertyChanged=(s,a) =>
{
if (a.PropertyName=="CurrentPageURL")
{
NavigationService.Navigate(new Uri(AppViewModel.CurrentPageURL, UriKind.Relative));
};
}
}
}
// Usage sample
public class Page1ViewModel
{
private btnMoveNextPage_Click(object s, EventHandler a) {
App.ViewModel.CurrentURL="~/Page2.xaml";
}
}

Resources