使用条码扫描器的WPF应用程序中的多线程问题



我使用的是MVVM风格的架构,整个应用程序运行良好。但我在应用程序中引入了一个扫描仪,现在有许多多线程问题。以下只是一些伪代码,但基本上是我需要它工作的方式:

View.xaml

<DataGrid ItemsSource="{Binding MyList}"/>

View.xaml.cs

class View : UserControl
{
    public View()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
}

ViewModel.cs

class ViewModel
{
    private Scanner scanner;//this is my scanner, duh
    public ViewModel()
    {
        scanner = new Scanner();
        scanner.ScanEvent += ScanEvent;
        //all this does is when the scanner scans something
        //then it will trigger an event looking for method ScanEvent()
    }
    public ObservableCollection<string> MyList{ get; set; }
    public void ScanEvent()
    {
        string strBarcode = scanner.strBarcode;
        MyList.Insert(0, strBarcode);//this is where the error is thrown
    }
} 

抛出的错误是This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.。当我将扫描仪设置为我的一个对象时,扫描仪工作正常,所以我不明白为什么我不能用这个ObservableCollection做同样的事情?下面是我的扫描器类中处理事件的一个片段:

Scanner.cs

internal class Scanner
{
    public delegate void EventHandler();
    public event EventHandler ScanEvent = delegate { };
    public Scanner()
    {
        m_pCoreScanner.BarcodeEvent += new _ICoreScannerEvents_BarcodeEventEventHandler(OnBarcodeEvent);
        RegisterForEvents();
    }
    public void OnBarcodeEvent(short eventType, ref string scanData)
    {
        strBarcode = GetBarcodeFromXml(scanData);
        ScanEvent();
    }
    //this class is huge, so I only included pertinent code 
}

正如异常消息所说,你必须在UI(或Dispatcher)线程中更新ObservableCollection,因为UI元素的属性(DataGrid.ItemsSource)被绑定到集合。

试试这个:

public void ScanEvent()
{
    string strBarcode = scanner.strBarcode;
    Application.Current.Dispatcher.Invoke(
        new Action(() => MyList.Insert(0, strBarcode)));
}

如果您想避免使用Dispatcher。当想要返回UI线程时,到处调用-我同意这是丑陋的,并且对测试没有很大帮助。你可以尝试使用响应式扩展(Rx . net)。

使用Rx,你可以用"推"的方式来处理事件,而不是标准的。net事件方式(拉模型)。这将允许你在事件数据上编写LINQ风格的查询,重要的是,在你的情况下,将调度处理回dispatcher (UI)线程。

下面是重写的代码,使用Rx代替标准的。net事件模型:

。. Net框架版本= 4.0 (WPF)Rx版本= 2.0.21103.1

nuGet上有新版本的Rx框架可用,如果你打算将它们与WPF一起使用,你还需要包括Rx-WPF。

internal class Scanner
{
    public IObservable<EventArgs> ScanEvent
    {
        get
        {
            return Observable.FromEventPattern<EventHandler, EventArgs>(
            h => m_pCoreScanner.BarcodeEvent += h, h => m_pCoreScanner.BarcodeEvent -= h)
            .Select(x => x.EventArgs);
        }
    }
}
class ViewModel : IDisposable
{
    private Scanner scanner;
    private IDisposable _disposable;
    public ViewModel()
    {
        scanner = new Scanner();
        MyList = new ObservableCollection<string>();
        _disposable = scanner.ScanEvent
            .ObserveOn(DispatcherScheduler.Current)
            .Subscribe(x =>
            {
                string strBarcode = scanner.strBarcode;
                MyList.Insert(0, strBarcode);
            });
    }
    public  void Dispose()
    {
        _disposable.Dispose();
    }
    public ObservableCollection<string> MyList { get; set; }
} 

一个开始学习Rx的好地方是这个免费的在线电子书- http://www.introtorx.com/

如果您使用MvvmLight这样的框架,您可以利用消息机制。在VM中注册一个处理程序,然后从扫描器库发送消息

http://wbsimms.com/mvvm-light-toolkit-messaging-example/

这些消息通过默认单例跨程序集工作,但这取决于您的扫描仪库是否可自定义。

最新更新