在WPF中适当实现MVVM模式 [英] Appropriate implementation of MVVM pattern in WPF

查看:88
本文介绍了在WPF中适当实现MVVM模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大家好,

我创建了一个简单聊天应用使用WPF / WCF,我怀疑是否正确实现了MVVM模式。

主聊天窗口有底层视图模型,它被分配给窗口DataContext属性(标准解决方案,我猜):

Hi all,
I created a Simple Chat Application using WPF/WCF and I have doubts if I correctly implemented MVVM pattern in it.
The main chat window has underlying view model which is assigned to window DataContext property (standard solution, I guess):

class ChatWindowViewModel : INotifyPropertyChanged, IChatCallback
{
    //fields and properties
    public ChatService.ChatClient client;
    InstanceContext instanceContext;
    public bool askToExitApp;

    private User chatUser;
    public User ChatUser
    {
        get { return chatUser; }
        set
        {
            chatUser = value;
            OnPropertyChanged("ChatUser");
        }
    }

    // storage for all logged users, they will be shown in datagrid
    public ObservableCollection<User> AllActiveUsers { get; set; }

    // storage for all sent messages, they will be shown in datagrid
    public ObservableCollection<ChatMessage> AllMessages { get; set; }

    // storage for a message, being typed by user in chat
    public ChatMessage CurrentMessage { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    //implementation of the IChatCallback callback interface
    public void UserSentMessage(ChatMessageDTO msgDTO){ ... }
    public void UserJoined(ChatMessageDTO msgDTO) { ... }
    public void UserLeft(ChatMessageDTO msgDTO) { ... }
    public void IsAlive() { ... }

    //constructor
    public ChatWindowViewModel()
    {
        instanceContext = new InstanceContext(this);
        askToExitApp = true;
        AllActiveUsers = new ObservableCollection<User>();
        AllMessages = new ObservableCollection<ChatMessage>();
        CurrentMessage = new ChatMessage();
        ChatUser = new User { Nickname = string.Empty };
    }

    //commands and everything related to them
    private ICommand chatWindowLoadedCommand;
    public ICommand ChatWindowLoadedCommand { ... }
    private void ChatWindowLoaded() { ... }
    .
    .
}



视图模型中的ChatUser.Nickname属性绑定到相应XAML文件中的主聊天窗口标题,以便用户名当用户登录时,窗口标题栏中会显示。

在服务器端,使用以下合同实现聊天服务:


The ChatUser.Nickname property from the view model is bound to the main chat window title in corresponding XAML file, so that user name is shown in the window title bar when a user logs in.
On the server side, a chat service is implemented with following contracts:

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IChatCallback))]
interface IChat
{
    [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
    void SendMessage(ChatMessageDTO msg);

    [OperationContract(IsOneWay = false, IsInitiating = true, IsTerminating = false)]
    string Join(UserDTO user, out UserDTO[] activeUsers);

    [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true)]
    void Leave();
}



在用户开始聊天之前,他/她/它必须登录。在登录窗口中,昵称可以是指定并将根据已登录用户的昵称进行检查。



为了在ChatWindowViewModel中使用已定义的方法和属性,例如:

- 用ChatUser.Nickname属性绑定登录窗口文本框(用户键入他的昵称)

- 检查是否有任何已登录用户已选择指定的昵称(使用服务加入合同),

我将ChatWindowViewModel分配给登录窗口的DataContext属性:


Before a user can start with chatting, he/she/it has to log in. In the login window a nickname can be specified and it will be checked against nicknames of the logged users.

In order to use already defined methods and properties in ChatWindowViewModel, for example:
- to bind login window textbox (where user types his nickname) with ChatUser.Nickname property
- to check if any logged user has already chosen the specified nickname (using service Join contract),
I assigned ChatWindowViewModel to the DataContext property of the login window:

private void ChatWindowLoaded()
{
    client = new ChatService.ChatClientinstanceContext);
    client.Open();

    bool? ret = true;
    using (LoginDialog loginDlg = new LoginDialog())
    {
        loginDlg.DataContext = this; //!!!! is this ok?
        ret = loginDlg.ShowDialog();
    }

    // in case user do not want to login,
    // close chat window client application
    if (ret == false)
        MainWindowRequestCloseCommand.Execute(null);
}



这样我可以在登录窗口中使用ChatWindowViewModel中定义的功能,例如:


This way I could use functionalites defined in ChatWindowViewModel, within the login window, for example like this:

// memeber of LoginWindow class
     private void DummyHandler(object sender, EventArgs e)
{
    try
    {
        UserDTO[] activeUsers;
        string result = ((ChatWindowViewModel)DataContext).client.Join(((ChatWindowViewModel)DataContext).ChatUser.ToUserDTO(), out activeUsers);
        if (result != "OK")
        {
            MessageBox.Show(result);
            ((ChatWindowViewModel)DataContext).askToExitApp = false;
            DialogResult = false;
        }
        else
        {
            ((ChatWindowViewModel)DataContext).CurrentMessage.Sender = ((ChatWindowViewModel)DataContext).ChatUser;
            foreach (var user in activeUsers.OrderBy(x => x.Nickname).Select(x => x.ToUser()))
                ((ChatWindowViewModel)DataContext).AllActiveUsers.Add(user);
            DialogResult = true;
            Close();
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("Unexpected error occured. Application will be terminated.");
        ((ChatWindowViewModel)DataContext).askToExitApp = false;
        DialogResult = false;
        Close();
    }
}



最后,回到问题:这是在MVVM中做这样的事情的正确方法?



我尝试过的事情:



我或许有点儿在我的描述中有点冗长,为此道歉,我只想解释我试图做的事情。感谢您的评论。


Finally, to get back to the question: Is this a correct way to do something like this in MVVM?

What I have tried:

I was maybe a little bit lengthy in my description, an apology for that, I just wanted to explain what I tried to do. Thanks for the comments.

推荐答案

您所拥有的一些正在朝着正确的解决方案发展。但是,当您在窗口后面的代码中引入硬耦合时,您会突然偏离轨道,您开始直接引用ViewModel的方法和行为。我认为部分问题在于你对MVVM的模型部分实际引用的内容有一个模糊的概念。例如,您有代码要添加到活动用户 - 这是模型的完美候选者(模型不仅仅是数据实体,它们可以是业务逻辑等),所以你可以这样:
Some of what you have is moving towards the correct solution. However you veer suddenly off track when you introduce a hard-coupling in the code behind of the windows where you start to refer directly to methods and behaviours of the ViewModel. Part of the problem I think is that you have a fuzzy idea of what the model part of MVVM actually refers to. For instance, you have code to add to the active users - this is a perfect candidate for a model (models aren't just data entities, they can be business logic, etc), so you could have something like this:
private IUserDetails _userDetails;

public ChatWindowViewModel(IUserDetails userDetails)
{
  _userDetails = userDetails;
}

private void AddUser()
{
  CurrentMessage.Sender = ChatUser;
  foreach (var activeUser in activeUsers)
  {
    var user = _userDetails.GetFromNickname(activeUser.Nickname);
    if (user == null) continue;
    AllActiveUsers.Add(user);
  }
}

然后,你会把它作为一个命令挂钩到OK按钮。

Then, you would look to hook this up to the OK button as a Command.


这篇关于在WPF中适当实现MVVM模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆