1

I'm using WPF and an MVVM architecture. I need to update my view and data model with any serial data that is received, which could be at any time.

To implement this I have

public static partial class SerialPortService
{
    private static SerialPort Port = new SerialPort
    {
        Handshake = Handshake.None,
        BaudRate = 9600,
        ReadTimeout = 400,
        DiscardNull = false,
        ReceivedBytesThreshold = 1,
        WriteTimeout = 100
    };

    public static string PortName
    {
        get => Port.PortName;
        set => Port.PortName = value;
    }

    public static SerialDataReceivedEventHandler DataReceived
    {
        set => Port.DataReceived += value;
    }

    public static void OpenCOMPort()
    {
        try
        {
            Port.Open();
            Port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
        }
        catch(Exception ex)
        {
            Debug.Print("Exception caught while opening " + Port.PortName + Environment.NewLine + ex.ToString());
        }   
    }

    public static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
    {
        Byte[] rx_buffer = new Byte[ReadLength];    // ReadLength is set elsewhere

        try
        {
            for (int i = 0; i < ReadLength; i++)
            {
                rx_buffer[i] = (Byte)Port.ReadByte();
            }

            // update the viewModel with the data in rx_buffer here?!
        }
        catch(Exception ex)
        {
            if (Port.IsOpen)
            {
                Port.ReadExisting();        // Clears input buffer
            }

            Debug.Print(ex.ToString());
        }
    }

Whenever I receive data via the DataReceivedHandler(), I want to inform the viewModel that there is new data to display and to store it in my model (which is just the bytes received and a colour associated with the value of the first byte).

What is the best way to communicate this to the viewModel?

I also tried to update the model directly from the SerialPortService class but I got an error relating to thread affinity.

Thanks!

Bart
  • 63
  • 1
  • 12
  • On your viewmodel have a dependency property that your view binds to (with perhaps suitable converters). The dependency property would wrap the buffer property on this service class. See this: https://stackoverflow.com/questions/291518/inotifypropertychanged-vs-dependencyproperty-in-viewmodel?rq=1 – auburg Nov 12 '18 at 16:21

1 Answers1

2

Add an event to SerialPortService so the ViewModel can listen for data added events:

    static public event EventHandler<NewDataReceivedArgs> NewDataReceived;

    static public void OnNewDataReceived(byte[] data)
    {
        var handler = NewDataReceived;
        if (handler != null)
        {
            handler(null, new NewDataReceivedArgs(){Data = data});
        }
    }

    public class NewDataReceivedArgs : EventArgs
    {
        public byte[] Data { get; set; }
    }

Then in your DataReceivedHandler:

// update the viewModel with the data in rx_buffer here?!
OnNewDataReceived(rx_buffer);
Jon
  • 2,891
  • 2
  • 17
  • 15
  • I used this method and it seemed correct - however when the listener in the ViewModel gets called (and tries to update an ObservableCollection), I get the following error: Exception thrown: 'System.NotSupportedException' in PresentationFramework.dll System.NotSupportedException: This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread. My understanding is that this is a thread affinity issue. I don't however understand how to resolve it. – Bart Nov 13 '18 at 11:04
  • The code that updates the ObservableCollection needs to be run on the UI thread. See here: https://stackoverflow.com/questions/18331723/this-type-of-collectionview-does-not-support-changes-to-its-sourcecollection-fro – Jon Nov 13 '18 at 17:37