Потоковая передача высокоскоростных данных в пользовательский интерфейс WPF с использованием потоков и MVVM - PullRequest
2 голосов
/ 24 августа 2011

У меня проблема с использованием нового приложения WPF, которое пытается отобразить высокоскоростной поток байтов в текстовое поле. Байты поступают через последовательный порт, я создал старое приложение WinForms, которое прекрасно обрабатывало поток, имело фоновый поток, который считывал данные с последовательного порта и отправлял их в пользовательский интерфейс через вызов делегата.

Теперь проблема с этим приложением WPF заключается в том, что я использую базовый подход MVVM. У меня есть текстовое поле в интерфейсе пользователя, привязанное к свойству на виртуальной машине, которое запускает событие PropertyChanged, основанное на INotifyPropertyChanged. Когда данные готовы к отправке в пользовательский интерфейс через подписанное событие в службе, читающей последовательный порт, я использую следующее:

Action dispatchAction = () => { FormattedStream += s; };
_currentDispatcher.Invoke(dispatchAction);

FormattedStream - это строковое свойство виртуальной машины, к которому привязан пользовательский интерфейс.

То, что происходит в приложении WPF, чего не происходит в версии WinForms, - это то, что приложение WPF становится медленным и не отвечает, поскольку оно работает вместе с тем фактом, что оно не успевает за потоком, а также приложением WinForms и Приложение wpf использует / требует больше процессора в соответствии с моим диспетчером задач.

Я хочу знать, есть ли какое-нибудь решение для обработки потоковых (высокоскоростных) данных в пользовательском интерфейсе WPF.

ETA: я также попытался использовать BeginInvoke вместо Invoke, и при использовании BeginInvoke поток длится несколько секунд, а затем останавливается. Invoke - единственный способ заставить его непрерывно передавать данные в пользовательский интерфейс.

ETA: Вот код:

// Окно / Вид

public partial class MainWindow : Window, IView
{
  public MainWindow()
  {
     InitializeComponent();
  }

  public IViewModel ViewModel
  {
     get { return DataContext as IViewModel; }
     set { DataContext = value; }
  }

  public void ScrollToCaret()
  {
     txtBoxOutPut.ScrollToEnd();

     if (txtBoxOutPut.Text.Length > 10000)
        txtBoxOutPut.Text = txtBoxOutPut.Text.Remove(0, 9000);
  }

  public event Action ComPortSelected;
  public event Action StartPortReader;
  public event Action StopPortReader;


  private void Start_Click(object sender, RoutedEventArgs e)
  {
     StartPortReader.Invoke();
  }

  private void Stop_Click(object sender, RoutedEventArgs e)
  {
     StopPortReader.Invoke();
  }

}

// ViewModel

public class ViewModel : IViewModel, INotifyPropertyChanged

{ только для чтения ISerialPortReaderService _portReaderService; частный только для чтения Диспетчер _currentDispatcher;

  public ViewModel(IView view, ISerialPortReaderService portReaderService)
  {
     View = view;
     View.ViewModel = this;
     View.StartPortReader += View_StartPortReader;
     View.StopPortReader += View_StopPortReader;
     _portReaderService = portReaderService;
     _currentDispatcher = Dispatcher.CurrentDispatcher;
     _portReaderService.ByteArrived += _portReaderService_ByteArrived;
  }

  private void _portReaderService_ByteArrived(string s)
  {
     Action dispatchAction = () => { FormattedStream = s; };
     _currentDispatcher.Invoke(dispatchAction);
  }

  private void View_StopPortReader()
  {
     _portReaderService.Stop();
  }

  private void View_StartPortReader()
  {
     _portReaderService.Start(SelectedPort);
  }

  public IView View { get; private set; }

  public void ShowView()
  {
     View.Show();
  }

  private StringBuilder _FormattedStream = new StringBuilder();
  public string FormattedStream
  {
     get
     {
        return _FormattedStream.ToString();
     }
     set
     {
        _FormattedStream.Append(value);
        PropertyChanged(this, new PropertyChangedEventArgs("FormattedStream"));
        View.ScrollToCaret();
     }
  }

  private string _SelectedPort;
  public string SelectedPort
  {
     get
     {
        return _SelectedPort;
     }
     set
     {
        _SelectedPort = value;
        PropertyChanged(this, new PropertyChangedEventArgs("SelectedPort"));
     }
  }

  public ReadOnlyCollection<string> AvailablePorts
  {
     get { return GetAvailablePorts(); }
  }

  private ReadOnlyCollection<string> GetAvailablePorts()
  {
     var ports = System.IO.Ports.SerialPort.GetPortNames();
     return new ReadOnlyCollection<string>(ports.ToList());
  }

  public event PropertyChangedEventHandler PropertyChanged = delegate { };

}

// Служба чтения последовательного порта

public class SerialPortReaderService : ISerialPortReaderService

{ private SerialPort _port = new SerialPort (); только для чтения IThreadRunner _threadRunner;

  public SerialPortReaderService(IThreadRunner threadRunner)
  {
     _threadRunner = threadRunner;
  }

  public void Start(string comPort)
  {
     if (_port != null && !_port.IsOpen)
     {
        _port.PortName = comPort;
        _port.BaudRate = 4800;            
        _port.Open();

        _threadRunner.Start(() =>
                               {

                                     var b = new byte[20];
                                     var bArray = _port.Read(b, 0, 20);
                                     foreach (var b1 in b)
                                     {
                                        next10Bytes.Append(b1 + ", ");
                                     }                                        

                                     BytesArrived(next10Bytes.ToString());
                                     next10Bytes.Clear();

                                     Thread.Sleep(10);

                               });
     }
  }

  private StringBuilder next10Bytes = new StringBuilder();

  public void Stop()
  {
     if (_port.IsOpen)
     {
        _threadRunner.Stop();
        _port.Close();
     }
  }

  public event Action<string> BytesArrived;

}

// Я использую ThreadRunner

public class ThreadRunner : IThreadRunner

{ приватная тема _thread; private bool _isRunning;

  /// <summary>
  /// Will continuously run in a while loop the action submitted in a separate thread
  /// </summary>
  /// <param name="toDoAction"></param>
  public void Start(Action toDoAction)
  {
     if (_thread != null && _thread.IsAlive)
        Stop();

     _isRunning = true;

     _thread = new Thread(() =>
                             {
                                while (_isRunning)
                                {
                                   toDoAction.Invoke();
                                }
                             });
     _thread.Start();
  }

  public void Stop()
  {
     _isRunning = false;
     if (_thread != null && _thread.IsAlive)
     {
        _thread.Abort();
        _thread.Join(new TimeSpan(0, 0, 1));
     }
  }

  public bool ThreadIsRunning
  {
     get { return _isRunning; }
  }

}

1 Ответ

0 голосов
/ 25 августа 2011

Из того, что упомянул Петой, я сделал новое окно, все еще используя потоковую службу, но только сам окошко подписывалось на событие bytesarrived и вручную добавлялось в txtbox через Dispatcher.Invoke и low, и вот что решило проблему, нетзамедлить и не использовать процессор

Мораль этой истории заключается в том, что привязка или, по крайней мере, привязка к строке, постоянно обновляющейся при каждом добавлении, вызывает замедление.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...