我是C#和MVVM的新手,所以我可能做错了什么。 我创建了一个窗口(称为LoginMenu),并添加了一个用于聊天的userControl,占用了窗口的1/3。然后,我为该聊天userControl创建了ViewModel和模型。当我的聊天模型收到来自客户端的任何消息时,套接字将对其进行更新。看来我的对话字段在模型中正在更新,但是我的ChatViewModel中的PropertyChanged为空。 在开始使用套接字之前,我的对话文本框已正确更新。 我在网上阅读这可能是因为我的UI线程未与套接字在同一线程上运行而未得到更新。这感觉不太可能,因为即使我的ChatViewModel也没有得到正确的PropertyChanged事件。
以下是一些代码段: LoginMenu.xaml:
<Window.DataContext>
<viewmodels:LoginMenuVM />
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Name="mainMenuOnlineTemplate" DataType="{x:Type viewmodels:MainMenuOnlineVM}">
<views:MainMenuOnline DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="chatTemplate" DataType="{x:Type viewmodels:ChatVM}">
<views:Chat DataContext="{Binding}"/>
</DataTemplate>
</Window.Resources>
...
<views:Chat Grid.Column="1"></views:Chat>
LoginMenu.xaml.cs:
public partial class LoginMenu : Window
{
public LoginMenu()
{
InitializeComponent();
this.DataContext = new LoginMenuVM();
}
}
LoginMenuViewModel:
public class LoginMenuVM : ViewModelBase
{
private SocketService socketService = new SocketService();
private User user = new User();
private ChatVM chatVM = new ChatVM();
...
public void ConnectUser(object obj)
{
if (NameIsIncorrect())
{
MessageBox.Show("Username is incorrect!");
return;
}
else
{
AssignName(potentialName);
socketService.Start(ipAdress);
try
{
string authentification_informations = user.Name;
socketService.SendDemand(authentification_informations);
{
chatVM.connectSocket(socketService, user);
} catch (Exception ex)
{
}
}
}
Chat.xaml:
<UserControl.DataContext>
<viewmodels:ChatVM />
</UserControl.DataContext>
<DockPanel Background="White">
<TextBlock DockPanel.Dock="Top" x:Name="name" Text="Name" Background="LightGray" />
<TextBox DockPanel.Dock="Bottom" Height="50" Name="messageEntry" Text="{Binding ChatBoxMessage, UpdateSourceTrigger=PropertyChanged}" >
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding SendMessageCommand}" CommandParameter="{Binding Path=Text, RelativeSource={RelativeSource AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
</TextBox>
<TextBlock x:Name="Conversation" Text="{Binding Path=Conversation, Mode=TwoWay}" />
</DockPanel>
ChatViewModel:
public class ChatVM : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private static SocketService socketService;
private static User user;
private static Chat chat;
public string Conversation
{
get { return chat.Conversation; }
set { NotifyPropertyChanged(); }
}
private string _chatBoxMessage = "Enter Message";
public string ChatBoxMessage
{
get { return _chatBoxMessage; }
set
{
_chatBoxMessage = value;
NotifyPropertyChanged("ChatBoxMessage");
}
}
public RelayCommand<object> SendMessageCommand { get; set; }
public ChatVM()
{
chat = new Chat();
SendMessageCommand = new RelayCommand<object>(SendMessage);
}
public void SendMessage(object obj)
{
if (socketService != null) {
if (!string.IsNullOrWhiteSpace(ChatBoxMessage))
{
socketService.SendDemand(user.Name + ":" + ChatBoxMessage);
MessageBox.Show(Conversation);
}
else {
MessageBox.Show("You can't send empty or only white space messages.");
}
}
else {
MessageBox.Show("You can't send messages since you're not connected.");
}
}
public void connectSocket (SocketService socketServiceTemp, User userTemp)
{
user = userTemp;
socketService = socketServiceTemp;
chat = socketService.GetChat();
chat.PropertyChanged += Conversation_CollectionChanged;
}
private void Conversation_CollectionChanged(object sender, PropertyChangedEventArgs e)
{
Conversation = chat.Conversation;
}
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (PropertyChanged != null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Chat.cs:
public class Chat : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _conversation = "Test";
public string Conversation
{
get { return _conversation; }
set
{
_conversation = value;
NotifyPropertyChanged();
}
}
private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (PropertyChanged != null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
SocketService.cs:
private void TreatAnswer(IAsyncResult ar)
{
int messageReceivedInInt = socket.EndReceive(ar);
//Transform Bytes received to string
App.Current.Dispatcher.BeginInvoke((Action)delegate
{
chat.Conversation += messageReceived;
Thread.Sleep(100);
});
Thread.Sleep(100);
socket.BeginReceive(byteMessage, 0, 2048, SocketFlags.None, TreatAnswer, socket);
}
我尝试使用
App.Current.Dispatcher.BeginInvoke((Action)delegate
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
Thread.Sleep(100);
});
在Chat.cs NotifyPropertyChanged和ChatViewModel NotifyPropertyChanged中。
如上所述,当我向ChatViewModel的NotifyPropertyChanged添加一个断点时,PropertyChanged为null。无论如何,我可以使此代码正常工作吗?这感觉像是一个小错误,但我找不到。
编辑:
我发现了问题。我不得不打电话给:
chat.PropertyChanged += new PropertyChangedEventHandler(Conversation_CollectionChanged);
在我的ChatVM的SendMessage函数内部,以触发适当的事件。
答案 0 :(得分:1)
您没有告诉UI聊天模板要使用哪个ChatVM实例
LoginMenuViewModel.cs
private ChatVM chatVM = new ChatVM();
Public ChatVM ChatVMProperty // We need a property to bind
{
get { return chatVM; }
set { chatVM = value;
/* Call Notify Property Changed if
you are assigning after constructor
getting called */
}
}
LoginMenu.xaml
<DataTemplate x:Name="chatTemplate" DataType="{x:Type viewmodels:ChatVM}">
<views:Chat DataContext="{Binding ChatVMProperty}"/>
</DataTemplate>