I need to pass data to the home page when the user is logged in. I need to pass the username to the home page.
public void Login(string Username, string password)
{
// ..... Do login and if success
var Logindata = database.GetUsername(_usernamelogin);
Application.Current.MainPage.Navigation.PushAsync(new Homepage(Logindata));
}
My method to get the user name is
public Register_person GetUsername(string mail1)
{
return Conn.Table<Register_person>().FirstOrDefault(t => t.UserName == mail1);
}
my home page XAML
In my home page code behind cs, I retrieve the incoming data
public Register_person register_Person;
public Homepage (Register_person loindata)
{
InitializeComponent ();
l1.Text = logindata.UserName;
}
This code works, I can get the username. But I am using MVVM and not sure how to implement this in MVVM.
The pure MVVM way of doing this would be to abstract the navigation and invoke it from your viewmodel (see Prisms navigation service as a reference). Anyway, there can be quite some pitfalls in implementing such a navigation service. If at any rate possible I'd suggest to integrate Prism in your solution and go full MVVM.
There is, however, a hybrid approach that would be way easier to implement, but is not pure MVVM. Assuming that you are not injecting dependencies you could define your binding directly in your XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App1"
xmlns:generic="clr-namespace:System.Collections.Generic;assembly=netstandard"
x:Class="App1.MainPage"
x:Name="Page">
<ContentPage.BindingContext>
<local:ViewModel />
</ContentPage.BindingContext>
<!-- Your content goes here -->
</ContentPage>
Within your viewmodel you can now define a command to log the user in and an event that is used to communicate to your view that the user was logged in successfully (please note that this code is stripped down to the bare minimum)
class ViewModel
{
/// <summary>Initializes a new instance of the <see cref="T:System.Object"></see> class.</summary>
public ViewModel()
{
LogInCommand = new Command(OnLogIn);
}
private void OnLogIn()
{
// your login logic shall go here
// your password and user name shall be bound
// via other properties
// Invoke the LoggedIn event with the user name
// of the logged in user.
LoggedIn?.Invoke(userName);
}
public event Action<string> LoggedIn;
public Command LogInCommand { get; }
}
From your view you can subscribe to LoggedIn
<ContentPage.BindingContext>
<local:ViewModel LoggedIn="ViewModel_OnLoggedIn" />
</ContentPage.BindingContext>
And of course you need the respective method in your code behind (the .xaml.cs file)
private void ViewModel_OnLoggedIn(string obj)
{
// navigate the other page here
}
This is not a solution you can plug in directly, but should point you to the right direction. Please note that you'll have to bind some Button or something else to LogInCommand, as well as entries to properties for the username and password.
Related
I have a Window with a Grid, which has a "MainWindowViewModel" set as its DataContext
<Grid x:Name="MainGrid">
<Grid.DataContext>
<view:MainWindowViewModel/>
</Grid.DataContext>
<!-- ... -->
</Grid>
This MainGrid has two SubGrids (not named) and one of them contains a Frame which displays Pages.
The Pages displayed have other ViewModels set as their DataContext.
<Page.DataContext>
<view:AddOrderViewModel/>
</Page.DataContext>
In the MainWindowViewModel I have a Property "User". I want to access this Property from the ViewModel of the Page.
Is that even possible (without using "code behind"). I dont really know where to start since I dont know how to get the FrameworkElement using the ViewModel from within the ViewModel (I guess from there its only handling the visual tree?)
Any help, or push in the right direction would be greatly appreciated. Also if you have a better idea of how to pass the property from one ViewModel to the other, feel free to share :)
Thanks
I would suggest to try MVVM Light's Messenger.
It is thoroughly enough explained here
You create a class where you place the object property you want to send between ViewModels
public class MessageClassName
{
public object MyProperty { get; set;}
}
Assuming you want to send the property from ViewModel1 to ViewModel2, you create a method in ViewModel1
private void SendProperty(object myProperty)
{
Messenger.Default.Send<MessageClassName>(new MessageClassName() { MyProperty = myProperty });
}
Then you are calling it from your code when you want it to be sent.
SendProperty(_myProperty);
In the constructor of ViewModel2 you register to that message
public ViewModel2()
{
Messenger.Default.Register<MessageClassName>(this, (message) =>
{
ReceiveProperty(message.MyProperty);
)};
}
Then also in ViewModel2 you define the method ReceiveProperty
private void ReceiveProperty(object myProperty)
{
...Do whatever with myProperty here...
}
Note that you need to add
using GalaSoft.MvvmLight.Messaging;
in both ViewModel1 and ViewModel2 classes
My exact problem is I am trying to add logic to all the pages in my Xamarin app, globally.
That is, I am trying to create a "master page" that all my pages inherit from. I put header/footer and navigation logic in this "master page" so that I don't have to rewrite it on every page. ...I say "master page" because Xamarin has something called a master-detail page and that's not what I'm talking about. I'm talking about the concept of a master page like you might have used in ASP.NET Web Forms. The closest analog to that in Xamarin is <ControlTemplate> which, with a little work on your part, gives pretty much the same effect as what you might actually think of as a master page. It kind of looks like this:
App.xaml (the body of the master page)
...
<Application.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="MainPageTemplate">
<StackLayout>
<Label Text="{TemplateBinding HeaderText}" />
<ContentPresenter /> <!-- this is a placeholder for content -->
<Label Text="{TemplateBinding FooterText}" />
</StackLayout>
</ControlTemplate>
</ResourceDictionary>
</Application.Resources>
...
MyPage.xaml (the content that goes in the master page)
<!-- Translation: this page inherits from CoolApp.Views.MyMasterPage.cs -->
<Views:MyMasterPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Views="clr-namespace:CoolApp.Views"
x:Class="CoolApp.Views.MyPage"
ControlTemplate="{StaticResource MainPageTemplate}">
<Label Text="I am the content" />
</Views:MyMasterPage>
MyMasterPage.cs (the shared header/footer and navigation logic)
// it inherits from ContentPage here, which works fine. so long as your app only uses ContentPages
public class MyMasterPage : ContentPage, INotifyPropertyChanged
{
...
public static BindableProperty HeaderTextProperty;
public string HeaderText
{
get { return (string)GetValue(HeaderTextProperty); }
set
{
HeaderTextProperty = BindableProperty.Create("HeaderText", typeof(string), typeof(SearchPage), value);
OnPropertyChanged("HeaderText");
}
}
// footer logic
// navigation logic
...
}
This master page setup works great for me until I need to use page types other than ContentPage. Specifically, I need a CarouselPage to work exactly the same way. In the above code I could have MyMasterPage.cs inherit from CarouselPage instead of ContentPage and that would be fine except then I would have the global header/footer and navigation logic in two different master pages. I can't just put the logic in a master page that inherits from Page because then my pages would be of type Page instead of ContentPage and CarouselPage. But if I could modify Page directly then ContentPage and CarouselPage would both get my changes. Maybe that would sort of look like this?
I hope it's clear but in case it's not the question I am trying to ask is how can I get my header and footer and navigation logic all into a single master page?
I hear add functionality to a superclass and think extensions. I don't know anything about c# but, for instance, in swift you can add behavior to an existing type. This isn't achieved by modifying a child, however, but you can give a superclass new abilities. Not sure if this is exactly what you might be looking for in your use case
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods
I have the same reasoning as nbpeth with extensions
if you want to add a property to all your pages (here MyContentPage and MyCarouselPage), you may want to create a single master page for each instead of only one master page, but centralize the navigation logic in an extension class
you could try something like the following (I do not guaranty this code works, I have no Xamarin setup at hand)
public static class PageExtensions
{
public string GetMyValue(this Page page, BindableProperty headerTextProperty)
{
return (string)page.GetValue(headerTextProperty);
}
public void SetMyValue(this Page page, out BindableProperty headerTextProperty, string value)
{
headerTextProperty = null;
if (page is INotifyPropertyChanged notifyPage)
{
headerTextProperty = BindableProperty.Create("HeaderText", typeof(string), typeof(SearchPage), value);
notifyPage.OnPropertyChanged("HeaderText");
}
}
}
public class MyMasterContentPage : ContentPage
{
// ...
public BindableProperty HeaderTextProperty;
public string HeaderText
{
get { return GetMyValue(HeaderTextProperty); }
set { SetMyValue(out HeaderTextProperty, value); }
}
}
public class MyContentPage : MyMasterContentPage, INotifyPropertyChanged
{
}
public class MyMasterCarouselPage : CarouselPage
{
// ...
public BindableProperty HeaderTextProperty;
public string HeaderText
{
get { return GetMyValue(HeaderTextProperty); }
set { SetMyValue(out HeaderTextProperty, value); }
}
}
public class MyCarouselPage : MyMasterCarouselPage, INotifyPropertyChanged
{
}
You would still have some duplicate code but not duplicate logic
I'd like to bind a command to the command property of my button. This seemed pretty straightforward since I've done this many times before in WPF and the method here is very similar. Let me show some code snippets.
XAML
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.View.CustomPage"
Title="Something">
<ContentPage.Content>
<StackLayout>
<Button x:Name="numBtn" Text="Increase number" Command="{Binding IncreaseCommand}" />
<Label x:Name="numLabel" Text="{Binding numberText}" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
Code-behind
public partial class CustomPage : ContentPage
{
public CustomPage ()
{
InitializeComponent ();
BindingContext = ViewModelLocator.ViewModel(); //ViewModelLocator is singleton, gives
//you a ViewModel instance
}
}
ViewModel
public ICommand IncreaseCommand { get; private set; }
private int number;
public string numberText { get; private set;}
the constructor:
public ViewModel()
{
IncreaseCommand = new Command (() => IncreaseExecuted ());
number = 0;
numberText = number.ToString ();
OnPropertyChanged (numberText);
}
and then
private void IncreaseExecuted()
{
number++;
numberText = number.ToString ();
OnPropertyChanged (numberText);
}
When I run the app using the Xamarin Android Player (KitKat) I see the button, and the label reading 0. Then I press the button and nothing happens. I tried checking what happens with breakpoints but the app doesn't pause, not even when they're in the constructor of my ViewModel. I guess it's something to do with the emulator. Anyway, I think the binding is ok since I can see a "0" on the screen. What could be the problem? Let me show my ViewModelBase class just in case:
ViewModelBase
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Maybe my numberText property doesn't get updated when I call OnPropertyChanged? But I've used the exact same ViewModelBase class several times before and it always worked fine. One last thing, my CustomPage page is wrapped inside a NavigationPage which is a child of a TabbedPage:
MainPage.xaml.cs
this.Children.Add (new NavigationPage (new CustomPage ()) {Title="Something"} );
This shouldn't affect anything but there it is just in case. So what's wrong with my command binding? Thank you in advance!
This answer is not directly related to the problem of the original question, however this question is the one that ranks highest in search engines and the title of this question is ambiguous to the question that this answer answers.
I was having issues with a Command that wouldn't fire the associated command action. My problem was that I had defined the Command as a field instead of a property.
Works:
public Command MyCommand { get; set; }
Doesn't work:
public Command MyCommand;
Hope this helps someone else.
You are almost there. Take a close look at your call to the OnPropertyChanged method; you are passing the value of numberText and not the name. If you change your code to pass "numberText" I expect it shall work properly.
Edit: I should add that the the OnPropertyChanged call in the constructor has the same problem. The reason you see "0" at startup is that the view is simply using the existing binding to retrieve the value.
Edit 2: Now that Xamarin supports C# 6.0, you can use the new "nameof" expression that eliminates the need for a hard-coded string. Alternately, you can use MvvmCross, MvvmLight, or XLabs' MVVM classes.
This question already has answers here:
How can I use the RelayCommand in wpf?
(2 answers)
Closed 8 years ago.
I'm fairly new to MVVM and I recently discovered that buttons could be bound to functions in classes as well.
So what I've been doing basically is authenticating usernames and passwords using Linq using the click event in the code behind like this:
private void Login_Click(object sender, RoutedEventArgs e)
{
var user = from u in MyDBDataSet.logins
where u.username == usernameTextBox.Text
&& u.password == passwordPasswordBox.Password
select u;
if (user.Any())
{
MessageBox.Show("Success, Credentials Found");
}
else
{
MessageBox.Show("Error, Nothing Found");
}
}
In order for me to use the MVVM pattern I'd end up having to do something like this right?
<!-- Create the binding in the xaml />
<Button Content="Login" Command="{Binding login}"/>
Set the data context in the code behind
public login()
{
InitializeComponent();
// LoginSQL is the View Model class
LoginSql = new SQL.content.authentication.login.loginSQL();
DataContext = LoginSql;
}
And in the View Model, create the property to bind the button to.
private ICommand _login;
public ICommand login
{
get;
set;
}
See this is where my knowledge of it is very limited. From looking around here, I got the understanding that I need to tell it under which conditions it can execute or the button will be greyed out when the app is run, and I should also tell it what to do when it executes.
At the moment that's what's failing me though. How do I hook up the method to log the user in to the ICommand property?
You will need to create/obtain a common MVVM pattern base-class called DelegateCommand. Basically this class implements ICommand allows you to specify delegates for the Execute() and CanExecute() methods in the ICommand interface.
The PRISM Library contains an implementation of it and so does Kent Boogaart's blog
I am working on a Windows Phone 7 application. Now I need to switch the view after a user tapped the designated button which takes user to another view.
Which component, theoretically, in MVVM should be in charge of the navigation, i.e. switching views? Code snippets would be good to show demonstration.
I have tried inserting the switching code in View and it works alright, but I encountered a situation where I call an asynchronous web service and would like to navigate user to the new view only after the operation is done, the navigation code should be inside the event handler.
Thank you.
P/S: My project's deadline is coming soon, I have no time to rebuild my project using MVVM tools, such as MVVM Light, Caliburn Micro, and etc.
I put a Navigate methods in the base class that all my ViewModel's share:
protected void Navigate(string address)
{
if (string.IsNullOrEmpty(address))
return;
Uri uri = new Uri(address, UriKind.Relative);
Debug.Assert(App.Current.RootVisual is PhoneApplicationFrame);
BeginInvoke(() =>
((PhoneApplicationFrame)App.Current.RootVisual).Navigate(uri));
}
protected void Navigate(string page, AppViewModel vm)
{
// this little bit adds the viewmodel to a static dictionary
// and then a reference to the key to the new page so that pages can
// be bound to arbitrary viewmodels based on runtime logic
string key = vm.GetHashCode().ToString();
ViewModelLocator.ViewModels[key] = vm;
Navigate(string.Format("{0}?vm={1}", page, key));
}
protected void GoBack()
{
var frame = (PhoneApplicationFrame)App.Current.RootVisual;
if (frame.CanGoBack)
frame.GoBack();
}
So the ViewModel base class executes the navigation if that's what you are asking. And then typically some derived ViewModel class controls the target of the navigation in response to the execution of an ICommand bound to a button or hyperlink in the View.
protected SelectableItemViewModel(T item)
{
Item = item;
SelectItemCommand = new RelayCommand(SelectItem);
}
public T Item { get; private set; }
public RelayCommand SelectItemCommand { get; private set; }
protected override void SelectItem()
{
base.SelectItem();
Navigate(Item.DetailPageName, Item);
}
So the View only knows when a navigate action is needed and the ViewModels know where to go (based on ViewModel and Model state) and how to get there.
The view should have a limited number of possible destinations. If you have to have a top-level navigation on every page, that should be part of your layout or you can put them in a child view.
I put navigation outside of MVVM in a class that is responsible for showing/hiding views.
The ViewModels use a messagebroker with weakevents to publish messages to this class.
This setup gives me most freedom and doesn't put any responsibilities in the MVVM classes that do not belong there.