I have a shell app and I want to navigate from one page to another. I have followed the documentation and turotials step by step but for some reason the QueryParameter is being ignored(Since the Init method is not being called). Am I missing something?
First Page:
List<int> rowColumn = Methods.GetGridRowColumn((Frame)s);
HomeTab homeTab = dict[rowColumn[0]+""+rowColumn[1]];
var content = JsonConvert.SerializeObject(homeTab);
await Shell.Current.GoToAsync($"{nameof(CategoriesPage)}?homeTab={content}");
Page to navigate:
[XamlCompilation(XamlCompilationOptions.Compile)]
[QueryProperty(nameof(HomeTab), "homeTab")]
public partial class CategoriesPage : ContentPage
{
private HomeTab homeTab;
public string HomeTab
{
set
{
string content = Uri.UnescapeDataString(value);
homeTab = JsonConvert.DeserializeObject<HomeTab>(content);
Init();
}
}
public CategoriesPage()
{
InitializeComponent();
}
}
Related
I am writing a program using Xamarin, Shell and MVVM.I want to send a parameter to next page and I am using the following code:
await Shell.Current.GoToAsync($"//{nameof(CopyBooksPage)}?RegisteredUserId={registeredUser.Id}");
Question: How to get the parameter before binding?
First wrong solution:
In CopyBooksPage.xaml I have binding to properties in VM but I have no affiliation with the VM.
I do this:
[XamlCompilation(XamlCompilationOptions.Compile)]
[QueryProperty(nameof(RegisteredUserId), "RegisteredUserId")]
public partial class CopyBooksPage : ContentPage
{
private int _registeredUserId;
public int RegisteredUserId
{
get { return _registeredUserId; }
set
{
_registeredUserId = value;
CopyBooksViewModel copyBooksViewModel = App.GetViewModel<CopyBooksViewModel>();
copyBooksViewModel.RegisteredUserId = _registeredUserId;
BindingContext = copyBooksViewModel;
copyBooksViewModel.RefreshBinding();
}
}
public CopyBooksPage()
{
InitializeComponent();
}
}
Call order:
construktor
properties RegisteredUserId
The problem is twofold.
After calling the constructor, I get information about binding errors (because there is no VM). It's not annoying but I want (need) to get rid of it.
I do the binding only in the propertis and this causes the problem that for each VM propertis I have to call the OnPropertyChanged method to refresh the binding. And this is troublesome for me. I do not want to do it.
Second wrong solution
Code behind:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CopyBooksPage : ContentPage
{
public CopyBooksPage()
{
InitializeComponent();
BindingContext = App.GetViewModel<CopyBooksViewModel>();
}
}
ViewModel:
class CopyBooksViewModel : BaseViewModel, IQueryAttributable
{
private int registeredUserId;
//read from database
private CopyBook copyBookModel;
public string BookTitle
{
get { return copyBookModel.Title; }
set
{
copyBookModel.Title = value;
OnPropertyChanged(nameof(Title));
}
}
public CopyBooksViewModel()
{
}
public void ApplyQueryAttributes(IDictionary<string, string> query)
{
if (query.ContainsKey("RegisteredUserId"))
{
registeredUserId = int.Parse(HttpUtility.UrlDecode(query["RegisteredUserId"]));
copyBookModel = ReadFromDatabase(registeredUserId);
}
}
}
Call order:
construktor
Binding
method ApplyQueryAttributes
When binding the BookTitle, I reference the copyBookModel which is null. I could secure it.
The problem is that the ApplyQueryAttributes method is called last. In it again I would have to call OnPropertyChanged for all propertis. I do not want to do it.
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 have a bug in my Xamarin Forms app. When I was coding page to appear content "master-detail" when the user chose on the last page. The content doesn't appear.
During debugging, the app work without any code errors; however, it appears without any content. I am using the Firebase database.
Firebase helper:
public async Task<List<Cuts>> GetAction()
{
return (await firebase
.Child("Action")
.OnceAsync<Cuts>()).Select(item => new Cuts
{
name = item.Object.name,
link = item.Object.link,
poster = item.Object.poster
}).ToList();
}
The page which the users choose from:
public partial class ActionFilms : ContentPage
{
FirebaseHelper firebaseHelper = new FirebaseHelper();
public ActionFilms()
{
InitializeComponent();
}
protected async override void OnAppearing()
{
base.OnAppearing();
var allPersons = await firebaseHelper.GetAction();
act.ItemsSource = allPersons;
}
public void act_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var acctionn = e.PreviousSelection as Cuts;
App.Current.MainPage.Navigation.PushAsync(new ActionMaster(acctionn));
}
}
The code of master-detail Page "which the content does not appear at here":
public partial class ActionMaster : ContentPage
{
public ActionMaster(Cuts acctionn)
{
BindingContext = acctionn;
InitializeComponent();
}
}
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!!!