CodeGo.net>有没有更好的方法来使用条形码扫描仪?

我目前正在使用UWP App中的BarcodeScanner.
为了实现它,我遵循了一些有关Microsoft文档的教程.

它工作正常,但不像我想要的那样工作.

条形码扫描器只能通过DataReceived事件获取值.
因此,当我想从BarcodeScanner返回值时,这是不可能的.

在这里,我正在注册扫描仪:

private static async Task<bool> ClaimScanner()
{
    bool res = false;

    string selector = BarcodeScanner.GetDeviceSelector();
    DeviceInformationCollection deviceCollection = await 
    DeviceInformation.FindAllAsync(selector);

    if (_scanner == null)
        _scanner = await BarcodeScanner.FromIdAsync(deviceCollection[0].Id);

    if (_scanner != null)
    {
        if (_claimedBarcodeScanner == null)
            _claimedBarcodeScanner = await _scanner.ClaimScannerAsync();

        if (_claimedBarcodeScanner != null)
        {
            _claimedBarcodeScanner.DataReceived += ClaimedBarcodeScanner_DataReceivedAsync;
            [...] 
        }
    }
}

一旦我接收到数据,就会触发该事件:

private static async void ClaimedBarcodeScanner_DataReceivedAsync(ClaimedBarcodeScanner sender, BarcodeScannerDataReceivedEventArgs args)
{
    await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        if (CurrentDataContext != null && CurrentDataContext is IScannable)
        {
            IScannable obj = (IScannable)CurrentDataContext;
            obj.NumSerie = CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, args.Report.ScanDataLabel);
        }
        else if (CurrentDataContext != null && CurrentDataContext is Poste)
        {
            Poste p = (Poste)CurrentDataContext;
            string code = CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, args.Report.ScanDataLabel);
            p.CodePoste = code.Substring(0, 6);
        }
    });
}

如您所见,我有点*执行该方法中的所有操作(更新其他类的实例等).

目前,我在ViewModel中这样称呼BarcodeScanner:

public void ScanPosteCodeAsync()
{
    BarcodeScannerUtil.ScanBarcodeUtil(CurrentPoste);
}

但是我无法控制CurrentPoste实例,而我会做的更像是:

public void ScanPosteCodeAsync()
{
    string returnedCode = BarcodeScannerUtil.ScanBarcodeUtil()
    this.CurrentPoste.Code = returnedCode;
}

有什么方法可以返回扫描仪的值,以便在ViewModel中使用返回的值?

解决方法:

在使用MVVM时,WPF开发人员也存在类似的模式,因此您需要获取/更新视图模型(VM)公开的模型.也许它们在数据库中.不必使用丑陋的DB代码来污染漂亮的VM,而是可以将“服务”传递到VM中.现在,“服务”并不一定意味着SOA /微服务,可能只是不同项目中的另一类.关键是您将所有条形码物品放在此处,当收到物品时,它可能会触发您的VM监听的事件,或者只是将其排队在某个位置,以供您的VM通过服务接口请求.

I already have all the barcode code in a service class, and there’s the problem because I don’t want the service class to update my current model. The major issue I have is that I don’t know how to do to make my VM listen to the DataReceived event

好吧,据我所知,您的服务并未与UWP MVVM分离.对于该事件,您是否考虑过纯粹为VM客户端公开次事件?我发现这对我来说很好.

Like an event in the VM listening to the data received event ?

是的,但是不必只是物理事件类型的列表而已. C#事件暗示可以有多个订阅者,这对于条形码应用程序实际上没有任何意义.应该只有一个前台阅读器.

在这里,我将使用Action< string>将条形码从BarcodeScanner传递到客户端(在本例中为VM).通过使用操作并将条形码处理移至客户端,我们使BarcodeScanner完全不了解MVVM. Windows.ApplicationModel.Core.CoreApplication.MainView使BarcodeScanner难以置信地与它不关心的东西耦合在一起.

首先,我们要使所有内容脱钩,因此首先是一个代表条形码扫描器重要部分的接口:

public interface IBarcodeScanner
{
    Task<bool> ClaimScannerAsync();
    void Subscribe(Action<string> callback);
    void Unsubscribe();
}

定义好后,我们将其传递到您的VM中,如下所示:

public class MyViewModel 
{
    private readonly IBarcodeScanner _scanner;

    /// <summary>
    /// Initializes a new instance of the <see cref="MyViewModel"/> class.
    /// </summary>
    /// <param name="scanner">The scanner, dependency-injected</param>
    public MyViewModel(IBarcodeScanner scanner)
    {
        // all business logic for scanners, just like DB, should be in "service"
        // and not in the VM

        _scanner = scanner;
    }

接下来,我们添加一些命令处理程序.想象一下,我们有一个按钮,单击该按钮即可启动条形码订阅.将以下内容添加到VM:

public async void OnWidgetExecuted()
{
    await _scanner.ClaimScannerAsync();
    _scanner.Subscribe(OnReceivedBarcode);
}

// Barcode scanner will call this method when a barcode is received
private void OnReceivedBarcode(string barcode)
{
    // update VM accordingly
}

最后,BarcodeScanner的新外观:

public class BarcodeScanner : IBarcodeScanner
{
    /// <summary>
    /// The callback, it only makes sense for one client at a time
    /// </summary>
    private static Action<string> _callback; // <--- NEW

    public async Task<bool> ClaimScannerAsync()
    {
        // as per OP's post, not reproduced here
    }

    public void Subscribe(Action<string> callback) // <--- NEW
    {
        // it makes sense to have only one foreground barcode reader client at a time
        _callback = callback;
    }

    public void Unsubscribe() // <--- NEW
    {
        _callback = null;
    }

    private void ClaimedBarcodeScanner_DataReceivedAsync(ClaimedBarcodeScanner sender, BarcodeScannerDataReceivedEventArgs args)
    {
        if (_callback == null) // don't bother with ConvertBinaryToString if we don't need to
            return;

        // all we need do here is convert to a string and pass it to the client

        var barcode = CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8,
                                                                args.Report.ScanDataLabel);

        _callback(barcode);

    }
}

那是什么问题呢?

总而言之,您陷入了某种循环依赖问题,即VM依赖于BarcodeScanner,而BarcodeScanner依赖于表示API,这是您不需要了解的.即使您在BarcodeScanner中就IScannable进行了抽象的尝试(不幸的是Poste也不是这种情况),扫描层仍在假设使用它的用户类型.这只是垂直的.

使用这种新方法,您可以根据需要将其用于其他类型的应用程序,包括UWP控制台应用程序.

上一篇:操作系统-Windows:UWP(Universal Windows Platform)


下一篇:如何在UWP中的Pivot中的Pivot.ItemTemplate中获取控件?