0

Начал изучать паттерн MVVM для использования в программе. В частности, сейчас есть пустой каркас MainWindow с ContentPresenter и две View - AuthView, MainView. Соответственно есть и ViewModel каждой. При старте программы в окно сразу загружается AuthView (скриншот ниже).

Форма авторизации

Желаемый результат - по нажатию кнопки выполняется обращение к серверу, получение ответа. Если ответ положительный - эти элементы исчезают и сюда грузится MainView. И что-то я совсем запутался, где разместить эту функцию взаимодействия с сервером, и как при получении именно положительного ответа в функции сменить View. Весь код приведен далее. Подскажите, пожалуйста, как добиться этого результата? То, что есть, сделал по различным статьям и примерам реализации, ещё разобрался слабо, так что заранее извиняюсь за ошибки, буду рад, если укажете на них.

Помогите, пожалуйста!

MainWindow.xaml

    <Window x:Class="QRScanner.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:QRScanner"
        mc:Ignorable="d"
        Title="QRSCanner" Height="450" Width="800">
    <Grid>
        <ContentPresenter x:Name="OutputView" Margin="5"/>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using QRScanner.ViewModels;
using QRScanner.Views;

namespace QRScanner
{
    public interface IMainWindowsCodeBehind
    {
        /// <summary>
        /// Показ сообщения для пользователя
        /// </summary>
        /// <param name="message">текст сообщения</param>
        void ShowMessage(string message);

        /// <summary>
        /// Загрузка нужной View
        /// </summary>
        /// <param name="view">экземпляр UserControl</param>
        void LoadView(ViewType typeView);
    }

    public enum ViewType
    {
        Authorization,
        Main
    }

    public partial class MainWindow : Window, IMainWindowsCodeBehind
    {
        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += MainWindow_Loaded;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            //загрузка стартовой View
            LoadView(ViewType.Authorization);
        }

        public void LoadView(ViewType typeView)
        {
            switch (typeView)
            {
                case ViewType.Authorization:
                    //загружаем вьюшку, ее вьюмодель
                    AuthView view = new AuthView();
                    AuthViewModel vm = new AuthViewModel(this);
                    //связываем их м/собой
                    view.DataContext = vm;
                    //отображаем
                    this.OutputView.Content = view;
                    break;
                case ViewType.Main:
                    MainView viewF = new MainView();
                    MainViewModel vmF = new MainViewModel(this);
                    viewF.DataContext = vmF;
                    this.OutputView.Content = viewF;
                    break;
            }
        }

        public void ShowMessage(string message)
        {
            MessageBox.Show(message);
        }
    }
}

AuthView.xaml

    <UserControl x:Class="QRScanner.Views.AuthView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:QRScanner.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.4*"></ColumnDefinition>
            <ColumnDefinition Width="0.6*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition Height="0.3*"></RowDefinition>
            <RowDefinition Height="0.3*"></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Label x:Name="login_lbl" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="10 0" Content="Логин:"></Label>
        <TextBox x:Name="login_box" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="10 0" Width="200"></TextBox>
        <Label x:Name="pass_lbl" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="10 0" Content="Пароль:"></Label>
        <TextBox x:Name="pass_box" Grid.Column="1" Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10 0" Width="200"></TextBox>
        <Label x:Name="entry_status" Grid.ColumnSpan="2" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center" Content="{Binding Auth_status}"></Label>
        <Button x:Name="authorize" Grid.ColumnSpan="2" Grid.Row="3" Command="{Binding LoadMainViewCommand, Mode=OneTime}" CommandParameter="{Binding Auth_status}" HorizontalAlignment="Center" VerticalAlignment="Top" Padding="30 10" Content="Войти" Click="authorize_Click"></Button>
    </Grid>
</UserControl>

AuthView.xaml.cs

    using QRScanner.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace QRScanner.Views
{
    /// <summary>
    /// Логика взаимодействия для AuthView.xaml
    /// </summary>
    public partial class AuthView : UserControl
    {
        public AuthView()
        {
            InitializeComponent();
        }

        private void authorize_Click(object sender, RoutedEventArgs e)
        {
            if (login_box.Text == "1")
                entry_status.Content = "OK";
            else
                entry_status.Content = "NO";
        }
    }
}

AuthViewModel.cs

   using QRScanner.ViewModel;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QRScanner.ViewModels
{
    class AuthViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        private IMainWindowsCodeBehind _MainCodeBehind;
        private string auth_status;

        public AuthViewModel(IMainWindowsCodeBehind codeBehind)
        {
            if (codeBehind == null) throw new ArgumentNullException(nameof(codeBehind));

            _MainCodeBehind = codeBehind;
        }

        /// <summary>
        /// Переход к главному отображению
        /// </summary>
        private RelayCommand _LoadMainViewCommand;
        public RelayCommand LoadMainViewCommand
        {
            get
            {
                return _LoadMainViewCommand = _LoadMainViewCommand ??
                  new RelayCommand(OnLoadMainView, CanLoadMainView);
            }
        }
        private bool CanLoadMainView()
        {
            return true;
        }
        private void OnLoadMainView()
        {
            _MainCodeBehind.LoadView(ViewType.Main);
        }
    }
}

MainView.xaml

    <UserControl x:Class="QRScanner.Views.MainView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:QRScanner.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Label x:Name="test" Content="Hello!"></Label> 
    </Grid>
</UserControl>

MainViewModel.cs

using QRScanner.ViewModel;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QRScanner.ViewModels
{
    public class MainViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        private IMainWindowsCodeBehind _MainCodeBehind;

        public MainViewModel(IMainWindowsCodeBehind codeBehind)
        {
            if (codeBehind == null) throw new ArgumentNullException(nameof(codeBehind));

            _MainCodeBehind = codeBehind;
        }
    }
}
  • Странное у вас тут только то, что вы обращаетесь к View элементам из VM слоя, что является грубым нарушением MVVM (ибо слой View должен быть совершенно независим от чего либо из слоя VM), уберите все x:Name и работайте с привязками. Также у кнопки authorize у вас зачем то идет привязка к команде и событию Click, оставьте что то одно (в строгом MVVM события по типу Click являются нарушением). По поводу самого вопроса, мне лично он не понятен. Просто сделайте метод, который будет отправлять на сервер данные и выводить нужную информацию исходя из ответа и вызывайте его в команде логина. – EvgeniyZ Feb 25 '19 at 13:56
  • Исходя из ответа сервера мне нужно загружать MainView или оставлять AuthView, и я не знаю как реализовать именно это. Я только начал разбираться с паттерном. Если делать метод, то где именно - в AuthViewModel? И как реализовать загрузку другой View в случае положительного ответа сервера? – Ярослав Оверченко Feb 25 '19 at 18:04
  • MVVM - это разделение всего на 3 слоя (Model/ViewModel/View), где Model отвечает за данные, View за UI, а ViewModel за связь этих двух слоев. Исходя из этого, правильней будет создать модель взаимодействия с сервером (отправка/получение данных) и в AuthViewModel взаимодействовать с ней. Если же это всего пару строк кода, то модель можно не создавать и в самом AuthViewModel реализовать работу с сервером. Отвечая на "Как реализовать загрузку другой View" - примерно так. – EvgeniyZ Feb 25 '19 at 18:22
  • Нашел в закромах своих такой ответ, в нем я подробно описывал процесс смены "страницы" после "псевдо" успешной авторизации. Вам лишь остается переписать метод Authorize() для работы с сервером. – EvgeniyZ Feb 25 '19 at 19:54
  • Огромное спасибо, @evgeniyz! Это то, что я искал и очень поможет разобраться в паттерне. Я использовал код из ваших "закромов". Но далее в приложении будет ещё много чего, поможете, если появятся ещё вопросы?=) – Ярослав Оверченко Feb 25 '19 at 21:40
  • Без проблем, обращайтесь если будут вопросы. Этот вопрос я думаю мы тогда закроем как дубликат. – EvgeniyZ Feb 25 '19 at 21:43
  • Здравствуйте, @EvgeniyZ! Возник вопрос касаемо этого же приложения. Если опираться на тот Ваш ответ, который мне помог, то как сделать, чтобы например по кнопке, размещенной в ContentView, программа возвращалась к странице авторизации? Пробовал это сделать по типу события OnAuthorize, создал подобный существующему классу LoginEventArgs. Соответственно, по той же логике добавляю в конструктор MainViewModel экземпляр ContentViewModel и вешаю функцию на его событие по типу OnAuthorize. Переход работает, но обратный переход со страницы логина на основную не происходит - зависает. – Ярослав Оверченко Mar 09 '19 at 17:31

0 Answers0