Как уведомить View от ViewModel, не ломая MVVM? - PullRequest
0 голосов
/ 31 мая 2019

Я недавно начал опробовать паттерн MVVM в школе, и мне было интересно, как лучше всего (если таковой имеется) уведомить View из ViewModel, чтобы он знал, как запустить метод, не нарушая MVVM? В основном, позволяя представлению узнать, было ли что-то успешным, например, попытка входа в систему или попытка подключения к базе данных?

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

Edit:

Я использую .NET

Что я пробовал до сих пор:

Вид:

<Page
      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:View.Pages" xmlns:ViewModels="clr-namespace:ViewModel.ViewModels;assembly=ViewModel" x:Class="View.Pages.Start_Page"
      mc:Ignorable="d" 
      d:DesignHeight="720" d:DesignWidth="1280"
      Title="Start_Page">

    <Page.DataContext>
        <ViewModels:Start_Page_ViewModel/>
    </Page.DataContext>

Код, стоящий за этим:

public Start_Page()
        {
            InitializeComponent();

            Start_Page_ViewModel currentDataContext = DataContext as Start_Page_ViewModel;

            currentDataContext.CurrentUserIDGotten += GoToMenu;
        }

        private void GoToMenu(int result)
        {
            if (result == -1)
            {
                MessageBox.Show("User credentials incorrect");
            }
            else if (result == -2)
            {
                MessageBox.Show("Connection failed");
            }
            else
            {
                Application.Current.MainWindow.Content = new Menu_Page();
            }
        }

ViewModel:

public class Start_Page_ViewModel
    {

        private string userName;
        private string userPassword;

        public string UserName { get => userName; set => userName = value; }

        public string UserPassword { get => userPassword; set => userPassword = value; }

        private RelayCommand logIn;

        public RelayCommand LogIn => logIn;


        public delegate void CurrentUserIDGottenEventHandler(int result);
        public event CurrentUserIDGottenEventHandler CurrentUserIDGotten;

        public Start_Page_ViewModel()
        {
            logIn = new RelayCommand(LogInToProgram, CanLogIn);
        }

        public void LogInToProgram(object o)
        {
            PasswordBox passwordBox = o as PasswordBox;

            ViewModelController.Instance.CurrentUserID = Database_Controller.Instance.SignIn(userName, passwordBox.Password);

            OnUserIDGotten(ViewModelController.Instance.CurrentUserID);
        }

        public bool CanLogIn(object o)
        {
            if (userName != null)
            {
                return true;
            }
            return false;
        }

        protected virtual void OnUserIDGotten(int result)
        {
            if (CurrentUserIDGotten != null)
            {
                CurrentUserIDGotten(result);
            }
        }
    }

Ответы [ 2 ]

0 голосов
/ 31 мая 2019

Чистым способом, без указанной структуры.

  1. Создание делегата события (или интерфейса слушателя), связывание с моделью представления
  2. Регистрация обработчика события в представлении
  3. Запустить событие при изменении модели представления

Нравится это

using System;

public class MainClass {
  public static void Main (string[] args) {
    ViewModel m = new ViewModel();
    View v = new View();
    v.Model = m;
    m.MakeSomeChange();
  }
}

public class View {
  private IViewModel _model;
  public IViewModel Model {
    get {
      return _model;
    }
    set {
      if(_model != null) {
        _model.OnChanged -= OnChanged;
      }
      if(value != null) {
        value.OnChanged += OnChanged;
      }
      _model = value;
    }
  }
  private void OnChanged(){
    //update view
    Console.WriteLine ("View Updated");
  }
}

public delegate void ViewChangeDelegate();

public interface IViewModel {
  event ViewChangeDelegate OnChanged;
}

public class ViewModel: IViewModel {
  public event ViewChangeDelegate OnChanged;

  public void MakeSomeChange() {
    //make some change in the view Model
    OnChanged.Invoke();
  }
}
0 голосов
/ 31 мая 2019

Обычно ViewModel связывается с View через привязки данных.ViewModel может предоставлять свойство, например LoginSuccessful, к которому привязан View.Затем, когда свойство обновляется, представление получит уведомление PropertyChanged и изменит некоторые аспекты его внешнего вида.Как это будет происходить, меняется;например, текстовое свойство в XAML может быть привязано непосредственно к базовому свойству ViewModel:

<TextBox Text="{Binding Source={StaticResource UserViewModel}, Path=Username}"/>

ViewModel может выглядеть следующим образом:

public class UserViewModel : INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;

    public string Username {
        get { return _username; }
        set {
            _username = value;
            PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Username"));
        }
    }
    private string _username;

    public UserViewModel() { }
}

Всякий раз, когда свойство Username изменяется наКласс UserViewModel, текстовое поле будет обновляться для отображения нового значения.

Однако этот подход работает не для всех ситуаций.При работе с логическими значениями часто полезно реализовать триггеры данных:

<TextBox Text="{Binding Source={StaticResource UserViewModel}, Path=Username}">
    <TextBlock.Style>
        <Style TargetType="{x:Type TextBox}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Source={StaticResource UserViewModel}, Path=IsTaken}" Value="True">
                            <Setter Property="Background" Value="Red"></Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBlock.Style>
</TextBox>

Этот код расширяет предыдущий пример и окрашивает фон текстового поля в красный цвет, если для свойства IsTaken установлено значение true в ViewModel.Хорошая вещь о триггерах данных состоит в том, что они сбрасывают себя;например, если для этого параметра установлено значение false, фон вернется к своему первоначальному цвету.

Если вы хотите пойти другим путем и уведомить ViewModel о вводе пользователем или аналогичном важном событии, вы можете использоватькоманды.Команды могут быть привязаны к свойствам в XAML и реализованы с помощью ViewModel.Они вызываются, когда пользователь выполняет определенное действие, такое как нажатие кнопки.Команды могут быть созданы путем реализации интерфейса ICommand.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...