Делать что-то во время приема данных с последовательного порта в WPF


Я хочу создать программу, которая будет раскрашивать эллипс любым способом в зависимости от полученных данных в "реальном времени". Данные такие:: x1-1, x2-1 и т.д.

У меня есть странная проблема, я вижу во время отладки, что цвет заполнения эллипса должен измениться, но этого не происходит.

Часть моего кода с получением данных:

 private void ReceiveData()
            {
                SerialPort serialPort = new SerialPort("COM1", 9600);
                serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
                serialPort.Open();
            }

 private static void DataReceivedHandler(
                object sender,
                SerialDataReceivedEventArgs e)
            {
                SerialPort sp = (SerialPort)sender;
                string indata = sp.ReadExisting();
                if (indata.Contains(" "))
                {
                    testString = indata.Substring(0, 4);

                }
                Console.Write(indata);
            }

Я могу видеть полученные данные в консоли, даже после нажатия на кнопку, которая начинает получать данные. Задача testString состоит в том, чтобы извлечь один x1-1 из indata, который продолжает расти во время приема данных. Я знаю, что это работает не так, как должно, но сейчас это не имеет значения.

Код действия на кнопке:

 private void button1_Click(object sender, RoutedEventArgs e)
        {
            ReceiveData();
            while (true)
            {
                if (testString == "x1-1") x1.Fill = Brushes.Blue;
                else x1.Fill = Brushes.Red;
            }
        }

x1 это эллипс, который я хочу сделать красным или синим. Я могу установить точку останова в этой функции, и я вижу, что она должна измениться. Я думаю, проблема в том, что программа ждет закрытия порта и завершения передачи, и тогда, возможно, она изменится, но мне нужно иметь ее в "реальном времени". Кто-нибудь знает, как это делается?

2 2

2 ответа:

Я считаю, что вы идете по этому неверному пути. Вместо того, чтобы постоянно проверять значение teststring, вам нужно просто запустить прослушиватель comm - порта и позволить ему работать в фоновом режиме-так как это будет в его собственном потоке. Затем необходимо создать привязку данных к цвету. Всякий раз, когда прослушиватель comm-порта анализирует строку, он может автоматически обновить цвет.

Во-первых, в классе CommPort:

class MyCommPort : INotifyPropertyChanged
{
    SerialPort serialPort = null;
    public MyCommPort()
    {
        serialPort = new SerialPort("COM3", 9600);
        serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
        serialPort.Open();
    }
    ~MyCommPort()
    {
        serialPort.Close();
    }

    private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
    {
        SerialPort sp = (SerialPort)sender;
        string testString = null;
        string indata = sp.ReadLine();
        if (indata.Length >= 4)
        {
            testString = indata.Substring(0, 4);
            // Update the value
            if (testString == "x1-1") EllipseBrush = Brushes.Blue;
            else EllipseBrush = Brushes.Red;
        }
        Console.Write(testString);
    }

    // Create a property that will be bound
    private SolidColorBrush ellipseBrush = Brushes.Red;
    public SolidColorBrush EllipseBrush
    {
        get { return ellipseBrush; }
        set
        {
            ellipseBrush = value;
            OnPropertyChanged("EllipseBrush");
        }
    }

    // Extend the INotifyPropertyChanged interface
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            // Alert anyone bound to this that the value has changed
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Затем установите DataContext эллипса в класс CommPort и свяжите Свойство заполнения эллипса к EllipseBrush. Теперь все, что вам нужно сделать, это запустить прослушиватель Commport (ReceiveData();), и обновления цвета должны происходить автоматически.

Например: MainWindow.xaml

<Window x:Class="delete_me.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Ellipse x:Name="ellipse" Fill="{Binding Path=EllipseBrush}" />
    </Grid>
</Window>

И код за ним: MainWindow.код XAML.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        ellipse.DataContext = new MyCommPort();
    }
}

Не вешайте поток пользовательского интерфейса, имея while(true) внутри события нажатия кнопки. Вместо этого используйте DispatchTimer для выполнения работы каждые 500 секунд.

System.Windows.Threading.DispatcherTimer MyTimer = null;
private void ReceiveData()
{
    MyTimer = new System.Windows.Threading.DispatcherTimer();
    MyTimer.Interval = new TimeSpan(0, 0, 0, 0, 500);
    MyTimer.Tick += MyTimer_Tick;
    MyTimer.Start();
    SerialPort serialPort = new SerialPort("COM1", 9600);
    serialPort.DataReceived += new 
             SerialDataReceivedEventHandler(DataReceivedHandler);
    serialPort.Open();
}

private static void DataReceivedHandler(
                object sender,
                SerialDataReceivedEventArgs e)
{
    SerialPort sp = (SerialPort)sender;
    string indata = sp.ReadExisting();
    if (indata.Contains(" "))
    {
        testString = indata.Substring(0, 4);

    }
}

private void button1_Click(object sender, RoutedEventArgs e)
{
    ReceiveData();
}

private void MyTimer_Tick(object sender, EventArgs e)
{
    if (testString == "x1-1")
    {
       x1.Fill = Brushes.Blue;
    }
    else 
    {
       x1.Fill = Brushes.Red;
    }
}