如何在不破坏MVVM的情况下从ViewModel通知View?

时间:2019-05-31 00:03:37

标签: c# mvvm

我最近开始在学校尝试MVVM模式,并且想知道最好的方法(如果有)是从ViewModel通知View,让视图知道在不破坏MVVM的情况下运行方法吗?基本上是让视图知道是否成功,例如尝试登录或尝试连接数据库?

例如登录页面,主窗口仅在成功登录后才将内容更改为新页面,否则,将显示一个消息框

编辑:

我正在使用.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 :(得分:0)

通常,ViewModel通过数据绑定与View通信。 ViewModel可能会公开View绑定到的属性,例如LoginSuccessful。然后,当属性更新时,视图将收到PropertyChanged通知并更改其外观的某些方面。这样做的方式各不相同;例如,XAML中的text属性可以直接绑定到基础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() { }
}

只要在UserViewModel类上更改了Username属性,该文本框就会更新以显示新值。

但是,这种方法并不适用于所有情况。在使用布尔值时,实现数据触发器通常很有用:

<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>

如果在ViewModel上将IsTaken属性设置为true,则此代码扩展了前面的示例,以使文本框的背景变为红色。关于数据触发器的一件好事是它们会自行重置。例如,如果将该值设置为false,则背景将恢复为原始颜色。

如果要采用其他方法,并向ViewModel通知用户输入或类似重要的事件,则可以使用命令。命令可以绑定到XAML中的属性,并由ViewModel实现。当用户执行某些操作(例如单击按钮)时,将调用它们。可以通过实现ICommand接口来创建命令。

答案 1 :(得分:0)

以纯粹的方式,没有指定的框架。

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