2011-08-24 25 views
2

我遇到了使用试图向文本框显示高速字节流的新WPF应用程序的问题。字节通过一个串行端口,我做了一个较旧的WinForms应用程序,它处理流很好,有一个后台线程从串口读取并通过调用委托发布到UI。使用线程和MVVM将高速数据流式传输到WPF UI

现在这个WPF应用程序的问题是我使用基本的MVVM方法。我将UI上的文本框绑定到VM上基于INotifyPropertyChanged触发PropertyChanged事件的属性。当数据准备好通过服务读取串口订阅的事件发布到用户界面,我用的是以下几点:

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

FormattedStream之处在于用户界面绑定到VM字符串属性。

发生在WPF应用程序上的WinForms版本中没有发生的事情是,WPF应用程序变得缓慢且无响应,因为它跟不上流以及WinForms应用程序一起运行,根据我的任务管理器,wpf应用程序正在使用/需要更多的处理器。

我想知道的是,如果有一些解决方案来处理流(高速)数据到WPF UI。

ETA:我也尝试使用BeginInvoke而不是Invoke,并且当BeginInvoke最后一次使用BeginInvoke几秒钟后冻结。调用是我能够持续流向用户界面的唯一途径。

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(); 
    } 

}

//视图模型

public class ViewModel : IViewModel, INotifyPropertyChanged 

{ 私人只读ISerialPortReaderService _portReaderService; private readonly Dispatcher _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 

{ 私人的SerialPort _port =新的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; } 
    } 

}

+0

因此,多久调用一次?每秒N次? –

+0

你是分块还是一个字节? – Josh

+0

s是由','分隔的20个字节的连接字符串。我相信每40毫秒的字节数大约为1。再次,我只是不明白为什么它在WinForms应用程序,而不是在WPF中正常工作。我甚至在winforms应用程序中使用相同的服务(即从端口读取),两个应用程序之间的区别在于WPF应用程序实现了MVVM,Winforms不是,只是新建了串行端口读取器服务和订阅到WPF中的ViewModel执行相同的bytesririved事件。 –

回答

0

从什么Petoj提到我做了一个新的窗口,仍然使用流媒体服务,但刚刚自己订阅bytesarrived事件和手动追加到通过Dispatcher.Invoke的txtbox窗户低,看到解决了这个问题,没有减速,没有大量的CPU使用率。

故事的道理是,绑定或至少绑定一个字符串,每次被添加到字符串时都会被刷新,从而导致速度减慢。