The text in TextBlock doesn't change

73 views Asked by At

I have two programs: client (using WPF) and server (console app). The server can work with two threads. Client sends to server '1' or '2', then the server sends '3' or '4' to it. Client gets the answer successfully but shows '1' or '2' in TextBlock (outdated) because of the threads. The variable "result" is always "\0" and "PropertyChanged" in Client is always null.

Server:

using System.Text;
using System.Net.Sockets;
using System.Net;

namespace Server
{
    internal class ServerHandler
    {
        //private const string _IP = "127.0.0.1";
        private const int _PORT = 8888;
        private const int _BUFFER_SIZE = 1;
        protected internal TcpListener _Listener { get; set; }

        protected internal async Task ListenAsync()
        {
            try
            {
                //IPAddress localAddr = IPAddress.Parse(_IP);
                _Listener = new TcpListener(IPAddress.Any, _PORT);
                _Listener.Start();

                while (true)
                {
                    Console.WriteLine("oK");
                    TcpClient tcpClient = await _Listener.AcceptTcpClientAsync();
                    ClientObject clientObject = new ClientObject(tcpClient);
                }

                //ClientHandler(_Listener.AcceptTcpClient());
            }
            catch (SocketException ex)
            {
                throw new Exception(ex.Message);
            }
        }

        ~ServerHandler()
        {
            if (_Listener != null)
                _Listener.Stop();
        }

        static void Main(string[] args)
        {
            try
            {
                ServerHandler handler = new();
                handler.ListenAsync();
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        internal class ClientObject
        {
            protected internal TcpClient _Client { get; set; }
            protected internal NetworkStream _Stream { get; set; }
            public ClientObject(TcpClient client)
            {
                _Client = client;
                byte[] buffer = new byte[_BUFFER_SIZE];
                _Stream = _Client.GetStream();
                _Stream.BeginRead(buffer, 0, buffer.Length, EndRead, buffer);
                //_Stream.ReadAsync(buffer, 0, buffer.Length);
                //_Stream.Write(buffer, 0, buffer.Length);

                string result = Encoding.UTF8.GetString(buffer);
                //_Stream.WriteAsync(buffer);

                SendMessage(result);
            }

            public void EndRead(IAsyncResult result)
            {
                byte[] buffer = (byte[])result.AsyncState;
                var ns = _Client.GetStream();
                var bytesAvalable = ns.EndRead(result);
            }

            async void SendMessage(string str)
            {
                try
                {
                    //await Task.Run(() =>
                    //{
                        int val;
                        bool isParsed = Int32.TryParse(str, out val);

                        if (!isParsed)
                            return;

                        if (val != 1 && val != 2)
                            return;

                        val = val == 1 ? 3 : 4;

                        str = val.ToString();
                        byte[] buffer = Encoding.UTF8.GetBytes(str);
                        _Stream.WriteAsync(buffer);
                        _Stream.Flush();
                    //});
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
            }
        }

Client.xaml:

<Window x:Class="Client.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Client"
        mc:Ignorable="d"
        Title="MainWindow" Height="250" Width="300">
    <Grid>
        <Button x:Name="Button_1" Content="Send" FontSize="15" Width="100" Height="30" Margin="180, 10, 0, 160"
            Click="Button_Click" ClickMode="Press">
        </Button>

        <TextBox x:Name="TextBox_1" MaxLength="50" FontSize="15" Margin="10, 20, 120, 170" TextChanged="TextBox_TextChanged">
        </TextBox>

        <TextBlock x:Name="TextBlock_1" TextWrapping="Wrap" Margin="10, 90, 10, 100" Text="{Binding Message, Mode=TwoWay}" OpacityMask="#00000000" Background="#FF685959">
        </TextBlock>
    </Grid>
</Window>

Client.xaml.cs:

using System;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Net.Sockets;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace Client
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private const string _IP = "127.0.0.1";
        private const int _PORT = 8888;
        private const int _BUFFER_SIZE = 1;
        private TcpClient _Client;
        private string _message;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            try
            {
                TextBox textBox = (TextBox)sender;
                textBox.MaxLength = 1;
                int numVal;
                bool isParsed = int.TryParse(textBox.Text, out numVal);

                if (isParsed)
                {
                    if (numVal != 1 && numVal != 2)
                        TextBlock_1.Text = "The value must be 1 or 2!";
                    else
                        TextBlock_1.Text = String.Empty;
                }
                else
                {
                    TextBlock_1.Text = "The value must be 1 or 2!";
                }
            }
            catch (Exception ex)
            {
                TextBlock_1.Text = ex.Message;
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            _Client = new TcpClient();

            try
            {
                int numVal;
                bool isParsed = int.TryParse(TextBox_1.Text, out numVal);

                if (!isParsed)
                    return;

                if (numVal != 1 && numVal != 2)
                    return;

                _Client.ConnectAsync(_IP, _PORT);

                // Error
                var Stream = _Client.GetStream();

                if (Stream != null && TextBox_1.Text != String.Empty)
                {
                    byte[] buffer = new byte[_BUFFER_SIZE];
                    buffer = Encoding.UTF8.GetBytes(TextBox_1.Text);
                    SendMessage(Stream, buffer);
                    byte[] bufferResult = new byte[_BUFFER_SIZE];

                    Stream.BeginRead(bufferResult, 0, bufferResult.Length, EndRead, bufferResult);
                    string result = Encoding.UTF8.GetString(bufferResult);

                    //Binding binding = BindingOperations.GetBinding(TextBlock_1, TextBlock.TextProperty);
                    Message = result;

                }
            }
            catch (SocketException ex)
            {
                TextBlock_1.Text = ex.Message;
            }
        }

        public void EndRead(IAsyncResult result)
        {
            byte[] buffer = (byte[])result.AsyncState;
            var ns = _Client.GetStream();
            var bytesAvalable = ns.EndRead(result);
        }

        public void BeginRead(TcpClient Client)
        {
            try
            {
                if (Client == null)
                    return;

                byte[] buffer = new byte[_BUFFER_SIZE];
                
                if (Client.Connected)
                {
                    NetworkStream Stream = Client.GetStream();
                    AsyncCallback callback = null;
                    callback = ar =>
                    {
                        Stream.BeginRead(buffer, 0, buffer.Length, callback, buffer);
                    };
                }
                else
                {
                    Client.Connect(_IP, _PORT);
                }
                
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        async void SendMessage(NetworkStream Stream, byte[] buffer)
        {
            try
            {
                Stream.Write(buffer, 0, buffer.Length);
                string result = Encoding.UTF8.GetString(buffer);
                Stream.Flush();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        #region ViewModelProperty: Message
        public string Message
        {
            get
            {
                return _message;
            }
            set
            {
                //_message = value;
                _message = "123";
                OnPropertyChanged(_message);
            }
        }
        #endregion

        #region INotifiedProperty Block
        //public event EventHandler SomeEventHappened = delegate { };
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            try
            {
                PropertyChangedEventHandler handler = PropertyChanged;

                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
                //handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
            catch (Exception ex) { }
        }
        #endregion
    }
}

Initially, I tried to use "TextBlock_1.Text = result" and it didn't work, the values were still '1' and '2'. Then I read that I needed to use PropertyChangedEventHandler event. But it didn't help.

1

There are 1 answers

0
Daniel W. On

That this works correct a correct implementation of INotifyPropertyChanged is required. Meaning every time a property changes a PropertyChanged event needs to be fired with the Properties name as content. In your implementation of public string Message you use the value instead the property name.

so use:

    public string Message
    {
        get
        {
            return _message;
        }
        set
        {
            //_message = value;
            _message = "123";
            OnPropertyChanged();// No parameter or "Message" as parameter
        }
    }

Your interface implementation is correct, use of [CallerMemberName] will automatically insert the Property name in case the Method is called without paramter:

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        try
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            //handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        catch (Exception ex) { }
    }