简体   繁体   中英

WPF c# How can I switch to dashboard view after successfull login attemp? Here's m code

Here is my xaml code. I want to update the current view after the successfull login attempt.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Button Content="Login Page" 
            Command="{Binding UpdateCurrentViewModelCommand}"
            CommandParameter="LoginView"/>
    <Button Content="Register Page" 
            Command="{Binding UpdateCurrentViewModelCommand}"
            CommandParameter="RegisterView"
            Grid.Row="1"/>
    <ContentControl Content="{Binding CurrentView}" Grid.Row="2"/>
</Grid>
public class MainViewModel : BaseViewModel
{
    private BaseViewModel _currentView = new LoginViewModel();
    public BaseViewModel CurrentView
    {
        get
        {
            return _currentView;
        }
        set
        {
            _currentView = value;
            OnPropertyChanged(nameof(CurrentView));
        }
    }
    public ICommand UpdateCurrentViewModelCommand { get; set; }
    public MainViewModel()
    {
        UpdateCurrentViewModelCommand = new RelayCommand(UpdateCurrentView);
    }

    private void UpdateCurrentView(object obj)
    {
        if (obj.ToString() == "LoginView")
        {
            CurrentView = new LoginViewModel();
        }
        else if (obj.ToString() == "RegisterView")
        {
            CurrentView = new RegisterViewModel();
        }
        else if (obj.ToString() == "DashboardView")
        {
            CurrentView = new DashboardViewModel();
        }
    }
}

Here when user logs in it should update the current view, it is executing the command also I am getting the value in command parameter and it also updating the property CurrentView in MainViewModel but the problem is, it is not updating the UI the view is not displaying...

public class LoginViewModel : BaseViewModel
{
    private string _email;
    public string Email
    {
        get
        {
            return _email;
        }
        set
        {
            _email = value;
            OnPropertyChanged(nameof(Email));
        }
    }
    private string _password;
    public string Password
    {
        get
        {
            return _password;
        }
        set
        {
            _password = value;
            OnPropertyChanged(nameof(Password));
        }
    }
    public ICommand LoginCommand { get; set; }
    private StringBuilder ErrorMessages { get; set; } = new StringBuilder();
    public LoginViewModel()
    {
        LoginCommand = new RelayCommandAsync(async (para) => await LoginUser(para));
    }
    private async Task LoginUser(object para)
    {
        SqlConnector sqlConnector = new SqlConnector();
        if (ValidateForm() == false)
        {
            MessageBox.Show(ErrorMessages.ToString());
            return;
        }
        User user = await sqlConnector.FindUserByEmail(Email);
        if (user == null)
        {
            MessageBox.Show("Incorrect username or password");
            return;
        }
        IPasswordHasher passwordHasher = new PasswordHasher();
        var passwordResult = passwordHasher.VerifyHashedPassword(user.PasswordHash, Password);
        if (passwordResult == PasswordVerificationResult.Success)
        {
            MessageBox.Show("Login success.");
            //here is the problem...I am telling my MainViewModel's CurrentView property to update 
            but it's not listening to me. 
                //new MainViewModel().UpdateCurrentViewModelCommand.Execute("DashboardView");
            new MainViewModel().CurrentView = new DashboardViewModel();
        }
        else
        {
            MessageBox.Show("Incorrect username or password");
        }
        ClearProperties();
    }
    private bool ValidateForm()
    {
        ErrorMessages.Clear();
        bool isValid = true;
        if (string.IsNullOrWhiteSpace(Email))
        {
            isValid = false;
            ErrorMessages.Append("Email cannot be empty\n");
        }
        if (string.IsNullOrWhiteSpace(Password))
        {
            isValid = false;
            ErrorMessages.Append("Password cannot be empty\n");
        }
        return isValid;
    }
    private void ClearProperties()
    {
        Email = Password = null;
    }

This is not working as you are creating a new instance of MainViewModel after the successful login. This is not the instance that is the DataContext of your view.

You could pass a reference to the MainViewModel instance eg, via the constructor to the LoginViewModel . But since the LoginViewModel doesn't really depend on the MainViewModel I wouldn't do that.
Instead I suggest one of the two following solutions. Generally your page view models shouldn't care about the content navigation. This should be only responsibility of the parent navigation view model, that already knows navigation details like current page or next page etc. Both examples follow this idea.

Also note that since you are creating new page view models every time the user navigates, you will lose all the content. Coming back to a previous page would show a blank initial page. Also the switch is very bad in terms of extensibility. Adding new pages is not very nice and would blow your UpdateCurrentView method.

I refactored your code to show an easy way to keep the page navigation simple and extensible. This are only small changes: Add an enum to replace strings in order to enable Intellisense and compiletime type checking support and add a Dictionary to replace the switch :

// Enum used to identify the requested page e.g as CommandParameter
public enum PageId
{
   None = 0, LoginView, RegisterView, DashboardView
}

// Use a Dictionary to navigate to content based on the PageId enum
public class MainViewModel : BaseViewModel
{
    private Dictionary<PageId, BaseViewModel> Pages { get; set; }

    public MainViewModel()
    {
        this.Pages = new Dictionary<PageId, BaseViewModel>
        {
            { PageId.LoginView, new LoginViewModel() },
            { PageId.RegisterView, new RegisterViewModel() },
            { PageId.DashboardView, new DashboardViewModel() }
       };
    }

    private void UpdateCurrentView(object commandParameter)
    {
        if (commandParameter is PageId pageId
          && this.Pages.TryGetValue(pageId, out BaseViewModel nextPage))
        {
            this.CurrentView = nextPage;
        }
    }
}

<!-- Modified view to use the enum -->
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Button Content="Login Page" 
            Command="{Binding UpdateCurrentViewModelCommand}"
            CommandParameter="{x:Static PageId.LoginView}"/>
    <Button Content="Register Page" 
            Command="{Binding UpdateCurrentViewModelCommand}"
            CommandParameter="{x:Static PageId.RegisterView}"
            Grid.Row="1"/>
    <ContentControl Content="{Binding CurrentView}" Grid.Row="2"/>
</Grid>

Solution 1: LoginSuccessful Event

You can expose a LoginSuccessful event, that the navigation view model can listen to:

MainViewModel.cs

public class MainViewModel : BaseViewModel
{
    private Dictionary<PageId, BaseViewModel> Pages { get; set; }

    public MainViewModel()
    {
        this.Pages = new Dictionary<PageId, BaseViewModel>
        {
            { PageId.RegisterView, new RegisterViewModel() },
            { PageId.DashboardView, new DashboardViewModel() }
       };
    
       var loginView = new LoginViewModel();
       loginView.LoginSuccessful += OnLoginSuccessful;
       this.Pages.Add(PageId.LoginView, loginView);
    }

    private void UpdateCurrentView(object commandParameter)
    {
        if (commandParameter is PageId pageId
          && this.Pages.TryGetValue(pageId, out BaseViewModel nextPage))
        {
            this.CurrentView = nextPage;
        }
    }

    private void OnLoginSuccessful(object sender, EventArgs e)
    {
        var loginViewModel = sender as LoginViewModel;
        loginViewModel.LoginSuccessful -= OnLoginSuccessful;

        UpdateCurrentView(PageId.LoginView);
    }
}

LoginViewModel.cs

public class LoginViewModel : BaseViewModel
{  
    public event EventHandler LoginSuccessful;
    private void OnLoginSuccessful() => this.LoginSuccessful?.Invoke(this, EventArgs.Empty);

    private async Task LoginUser(object para)
    {
        SqlConnector sqlConnector = new SqlConnector();
        if (ValidateForm() == false)
        {
            MessageBox.Show(ErrorMessages.ToString());
            return;
        }
        User user = await sqlConnector.FindUserByEmail(Email);
        if (user == null)
        {
            MessageBox.Show("Incorrect username or password");
            return;
        }
        IPasswordHasher passwordHasher = new PasswordHasher();
        var passwordResult = passwordHasher.VerifyHashedPassword(user.PasswordHash, Password);
        if (passwordResult == PasswordVerificationResult.Success)
        {
            MessageBox.Show("Login success.");
            OnLoginSuccessful();
        }
        else
        {
            MessageBox.Show("Incorrect username or password");
        }
        ClearProperties();
    }
}

Solution 2: Continuation Callback

Or force the navigation view mode to provide a continuation callback via the constructor:

MainViewModel.cs

public class MainViewModel : BaseViewModel
{
    private Dictionary<PageId, BaseViewModel> Pages { get; set; }

    public MainViewModel()
    {
        this.Pages = new Dictionary<PageId, BaseViewModel>
        {
            { PageId.LoginView, new LoginViewModel(() => UpdateCurrentView(PageId.LoginView)) },
            { PageId.RegisterView, new RegisterViewModel() },
            { PageId.DashboardView, new DashboardViewModel() }
       };
    }

    private void UpdateCurrentView(object commandParameter)
    {
        if (commandParameter is PageId pageId
          && this.Pages.TryGetValue(pageId, out BaseViewModel nextPage))
        {
            this.CurrentView = nextPage;
        }
    }
}

LoginViewModel.cs

public class LoginViewModel : BaseViewModel
{
    public Action LoginSuccessfulContinuation { get; set; }

    // Constructor
    public LoginViewModel(Action loginSuccessfulContinuation) => this.LoginSuccessfulContinuation = loginSuccessfulContinuation; 

    private async Task LoginUser(object para)
    {
        SqlConnector sqlConnector = new SqlConnector();
        if (ValidateForm() == false)
        {
            MessageBox.Show(ErrorMessages.ToString());
            return;
        }
        User user = await sqlConnector.FindUserByEmail(Email);
        if (user == null)
        {
            MessageBox.Show("Incorrect username or password");
            return;
        }
        IPasswordHasher passwordHasher = new PasswordHasher();
        var passwordResult = passwordHasher.VerifyHashedPassword(user.PasswordHash, Password);
        if (passwordResult == PasswordVerificationResult.Success)
        {
            MessageBox.Show("Login success.");
            this.LoginSuccessfulContinuation?.Invoke();
        }
        else
        {
            MessageBox.Show("Incorrect username or password");
        }
        ClearProperties();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM