WPF c # Как я могу переключиться на просмотр панели мониторинга после успешной попытки входа в систему? Вот m code

#c# #wpf #binding #command #desktop

#c# #wpf #привязка #команда #Для рабочего стола

Вопрос:

Вот мой xaml-код. Я хочу обновить текущее представление после успешной попытки входа в систему.

 <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();
        }
    }
}
  

Здесь, когда пользователь входит в систему, он должен обновить текущее представление, он выполняет команду, также я получаю значение в параметре command, и он также обновляет свойство CurrentView в MainViewModel, но проблема в том, что он не обновляет пользовательский интерфейс, который не отображается в представлении…

 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 emptyn");
        }
        if (string.IsNullOrWhiteSpace(Password))
        {
            isValid = false;
            ErrorMessages.Append("Password cannot be emptyn");
        }
        return isValid;
    }
    private void ClearProperties()
    {
        Email = Password = null;
    }
  

Ответ №1:

Это не работает, поскольку вы создаете новый экземпляр MainViewModel после успешного входа в систему. Это не тот экземпляр, который является DataContext вашим представлением.

Вы могли бы передать ссылку на MainViewModel экземпляр, например, через конструктор в LoginViewModel . Но поскольку LoginViewModel на самом деле это не зависит от MainViewModel , я бы не стал этого делать.
Вместо этого я предлагаю одно из двух следующих решений. Как правило, ваши модели просмотра страниц не должны заботиться о навигации по контенту. За это должна отвечать только родительская модель просмотра навигации, которая уже знает детали навигации, такие как текущая страница или следующая страница и т.д. Оба примера следуют этой идее.

Также обратите внимание, что, поскольку вы создаете новые модели просмотра страниц каждый раз, когда пользователь переходит, вы потеряете все содержимое. При возврате на предыдущую страницу будет показана пустая начальная страница. Также switch очень плох с точки зрения расширяемости. Добавление новых страниц не очень приятно и разрушило бы ваш UpdateCurrentView метод.

Я переработал ваш код, чтобы показать простой способ сделать навигацию по странице простой и расширяемой. Это всего лишь небольшие изменения: добавьте enum для замены строк, чтобы включить поддержку Intellisense и проверки типов во время компиляции, и добавьте Dictionary для замены 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
          amp;amp; 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>
  

Решение 1: LoginSuccessful Событие

Вы можете предоставить LoginSuccessful событие, которое может прослушивать модель представления навигации:

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
          amp;amp; 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();
    }
}
  

Решение 2: Обратный вызов продолжения

Или принудительно включить режим просмотра навигации для обеспечения обратного вызова продолжения через конструктор:

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
          amp;amp; 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();
    }
}