Login modal with MVVM pattern

Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
1
down vote
favorite
I would like some feedback on whether this follows MVVM principles well.
The MainPage has a Login button that opens the login modal page. Upon successful login, the LoginPageViewModel will raise an event which is handled in the parent viewmodel.
I use Action objects to do the show/hide modals. The Actions are defined in a page's codebehind and invoked in its viewmodel.
MainPage.xaml
<Button Command="Binding ShowLoginModalCommand" Text="Login"/>
MainPage.xaml.cs
public MainPage()
var vm = new MainPageViewModel();
vm.ShowLoginModal += (lvm) => Navigation.PushModalAsync(new LoginPage(lvm));
vm.HideLoginModal += () => Navigation.PopModalAsync();
BindingContext = vm;
InitializeComponent();
MainViewModel.cs
public Action<LoginViewModel> ShowLoginModal;
public Action HideLoginModal;
public ICommand ShowLoginModalCommand => new Command(() =>
var lvm = new LoginViewModel();
lvm.LoginSucceeded += (se, ev) =>
this.Customer = ev.Customer;
HideLoginModal();
;
ShowLoginModal(lvm);
public Customer Customer
get => _customer;
set
_customer = value;
OnPropertyChanged(nameof(Customer));
LoginPage.xaml
<Button Command="Binding LoginCommand" Text="Login"/>
LoginViewModel.cs
public ICommand LoginCommand => new Command(async () =>
var customer = await VerifyLogin(); // Assume valid login.
LoginSucceeded(this, new LoginSuccessEventArgs(customer));
public class LoginSuccessEventArgs : EventArgs
public Customer Customer get; set;
public LoginSuccessEventArgs(Customer customer) => Customer = customer;
public event EventHandler<LoginSuccessEventArgs> LoginSucceeded;
c# authentication mvvm xaml xamarin
add a comment |Â
up vote
1
down vote
favorite
I would like some feedback on whether this follows MVVM principles well.
The MainPage has a Login button that opens the login modal page. Upon successful login, the LoginPageViewModel will raise an event which is handled in the parent viewmodel.
I use Action objects to do the show/hide modals. The Actions are defined in a page's codebehind and invoked in its viewmodel.
MainPage.xaml
<Button Command="Binding ShowLoginModalCommand" Text="Login"/>
MainPage.xaml.cs
public MainPage()
var vm = new MainPageViewModel();
vm.ShowLoginModal += (lvm) => Navigation.PushModalAsync(new LoginPage(lvm));
vm.HideLoginModal += () => Navigation.PopModalAsync();
BindingContext = vm;
InitializeComponent();
MainViewModel.cs
public Action<LoginViewModel> ShowLoginModal;
public Action HideLoginModal;
public ICommand ShowLoginModalCommand => new Command(() =>
var lvm = new LoginViewModel();
lvm.LoginSucceeded += (se, ev) =>
this.Customer = ev.Customer;
HideLoginModal();
;
ShowLoginModal(lvm);
public Customer Customer
get => _customer;
set
_customer = value;
OnPropertyChanged(nameof(Customer));
LoginPage.xaml
<Button Command="Binding LoginCommand" Text="Login"/>
LoginViewModel.cs
public ICommand LoginCommand => new Command(async () =>
var customer = await VerifyLogin(); // Assume valid login.
LoginSucceeded(this, new LoginSuccessEventArgs(customer));
public class LoginSuccessEventArgs : EventArgs
public Customer Customer get; set;
public LoginSuccessEventArgs(Customer customer) => Customer = customer;
public event EventHandler<LoginSuccessEventArgs> LoginSucceeded;
c# authentication mvvm xaml xamarin
add a comment |Â
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I would like some feedback on whether this follows MVVM principles well.
The MainPage has a Login button that opens the login modal page. Upon successful login, the LoginPageViewModel will raise an event which is handled in the parent viewmodel.
I use Action objects to do the show/hide modals. The Actions are defined in a page's codebehind and invoked in its viewmodel.
MainPage.xaml
<Button Command="Binding ShowLoginModalCommand" Text="Login"/>
MainPage.xaml.cs
public MainPage()
var vm = new MainPageViewModel();
vm.ShowLoginModal += (lvm) => Navigation.PushModalAsync(new LoginPage(lvm));
vm.HideLoginModal += () => Navigation.PopModalAsync();
BindingContext = vm;
InitializeComponent();
MainViewModel.cs
public Action<LoginViewModel> ShowLoginModal;
public Action HideLoginModal;
public ICommand ShowLoginModalCommand => new Command(() =>
var lvm = new LoginViewModel();
lvm.LoginSucceeded += (se, ev) =>
this.Customer = ev.Customer;
HideLoginModal();
;
ShowLoginModal(lvm);
public Customer Customer
get => _customer;
set
_customer = value;
OnPropertyChanged(nameof(Customer));
LoginPage.xaml
<Button Command="Binding LoginCommand" Text="Login"/>
LoginViewModel.cs
public ICommand LoginCommand => new Command(async () =>
var customer = await VerifyLogin(); // Assume valid login.
LoginSucceeded(this, new LoginSuccessEventArgs(customer));
public class LoginSuccessEventArgs : EventArgs
public Customer Customer get; set;
public LoginSuccessEventArgs(Customer customer) => Customer = customer;
public event EventHandler<LoginSuccessEventArgs> LoginSucceeded;
c# authentication mvvm xaml xamarin
I would like some feedback on whether this follows MVVM principles well.
The MainPage has a Login button that opens the login modal page. Upon successful login, the LoginPageViewModel will raise an event which is handled in the parent viewmodel.
I use Action objects to do the show/hide modals. The Actions are defined in a page's codebehind and invoked in its viewmodel.
MainPage.xaml
<Button Command="Binding ShowLoginModalCommand" Text="Login"/>
MainPage.xaml.cs
public MainPage()
var vm = new MainPageViewModel();
vm.ShowLoginModal += (lvm) => Navigation.PushModalAsync(new LoginPage(lvm));
vm.HideLoginModal += () => Navigation.PopModalAsync();
BindingContext = vm;
InitializeComponent();
MainViewModel.cs
public Action<LoginViewModel> ShowLoginModal;
public Action HideLoginModal;
public ICommand ShowLoginModalCommand => new Command(() =>
var lvm = new LoginViewModel();
lvm.LoginSucceeded += (se, ev) =>
this.Customer = ev.Customer;
HideLoginModal();
;
ShowLoginModal(lvm);
public Customer Customer
get => _customer;
set
_customer = value;
OnPropertyChanged(nameof(Customer));
LoginPage.xaml
<Button Command="Binding LoginCommand" Text="Login"/>
LoginViewModel.cs
public ICommand LoginCommand => new Command(async () =>
var customer = await VerifyLogin(); // Assume valid login.
LoginSucceeded(this, new LoginSuccessEventArgs(customer));
public class LoginSuccessEventArgs : EventArgs
public Customer Customer get; set;
public LoginSuccessEventArgs(Customer customer) => Customer = customer;
public event EventHandler<LoginSuccessEventArgs> LoginSucceeded;
c# authentication mvvm xaml xamarin
edited Apr 4 at 7:09
asked Apr 3 at 17:11
Jam
1236
1236
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
1
down vote
accepted
Generally speaking I find a good implementation and like the simplicity of the approach. I would point out some problems I found though.
Inconsistent MVVM style
You are using two different ways to instantiate views and viewmodels and linking them together in both views, an inconsistence that could get bigger the and messier the more complex the program becomes.
In the MainPage you're creating the viewmodel in the view's constructor, and assigning it to the BindingContext right away. But in the LoginPage you create the viewmodel in the handler of the main page and pass it to the constructor of the view (not show here, but I guess it also assigns the BindingContext with it).
Specially for bigger applications, it's important to clearly define one style, either view first or viewmodel first, and live with it (I don't even know if it's called "style", but it sounds like that to me). A more concerning problem is that when VMs begin to take dependencies on services and other stuff, you most likely will want to use an IoC container for injecting them, and then it becomes more important to clearly define who'll create viewmodels and how. Personally, I like viewmodel-first approach, in which viewmodels create other viewmodels and are passed to the views.
MainPage viewmodel
- Why are
ShowLoginModalandHideLoginModalplain public fields? In situations like this, I would expect them to be normal events, raised from the viewmodel and handled in the view. This way feels a bit non-standard. - On the
ShowLoginModalCommandmethod, the viewmodel creates the login viewmodel directly. Maybe its unnecesary now, but consider using an IoC container or other factory to create this, as generally when other viewmodels take extra dependencies it becomes harder to create them all inline. - The
Customersetter doesn't needs to be public, a private one will do. You only want to set the current user from the login and nowhere else, so make that crystal clear. - The invocations of the
ShowLoginModalandHideLoginModaldelegates don't contain a null validation, and will result in aNullReferenceExceptionif no one suscribed to them. A nice shortcut to call them safely is to use theShowLoginModal?(lvm);syntax.
LoginPage viewmodel
- Beware of the async reentrancy in the
LoginCommandmethod. TheVerifyLogincall inside it could potentially be a slow one, but since it's anawaitcall, the UI will remain responsive. That means the user could do other things in the meanwhile, including changing the user/password or even click the login button again! That could have many unanticipated effects. I would counter it by disabling the UI while the verification takes place, using something like this in the VM:public bool EnableForm get; set;
public ICommand LoginCommand => new Command(async () =>
//Disable UI before doing anything
EnableForm = false;
OnPropertyChanged(nameof(EnableForm));
var customer = await VerifyLogin(); // Assume valid login.
LoginSucceeded(this, new LoginSuccessEventArgs(customer));
And in the view bind the controls to that property:<Button Command="Binding LoginCommand" Text="Login" IsEnabled="Binding EnableForm"/> - In the
LoginSuccessEventArgstheCustomerproperty doesn't needs to be read-write. Once again, since you're just informing a value, there is no benefit in changing it after creation. Also, the constructor should check for null on the customer passed. - Possibly it's not shown here or just a work-in-progress thing, but there is no handling of the login failure case, that should just reenable the controls (following my above suggestion), show some error message to the user and not raise the
LoginSuccessevent. - Like in the main page, the raising of the event is not null checked and will blow up if nobody happens to be suscribed.
- The call to the
VerifyLoginmethod, although maybe simplified here, could imply a lot of work under the cover. That's the kind of thing that's better delegated to a service to do all the heavy work, and just have the presentation layer handle the visual thing. I would inject it in the constructor and call it passing the user/password entered, and it would return success or failure. Something along the lines of this:private readonly ILoginService _loginService;
public LoginViewModel(ILoginService loginService)
if(loginService == null) throw new ArgumentNullException(nameof(loginService));
_loginService = loginService;
public ICommand LoginCommand => new Command(async () =>
var customer = await _loginService.VerifyLogin(Username, Password);
if(customer == null)
//show some error message here
else
LoginSucceeded(this, new LoginSuccessEventArgs(customer));
I started to use FreshMVVM with FreshIOC. After reading your answer I realized it's better to use an easy framework to understand how it's supposed to be. Everything I have taken with me. Thank you for the long and good answer.
â Jam
Apr 10 at 6:51
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
Generally speaking I find a good implementation and like the simplicity of the approach. I would point out some problems I found though.
Inconsistent MVVM style
You are using two different ways to instantiate views and viewmodels and linking them together in both views, an inconsistence that could get bigger the and messier the more complex the program becomes.
In the MainPage you're creating the viewmodel in the view's constructor, and assigning it to the BindingContext right away. But in the LoginPage you create the viewmodel in the handler of the main page and pass it to the constructor of the view (not show here, but I guess it also assigns the BindingContext with it).
Specially for bigger applications, it's important to clearly define one style, either view first or viewmodel first, and live with it (I don't even know if it's called "style", but it sounds like that to me). A more concerning problem is that when VMs begin to take dependencies on services and other stuff, you most likely will want to use an IoC container for injecting them, and then it becomes more important to clearly define who'll create viewmodels and how. Personally, I like viewmodel-first approach, in which viewmodels create other viewmodels and are passed to the views.
MainPage viewmodel
- Why are
ShowLoginModalandHideLoginModalplain public fields? In situations like this, I would expect them to be normal events, raised from the viewmodel and handled in the view. This way feels a bit non-standard. - On the
ShowLoginModalCommandmethod, the viewmodel creates the login viewmodel directly. Maybe its unnecesary now, but consider using an IoC container or other factory to create this, as generally when other viewmodels take extra dependencies it becomes harder to create them all inline. - The
Customersetter doesn't needs to be public, a private one will do. You only want to set the current user from the login and nowhere else, so make that crystal clear. - The invocations of the
ShowLoginModalandHideLoginModaldelegates don't contain a null validation, and will result in aNullReferenceExceptionif no one suscribed to them. A nice shortcut to call them safely is to use theShowLoginModal?(lvm);syntax.
LoginPage viewmodel
- Beware of the async reentrancy in the
LoginCommandmethod. TheVerifyLogincall inside it could potentially be a slow one, but since it's anawaitcall, the UI will remain responsive. That means the user could do other things in the meanwhile, including changing the user/password or even click the login button again! That could have many unanticipated effects. I would counter it by disabling the UI while the verification takes place, using something like this in the VM:public bool EnableForm get; set;
public ICommand LoginCommand => new Command(async () =>
//Disable UI before doing anything
EnableForm = false;
OnPropertyChanged(nameof(EnableForm));
var customer = await VerifyLogin(); // Assume valid login.
LoginSucceeded(this, new LoginSuccessEventArgs(customer));
And in the view bind the controls to that property:<Button Command="Binding LoginCommand" Text="Login" IsEnabled="Binding EnableForm"/> - In the
LoginSuccessEventArgstheCustomerproperty doesn't needs to be read-write. Once again, since you're just informing a value, there is no benefit in changing it after creation. Also, the constructor should check for null on the customer passed. - Possibly it's not shown here or just a work-in-progress thing, but there is no handling of the login failure case, that should just reenable the controls (following my above suggestion), show some error message to the user and not raise the
LoginSuccessevent. - Like in the main page, the raising of the event is not null checked and will blow up if nobody happens to be suscribed.
- The call to the
VerifyLoginmethod, although maybe simplified here, could imply a lot of work under the cover. That's the kind of thing that's better delegated to a service to do all the heavy work, and just have the presentation layer handle the visual thing. I would inject it in the constructor and call it passing the user/password entered, and it would return success or failure. Something along the lines of this:private readonly ILoginService _loginService;
public LoginViewModel(ILoginService loginService)
if(loginService == null) throw new ArgumentNullException(nameof(loginService));
_loginService = loginService;
public ICommand LoginCommand => new Command(async () =>
var customer = await _loginService.VerifyLogin(Username, Password);
if(customer == null)
//show some error message here
else
LoginSucceeded(this, new LoginSuccessEventArgs(customer));
I started to use FreshMVVM with FreshIOC. After reading your answer I realized it's better to use an easy framework to understand how it's supposed to be. Everything I have taken with me. Thank you for the long and good answer.
â Jam
Apr 10 at 6:51
add a comment |Â
up vote
1
down vote
accepted
Generally speaking I find a good implementation and like the simplicity of the approach. I would point out some problems I found though.
Inconsistent MVVM style
You are using two different ways to instantiate views and viewmodels and linking them together in both views, an inconsistence that could get bigger the and messier the more complex the program becomes.
In the MainPage you're creating the viewmodel in the view's constructor, and assigning it to the BindingContext right away. But in the LoginPage you create the viewmodel in the handler of the main page and pass it to the constructor of the view (not show here, but I guess it also assigns the BindingContext with it).
Specially for bigger applications, it's important to clearly define one style, either view first or viewmodel first, and live with it (I don't even know if it's called "style", but it sounds like that to me). A more concerning problem is that when VMs begin to take dependencies on services and other stuff, you most likely will want to use an IoC container for injecting them, and then it becomes more important to clearly define who'll create viewmodels and how. Personally, I like viewmodel-first approach, in which viewmodels create other viewmodels and are passed to the views.
MainPage viewmodel
- Why are
ShowLoginModalandHideLoginModalplain public fields? In situations like this, I would expect them to be normal events, raised from the viewmodel and handled in the view. This way feels a bit non-standard. - On the
ShowLoginModalCommandmethod, the viewmodel creates the login viewmodel directly. Maybe its unnecesary now, but consider using an IoC container or other factory to create this, as generally when other viewmodels take extra dependencies it becomes harder to create them all inline. - The
Customersetter doesn't needs to be public, a private one will do. You only want to set the current user from the login and nowhere else, so make that crystal clear. - The invocations of the
ShowLoginModalandHideLoginModaldelegates don't contain a null validation, and will result in aNullReferenceExceptionif no one suscribed to them. A nice shortcut to call them safely is to use theShowLoginModal?(lvm);syntax.
LoginPage viewmodel
- Beware of the async reentrancy in the
LoginCommandmethod. TheVerifyLogincall inside it could potentially be a slow one, but since it's anawaitcall, the UI will remain responsive. That means the user could do other things in the meanwhile, including changing the user/password or even click the login button again! That could have many unanticipated effects. I would counter it by disabling the UI while the verification takes place, using something like this in the VM:public bool EnableForm get; set;
public ICommand LoginCommand => new Command(async () =>
//Disable UI before doing anything
EnableForm = false;
OnPropertyChanged(nameof(EnableForm));
var customer = await VerifyLogin(); // Assume valid login.
LoginSucceeded(this, new LoginSuccessEventArgs(customer));
And in the view bind the controls to that property:<Button Command="Binding LoginCommand" Text="Login" IsEnabled="Binding EnableForm"/> - In the
LoginSuccessEventArgstheCustomerproperty doesn't needs to be read-write. Once again, since you're just informing a value, there is no benefit in changing it after creation. Also, the constructor should check for null on the customer passed. - Possibly it's not shown here or just a work-in-progress thing, but there is no handling of the login failure case, that should just reenable the controls (following my above suggestion), show some error message to the user and not raise the
LoginSuccessevent. - Like in the main page, the raising of the event is not null checked and will blow up if nobody happens to be suscribed.
- The call to the
VerifyLoginmethod, although maybe simplified here, could imply a lot of work under the cover. That's the kind of thing that's better delegated to a service to do all the heavy work, and just have the presentation layer handle the visual thing. I would inject it in the constructor and call it passing the user/password entered, and it would return success or failure. Something along the lines of this:private readonly ILoginService _loginService;
public LoginViewModel(ILoginService loginService)
if(loginService == null) throw new ArgumentNullException(nameof(loginService));
_loginService = loginService;
public ICommand LoginCommand => new Command(async () =>
var customer = await _loginService.VerifyLogin(Username, Password);
if(customer == null)
//show some error message here
else
LoginSucceeded(this, new LoginSuccessEventArgs(customer));
I started to use FreshMVVM with FreshIOC. After reading your answer I realized it's better to use an easy framework to understand how it's supposed to be. Everything I have taken with me. Thank you for the long and good answer.
â Jam
Apr 10 at 6:51
add a comment |Â
up vote
1
down vote
accepted
up vote
1
down vote
accepted
Generally speaking I find a good implementation and like the simplicity of the approach. I would point out some problems I found though.
Inconsistent MVVM style
You are using two different ways to instantiate views and viewmodels and linking them together in both views, an inconsistence that could get bigger the and messier the more complex the program becomes.
In the MainPage you're creating the viewmodel in the view's constructor, and assigning it to the BindingContext right away. But in the LoginPage you create the viewmodel in the handler of the main page and pass it to the constructor of the view (not show here, but I guess it also assigns the BindingContext with it).
Specially for bigger applications, it's important to clearly define one style, either view first or viewmodel first, and live with it (I don't even know if it's called "style", but it sounds like that to me). A more concerning problem is that when VMs begin to take dependencies on services and other stuff, you most likely will want to use an IoC container for injecting them, and then it becomes more important to clearly define who'll create viewmodels and how. Personally, I like viewmodel-first approach, in which viewmodels create other viewmodels and are passed to the views.
MainPage viewmodel
- Why are
ShowLoginModalandHideLoginModalplain public fields? In situations like this, I would expect them to be normal events, raised from the viewmodel and handled in the view. This way feels a bit non-standard. - On the
ShowLoginModalCommandmethod, the viewmodel creates the login viewmodel directly. Maybe its unnecesary now, but consider using an IoC container or other factory to create this, as generally when other viewmodels take extra dependencies it becomes harder to create them all inline. - The
Customersetter doesn't needs to be public, a private one will do. You only want to set the current user from the login and nowhere else, so make that crystal clear. - The invocations of the
ShowLoginModalandHideLoginModaldelegates don't contain a null validation, and will result in aNullReferenceExceptionif no one suscribed to them. A nice shortcut to call them safely is to use theShowLoginModal?(lvm);syntax.
LoginPage viewmodel
- Beware of the async reentrancy in the
LoginCommandmethod. TheVerifyLogincall inside it could potentially be a slow one, but since it's anawaitcall, the UI will remain responsive. That means the user could do other things in the meanwhile, including changing the user/password or even click the login button again! That could have many unanticipated effects. I would counter it by disabling the UI while the verification takes place, using something like this in the VM:public bool EnableForm get; set;
public ICommand LoginCommand => new Command(async () =>
//Disable UI before doing anything
EnableForm = false;
OnPropertyChanged(nameof(EnableForm));
var customer = await VerifyLogin(); // Assume valid login.
LoginSucceeded(this, new LoginSuccessEventArgs(customer));
And in the view bind the controls to that property:<Button Command="Binding LoginCommand" Text="Login" IsEnabled="Binding EnableForm"/> - In the
LoginSuccessEventArgstheCustomerproperty doesn't needs to be read-write. Once again, since you're just informing a value, there is no benefit in changing it after creation. Also, the constructor should check for null on the customer passed. - Possibly it's not shown here or just a work-in-progress thing, but there is no handling of the login failure case, that should just reenable the controls (following my above suggestion), show some error message to the user and not raise the
LoginSuccessevent. - Like in the main page, the raising of the event is not null checked and will blow up if nobody happens to be suscribed.
- The call to the
VerifyLoginmethod, although maybe simplified here, could imply a lot of work under the cover. That's the kind of thing that's better delegated to a service to do all the heavy work, and just have the presentation layer handle the visual thing. I would inject it in the constructor and call it passing the user/password entered, and it would return success or failure. Something along the lines of this:private readonly ILoginService _loginService;
public LoginViewModel(ILoginService loginService)
if(loginService == null) throw new ArgumentNullException(nameof(loginService));
_loginService = loginService;
public ICommand LoginCommand => new Command(async () =>
var customer = await _loginService.VerifyLogin(Username, Password);
if(customer == null)
//show some error message here
else
LoginSucceeded(this, new LoginSuccessEventArgs(customer));
Generally speaking I find a good implementation and like the simplicity of the approach. I would point out some problems I found though.
Inconsistent MVVM style
You are using two different ways to instantiate views and viewmodels and linking them together in both views, an inconsistence that could get bigger the and messier the more complex the program becomes.
In the MainPage you're creating the viewmodel in the view's constructor, and assigning it to the BindingContext right away. But in the LoginPage you create the viewmodel in the handler of the main page and pass it to the constructor of the view (not show here, but I guess it also assigns the BindingContext with it).
Specially for bigger applications, it's important to clearly define one style, either view first or viewmodel first, and live with it (I don't even know if it's called "style", but it sounds like that to me). A more concerning problem is that when VMs begin to take dependencies on services and other stuff, you most likely will want to use an IoC container for injecting them, and then it becomes more important to clearly define who'll create viewmodels and how. Personally, I like viewmodel-first approach, in which viewmodels create other viewmodels and are passed to the views.
MainPage viewmodel
- Why are
ShowLoginModalandHideLoginModalplain public fields? In situations like this, I would expect them to be normal events, raised from the viewmodel and handled in the view. This way feels a bit non-standard. - On the
ShowLoginModalCommandmethod, the viewmodel creates the login viewmodel directly. Maybe its unnecesary now, but consider using an IoC container or other factory to create this, as generally when other viewmodels take extra dependencies it becomes harder to create them all inline. - The
Customersetter doesn't needs to be public, a private one will do. You only want to set the current user from the login and nowhere else, so make that crystal clear. - The invocations of the
ShowLoginModalandHideLoginModaldelegates don't contain a null validation, and will result in aNullReferenceExceptionif no one suscribed to them. A nice shortcut to call them safely is to use theShowLoginModal?(lvm);syntax.
LoginPage viewmodel
- Beware of the async reentrancy in the
LoginCommandmethod. TheVerifyLogincall inside it could potentially be a slow one, but since it's anawaitcall, the UI will remain responsive. That means the user could do other things in the meanwhile, including changing the user/password or even click the login button again! That could have many unanticipated effects. I would counter it by disabling the UI while the verification takes place, using something like this in the VM:public bool EnableForm get; set;
public ICommand LoginCommand => new Command(async () =>
//Disable UI before doing anything
EnableForm = false;
OnPropertyChanged(nameof(EnableForm));
var customer = await VerifyLogin(); // Assume valid login.
LoginSucceeded(this, new LoginSuccessEventArgs(customer));
And in the view bind the controls to that property:<Button Command="Binding LoginCommand" Text="Login" IsEnabled="Binding EnableForm"/> - In the
LoginSuccessEventArgstheCustomerproperty doesn't needs to be read-write. Once again, since you're just informing a value, there is no benefit in changing it after creation. Also, the constructor should check for null on the customer passed. - Possibly it's not shown here or just a work-in-progress thing, but there is no handling of the login failure case, that should just reenable the controls (following my above suggestion), show some error message to the user and not raise the
LoginSuccessevent. - Like in the main page, the raising of the event is not null checked and will blow up if nobody happens to be suscribed.
- The call to the
VerifyLoginmethod, although maybe simplified here, could imply a lot of work under the cover. That's the kind of thing that's better delegated to a service to do all the heavy work, and just have the presentation layer handle the visual thing. I would inject it in the constructor and call it passing the user/password entered, and it would return success or failure. Something along the lines of this:private readonly ILoginService _loginService;
public LoginViewModel(ILoginService loginService)
if(loginService == null) throw new ArgumentNullException(nameof(loginService));
_loginService = loginService;
public ICommand LoginCommand => new Command(async () =>
var customer = await _loginService.VerifyLogin(Username, Password);
if(customer == null)
//show some error message here
else
LoginSucceeded(this, new LoginSuccessEventArgs(customer));
answered Apr 5 at 2:14
Alejandro
28328
28328
I started to use FreshMVVM with FreshIOC. After reading your answer I realized it's better to use an easy framework to understand how it's supposed to be. Everything I have taken with me. Thank you for the long and good answer.
â Jam
Apr 10 at 6:51
add a comment |Â
I started to use FreshMVVM with FreshIOC. After reading your answer I realized it's better to use an easy framework to understand how it's supposed to be. Everything I have taken with me. Thank you for the long and good answer.
â Jam
Apr 10 at 6:51
I started to use FreshMVVM with FreshIOC. After reading your answer I realized it's better to use an easy framework to understand how it's supposed to be. Everything I have taken with me. Thank you for the long and good answer.
â Jam
Apr 10 at 6:51
I started to use FreshMVVM with FreshIOC. After reading your answer I realized it's better to use an easy framework to understand how it's supposed to be. Everything I have taken with me. Thank you for the long and good answer.
â Jam
Apr 10 at 6:51
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191182%2flogin-modal-with-mvvm-pattern%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password