У меня проблема с использованием нового приложения 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; }
}
}