简体   繁体   中英

Using event-driven TCP client and NotifyPropertyChanged together causes exception

I'm writing a C# console application that connects to a server via TCP and receives periodic data back. I'm using some code I found here , which is a nice easy to use async client. Using this, my code starts up, makes a connection and then waits for events:

static void Main(string[] args)
        {
            agents = new ObservableCollection<AgentState>();
            EventDrivenTCPClient client = new EventDrivenTCPClient(IPAddress.Parse("192.168.0.1"), 5000);
            client.DataReceived += new EventDrivenTCPClient.delDataReceived(client_DataReceived);

               client.Connect();

                do
                {
                    while (!Console.KeyAvailable)
                    {
                        Thread.Sleep(50);
                    }

                } while (Console.ReadKey(true).Key != ConsoleKey.Q);

client.Disconnect();

       }

This starts up the app, connects to 192.168.0.1 on port 5000 and then starts listening for responses. When they're received, I update an ObservableCollection called "clients" (comprised of object "ClientState") with the results:

 static void client_DataReceived(EventDrivenTCPClient sender, object data)
    {
               string received = data as string;

        string parameters=received.Split(',');

        ClientState thisclient = clients.FirstOrDefault(a => a.clientName == parameters[0]);

        var index = clients.IndexOf(thisclient);
        if (index < 0)
        {

            ClientState newclient = new ClientState();
            newclient.clientName = clientname;
            newclient.currentState = state;
            newclient.currentCampaign = campaign;
            clients.Add(newclient);
        }
        else
        {
            clients[index].currentState = state;
            clients[index].currentCampaign = campaign;
        }

    }

To my great surprise, this all works fine: the code ticks along nicely, collecting the stats and adding to and updating the ObservableCollection. However, the problem is when I try to hook into the PropertyChanged... firstly, I add

   clients.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(agents_CollectionChanged);

to Main(), then I add:

    static void clients_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {

                 foreach (INotifyPropertyChanged item in e.NewItems)
                     item.PropertyChanged += new PropertyChangedEventHandler(newagent_PropertyChanged); 
    }

I'd like this to call the newagent_PropertyChanged method (which just spits out to the Console at the moment) when anything changes in the ObservableCollection .. but some reason, as soon as a PropertyChanged event is fired, I get an exception in the "cbDataRecievedCallbackComplete" method in the TCP code:

Unable to cast object of type 'ClientState' to type 'System.ComponentModel.INotifyPropertyChanged'.

... so somehow, the act of trying to raise a PropertyChanged is causing the "cbDataRecievedCallbackComplete" code to fire; it's almost as if the events are "crossing paths". If I "catch" the error the code grinds to a halt and doesn't process any more incoming data.

I don't have enough experience to know if this is a problem that I've introduced, or with the source code. I'll put money on it being the former: can anyone see where there problem lies?

UPDATE: In response to the answers below, I've changed my class definition to look like:

  public class ClientState : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }

        public string agentName { get; set; }
        public string currentCampaign { get; set; }

        public string _currentState;
        public string currentState
        {
            get { return _currentState; }
            set
            {
                if (value != _currentState)
                {
                    _currentState = value;
                    OnPropertyChanged("CurrentState");

                }
            }
        }
    }

... so I'd expect any changes to "currentState" to trigger an event. However, I get an error in the same place, except now the error is:

Object reference not set to an instance of an object.

in the r.DataReceived.EndInvoke(result) line of the cbDataRecievedCallbackComplete.

The exception will be caused by this line:

foreach (INotifyPropertyChanged item in e.NewItems)

You're implicitly casting each object in e.NewItems to INotifyPropertyChanged . Since e.NewItems contains instances of ClientState I'm guessing that ClientState does not implement INotifyPropertyChanged .

If your class definition for ClientState doesn't look something like this

public class ClientState : INotifyPropertyChanged

then you cannot cast it to the type INotifyPropertyChanged. You aren't getting a compile time exception because the e.NewItems collection is of type object so it will let you try to cast it to anything.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM