I have several ContentPages and I want to navigate from one to another at the click of an element in the page. I have my ViewModel class:
class JumpVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private INavigation _navigation;
public ICommand NewPage
{
get
{
return new Command(async () =>
{
await _navigation.PushAsync(new MySettingsPage());
});
}
}
public JumpVM() { }
public JumpVM(INavigation navitation)
{
_navigation = navitation;
}
}
And this is one of my pages( for the sake of space, i put only the relevant code):
BindingContext = new JumpVM(this.Navigation);
....
Image fbInvite = new Image
{
Source = ImageSource.FromResource(Constants.ASSETLOCATION + ".facebookInviteIcon.png"),
HorizontalOptions = LayoutOptions.Center
};
fbInvite.GestureRecognizers.Add(new TapGestureRecognizer(sender =>
{
//navigation in the method below
FaceboonInviteFriends();
fbInvite.Opacity = 0.8;
fbInvite.FadeTo(1);
}));
I want when I click the image, to execute the Command in the JumpVM class and navigate to the page there. How can I do that?
This is Answer for Navigating one page to another page in ViewModel concept.
public ICommand NavigationList { get; set; }
NavigationList = new Command(GetListview);
public void GetListview()
{
Xamarin.Forms.Application.Current.MainPage.Navigation.PushAsync(new ListViewPerson());
}
Try adding the following line after the FadeTo line:
((JumpVM)BindingContext).NewPage.Execute(null).
If you are using ViewModels you can implement this easily with ICommand.
namespace YourApp.ViewModels
{
public class CurrentPageViewModel
{
public ICommand BackToPage {get; private set; }
public CurrentPageViewModel()
{
BackToPage = new Command(async () => {
await Application.Current.MainPage.Navigation.PushModalAsync(new MainPage());
});
}
}
}
And in the ViewModel of the page that you want to go, you need to implement the PopAsync as follows.
namespace YourApp.ViewModels
{
public class MainPageViewModel
{
public ICommand BackToMain { get; private set; }
public MainPageViewModel()
{
BackToMain = new Command(async () => {
await Application.Current.MainPage.Navigation.PopAsync();
});
}
}
}
Also remember to use the Bindings at your Views CodeBehind on both the current page and that you want to go the like this.
namespace RealmApp1.Views
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new MainPageViewModel();
}
}
}
Hope it works for you!
Have A Nice Code!!!
Related
I created WPF on the MVVM principle, but I can't make it possible to open another one from the current page.
I followed this example
We need something like this:
Example
It is also worth considering that a page instance should be created. (That is, so that you can open yourself from Page 2, but with a different name)
My failed attempt:
Page 2 ViewModel
public Page2ViewModel()
{
ButtonCommand = new RelayCommand(o => LoadOtherView());
}
public string Title { get; set; } = "Page2";
public string Text { get; set; } = "Page two";
public ICommand ButtonCommand { get; set; }
private void MainButtonClick(object sender)
{
//MainViewModel main = new MainViewModel();
//main.SelectedPageViewModel = main.PageViewModels[0];
//main.SelectedPageViewModel.Title = "да";
//main.SelectedPageViewModel.Text = "Первая страница";
}
private void LoadOtherView()
{
// Instead of interacting with a whole ViewModel, we just use the interface
//_pageDisplay.ChangePageCommand.Execute(new ContactViewModel());
_pageDisplay.ChangeViewModel(_listPageViewModels[0]);
}
Interfaces
namespace WpfMVVMCore.Interfaces
{
public interface IPageDisplay
{
public IPageViewModel GetCurrentPage();
public IList<IPageViewModel> ListPageViewModels();
public void ChangeViewModel(IPageViewModel newPage);
}
}
MainViewModel
public MainViewModel(IPageDisplay pageDisplay, IList<IPageViewModel> ListPageViewModels)
{
_pageDisplay = pageDisplay;
_pageViewModels = ListPageViewModels;
}
public IPageViewModel GetCurrentPage()
{
return _selectedPageViewModel;
}
public void ChangeViewModel(IPageViewModel newPage)
{
this.SelectedPageViewModel = newPage;
}
public IList<IPageViewModel> ListPageViewModels()
{
return _pageViewModels;
}
** If the information provided by me is not enough for you, you can download this project (with my unsuccessful attempt 🤭 ):download**
P.S. Please do not criticize me if it is not difficult for you. I'm new to MVVM. Better help :)
I'm trying to implement logging in on Xamarin Forms (5.0.0, using ActiveDirectory's built in Login page). Any ideas on how to make this work?
In the constructor of App.xaml.cs, I have:
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new LoginPage());
}
I implement the Login page w/ a view model, in which I pass in a callback that should (according to the documentation), set my navigation root to my HomePage:
public partial class LoginPage : ContentPage
{
private async Task _handleLoginAsync()
{
Navigation.InsertPageBefore(new HomePage(), this);
await Navigation.PopAsync();
}
public LoginPage()
{
InitializeComponent();
BindingContext = new LoginPageViewModel(_handleLoginAsync);
}
}
In the view model, I try to login using Device.BeginInvokeOnMainThread, calling my(note, I didn't include login logic for brevity/cleanliness)
public Command LoginCommand => new Command(LoginUsingAzureAsync);
private Func<Task>_handleLoginAsync;
public LoginPageViewModel(Func<Task> handleLoginAsync)
{
_handleLoginAsync = handleLoginAsync;
LoginCommand.Execute(null);
}
internal void LoginUsingAzureAsync()
{
Device.BeginInvokeOnMainThread(async () =>
{
try
{
if (await Login()) == true)
{
UserDialogs.Instance.HideLoading();
await _handleLoginAsync();
return;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
UserDialogs.Instance.Alert("The login has failed.");
}
});
}
It successfully goes to the homepage, but navigating to other pages afterwards doesn't work. When I call the following, it enters the OtherPage() constructor, but fails to render the new page.
Navigation.PushAsync(new OtherPage());
Note, the navigation works as expected if I use PushAsync(new HomePage()) rather than removing the login, but I'd prefer to remove the login page from the navigation stack.
Thanks in advance!
Update: Here's the initial HomeViewModel:
public class HomeViewModel
{
private readonly INavigation _navigation;
public Command GoToOtherPageCommand => new Command(GoToOtherPage);
public async void GoToOtherPage()
{
await App.Navigation.PushAsync(new OtherPage());
}
}
The problem wasn't in the login, it was in the HomePageViewModel, which was initially referencing App.Navigation (see update in question).
Passing in the navigation into my ViewModel did the trick:
public class HomeViewModel
{
private readonly INavigation _navigation;
public Command GoToOtherPageCommand => new Command(GoToOtherPage);
public async void GoToOtherPage()
{
await _navigation.PushAsync(new OtherPage());
}
public HomeViewModel(INavigation navigation)
{
_navigation = navigation;
}
}
public partial class HomePage : ContentPage
{
public HomePage()
{
InitializeComponent();
BindingContext = new HomeViewModel(Navigation);
}
}
I am trying to pass a value to a view model from another view model before navigating to the page attached to that view model.
I was previously passing it to the view, then passing it to the view model. This seems like a clumsy way of doing things.
I am not using any kind of framework so that is not an option.
At the moment the property is set as static and this works but im not sure if this is good practice.
The code:
View model 1:
This command opens the new page:
public void OpenRouteDetails()
{
RouteStopPopOverViewModel.RouteName = "TestRoute";
App.Page.Navigation.PushAsync(new RouteStopPopOverView());
}
View model 2: (RouteStopPopOverViewModel)
public static string RouteName { get; set; }
This does work but I would prefer not to use static as a way to achieve this.
Is there some way to set the RouteName property without using static or passing it through view-> view model.
I have seen some answers about this but they don't seem to answer to question clearly.
Share a controller class between view models.
The same instance has to be supplied to the constructor in both view models.
So you can set values, and listen for events in both view models.
The controller class becomes the intermediary.
public class SharedController : IControlSomething
{
private string _sharedValue;
public string SharedValue
{
get => _sharedValue;
set
{
if (_sharedValue == value)
return;
_sharedValue = value;
OnSharedValueUpdated();
}
}
public event EventHandler SharedValueUpdated;
protected virtual void OnSharedValueUpdated()
{
SharedValueUpdated?.Invoke(this, EventArgs.Empty);
}
}
public class ViewModel1
{
private readonly IControlSomething _controller;
public ViewModel1(IControlSomething controller)
{
// Save to access controller values in commands
_controller = controller;
_controller.SharedValueUpdated += (sender, args) =>
{
// Handle value update event
};
}
}
public class ViewModel2
{
private readonly IControlSomething _controller;
public ViewModel2(IControlSomething controller)
{
// Save to access controller values in commands
_controller = controller;
_controller.SharedValueUpdated += (sender, args) =>
{
// Handle value update event
};
}
}
here the sample you can achieve your requirement easily with navigation
public class ViewModelFrom : BaseViewModel
{
async Task ExecuteCommand()
{
string routeName="value to trasfer";
Navigation.PushAsync(new View(routeName));
}
}
public partial class View : ContentPage
{
public View(string routeName)
{
InitializeComponent();
BindingContext = new ViewModelTo(routeName);
}
}
public class ViewModelTo : BaseViewModel
{
public string RouteName { get; set; }
public ViewModelTo(string routeName)
{
RouteName=routeName;
}
}
If there is a hierarchy you could express that in a parent to both of them.
public class Route
{
private string Name;
}
public class RouteSelectedArgs : EventArgs
{
public Route Selected { get; set; }
}
public interface IRouteSelection
{
event EventHandler<RouteSelectedArgs> RouteSelected;
}
public interface IRouteDetails { }
public class RouteWizard
{
public UserControl view { get; set; }
private IRouteSelection _selection;
private IRouteDetails _details;
public RouteWizard(IRouteSelection selection, IRouteDetails details)
{
_selection = selection;
_details = details;
_selection.RouteSelected += Selection_RouteSelected;
view = MakeView(_selection);
}
private void Selection_RouteSelected(object sender, RouteSelectedArgs e)
{
_selection.RouteSelected -= Selection_RouteSelected;
view = MakeView(_details, e.Selected);
}
private UserControl MakeView(params object[] args)
{
////magic
throw new NotImplementedException();
}
}
As you are using the MVVM pattern, you can use one of the many MVVM Frameworks to achieve this.
I use FreshMvvm and it allow me to pass parameters between view models like this
await CoreMethods.PushPageModel<SecondPageModel>(myParameter, false);
Then in SecondPageModel I can see access the parameters in the Init method
private MyParamType _myParameter;
public override void Init(object initData)
{
base.Init(initData);
var param = initData as MyParamType;
if (param != null)
{
_myParameter = param;
}
}
You can find more details about FreshMvvm here although most MVVM frameworks have similar functionality.
As the title suggests, on Xamarin Forms, I am trying to watch from a View when a property on the ViewModel changes.
This is my ViewModel class
public class RegisterViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public bool AutomaticVerificationDone { get; set; }
public ICommand AutomaticVerification
{
get
{
return new Command(async () =>
{
AutomaticVerificationDone = true;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("AutomaticVerificationDone"));
});
}
}
}
This is my Register.xaml.cs class
public partial class Register : ContentPage
{
public static readonly BindableProperty AutomaticVerificationDoneProperty = BindableProperty.Create(nameof(AutomaticVerificationDone), typeof(bool), typeof(Register), false);
public bool AutomaticVerificationDone
{
get { return (bool)GetValue(AutomaticVerificationDoneProperty); }
set
{
SetValue(AutomaticVerificationDoneProperty, value);
if (value)
accessButton.Opacity = 1;
else
accessButton.Opacity = 0.8f;
}
}
public Register()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
this.BindingContext = new RegisterViewModel();
}
}
Doing in this way nothing happens.
What am I missing?
Bindable properties don't use your setter; they go directly through the bindable property system.
Instead, you need to pass a propertyChanged callback to BindableProperty.Create.
But actually, you should bind Opacity in your XAML (using a converter) instead.
I'm using MVVM light for a WPF application. I have a view model with several commands that use the RelayCommand. Since the code is very similar for each command, I created a GetCommand Method. But the resulting RelayCommand does not work if I use the param inside the RelayCommand. If I don't use the param everything works fine (except that I can't pass a value).
Can someone explain why this happens and what other solution there is to reuse the code without copy & paste?
Below is a very reduced version of my code that shows only the important parts:
public class MainViewModel {
public RelayCommand commandOne = GetCommand("one");
public RelayCommand commandTwo = GetCommand("two");
public RelayCommand GetCommand(string param) {
return new RelayCommand(() => {
// Do something accessing other properties of MainViewModel
// to detect if another action is alreay running
// this code would need to be copy & pasted everywhere
if(param == "one")
_dataService.OneMethod();
else if(param == "two")
_dataService.TwoMethod();
else
_dataService.OtherMethod();
var name = param;
});
}
}
This is how I usually use RelayCommands where I just bind the commands to methods.
public class MainViewModel {
public MainViewModel()
{
CommandOne = new RelayCommand<string>(executeCommandOne);
CommandTwo = new RelayCommand(executeCommandTwo);
}
public RelayCommand<string> CommandOne { get; set; }
public RelayCommand CommandTwo { get; set; }
private void executeCommandOne(string param)
{
//Reusable code with param
}
private void executeCommandTwo()
{
//Reusable code without param
}
}
You may be looking for something like the following
public partial class MainWindow : Window
{
private RelayCommand myRelayCommand ;
private string param = "one";
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
public RelayCommand MyRelayCommand
{
get
{
if (myRelayCommand == null)
{
myRelayCommand = new RelayCommand((p) => { ServiceSelector(p); });
}
return myRelayCommand;
}
}
private void DoSomething()
{
MessageBox.Show("Did Something");
}
private void ServiceSelector(object p)
{
DoSomething();
if (param == "one")
MessageBox.Show("one");
else if (param == "two")
MessageBox.Show("two");
else
MessageBox.Show("else");
var name = param;
}
}