c# - 有没有更好的方法来使用条码扫描仪?

标签 c# uwp barcode-scanner

我目前正在 UWP 应用程序中使用 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 中使用返回的值?

最佳答案

WPF 开发人员在使用 MVVM 时也存在类似的模式,并且您需要获取/更新 View 模型 (VM) 公开的模型。也许它们在数据库中。可以将“服务”传递到虚拟机中,而不是用丑陋的数据库代码污染你的漂亮虚拟机。现在,“服务”并不一定意味着 SOA/微服务,也许它只是不同项目中的另一个类。关键是您将所有条形码内容放在那里,当收到某些内容时,它可能会触发您的虚拟机监听的事件,或者可能只是将其排队等待您的虚拟机通过服务接口(interface)请求。

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 ?

是的,但不一定是聆听实体 event仅输入概念。 C# 事件意味着可以有多个订阅者,这对于条形码应用程序来说实际上没有意义。应该只有一名前台读者。

这里我将使用Action<string>传递来自BarcodeScanner的条形码到客户端,在本例中是虚拟机。通过使用 Action并将条形码处理移至客户端,我们保留 BarcodeScanner完全不知道MVVM。 Windows.ApplicationModel.Core.CoreApplication.MainView正在制作 BarcodeScanner与它不应该关心的东西难以置信地耦合。

首先,我们希望将所有内容解耦,因此首先是一个代表条形码扫描仪重要部分的接口(interface):

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

定义后,我们将把它传递到您的虚拟机中,如下所示:

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

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

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

    }
}

那么问题出在哪里?

总而言之,您陷入了某种循环依赖问题,即虚拟机依赖于 BarcodeScannerBarcodeScanner依赖于表示 API - 这是它不需要了解的东西。即使您在 BarcodeScanner 中对抽象做了很好的尝试。关于IScannable (遗憾的是 Poste 不是这种情况),扫描层正在假设使用它的用户类型。它只是垂直

通过这种新方法,如果需要,您可以将其用于其他类型的应用,包括 UWP 控制台应用。

关于c# - 有没有更好的方法来使用条码扫描仪?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56325605/

相关文章:

c# - PropertyChangedEvent 和 CanExecute 问题

c# - 在 WPF 中限制 "Auto"和 "1*"上的行高

android - 如何在 Xamarin 上创建一个根据平台执行不同操作的函数?

javascript - JavaScript 中的条码扫描器?或插件/扩展

android - Cordova,条码扫描器在 Android 中打开两次

cordova - 尝试在我的项目中使用 PDF417 条码扫描 SDK。

c# - 显示工具提示 make 按钮函数不会运行 c#

c# - 将 JSON 对象 A 转换为 JSON B 对象,其中 B 是 A 的严格子集。两者都由两个 json 模式管理。在 .net 核心中

c# - UWP: MapControl 透明背景

c++ - 在 UWP 中找不到剪贴板