问题描述
大家好,
我创建了一个使用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.
推荐答案
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模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!