С# PorpertyChanged в модели представления имеет значение null при использовании сокетов

Я новичок в С# и MVVM, поэтому могу делать что-то не так. Я создал окно (называемое LoginMenu) и добавил userControl для чата, занимающего 1/3 окна. Затем я создал ViewModel и модель для этого пользовательского элемента управления Chat. Моя модель чата обновляется сокетом, когда он получает любое сообщение, приходящее от клиента. Похоже, что мое поле «Разговор» обновляется в модели, но PropertyChanged в моей модели ChatViewModel имеет значение null. Текстовое поле моего разговора обновлялось должным образом, прежде чем я начал использовать сокеты. Я читал в Интернете, что это может быть связано с тем, что мой поток пользовательского интерфейса не обновлялся, поскольку он не работает в том же потоке, что и мой сокет. Это кажется маловероятным, поскольку даже моя модель 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>

ЛогинМеню.xaml.cs :

public partial class LoginMenu : Window
{

    public LoginMenu()
    {
        InitializeComponent();
        this.DataContext = new LoginMenuVM();
    }

}

ЛогинМенюВиМодел:

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)
            {
            }
        }
    }

Чат.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>

ЧатВиевМодель:

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

Чат.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.

Как упоминалось выше, когда я добавляю точку останова в NotifyPropertyChanged модели ChatViewModel, PropertyChanged имеет значение null. Могу ли я заставить этот код работать? Это похоже на небольшую ошибку, но я не могу ее найти.

РЕДАКТИРОВАТЬ :

Я нашел проблему. Я должен был позвонить:

chat.PropertyChanged += new PropertyChangedEventHandler(Conversation_CollectionChanged);

Внутри функции SendMessage моего ChatVM для запуска соответствующего события.


person tilabb    schedule 01.10.2018    source источник
comment
Небольшой совет: старайтесь не использовать слишком много static-переменных. Они могут создавать странные проблемы, когда вы работаете с несколькими потоками. Каждый объект должен иметь свои поля и не делиться ими с другими объектами.   -  person akop    schedule 02.10.2018


Ответы (1)


Вы не сообщаете пользовательскому интерфейсу, какой экземпляр ChatVM будет использоваться шаблоном чата.

ЛогинМенювиевмодел.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 */ 
        }
}

ЛогинМеню.xaml

<DataTemplate x:Name="chatTemplate" DataType="{x:Type viewmodels:ChatVM}">
    <views:Chat DataContext="{Binding ChatVMProperty}"/>
</DataTemplate>
person VibeeshanRC    schedule 02.10.2018