使用套接字时,viewmodel中的C#PorpertyChanged为空

时间:2018-10-01 15:50:23

标签: c# wpf mvvm asyncsocket

我是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函数内部,以触发适当的事件。

1 个答案:

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