【问题标题】:Is there a better way to use barcode scanner?有没有更好的方法来使用条码扫描仪?
【发布时间】:2019-05-28 20:09:18
【问题描述】:

我目前在 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 时存在类似的模式,您需要获取/更新 VM 公开的模型。也许它们在数据库中。与其用丑陋的 DB 代码污染你漂亮的 VM,不如将 "service" 传递到 VM 中。现在,“服务”并不一定意味着 SOA/微服务,也许它只是不同项目中的另一个类。关键是你把所有的条形码都放在那里,当收到东西时,它可能会触发你的虚拟机监听的事件,或者它只是将它排队等待你的虚拟机通过服务接口请求的地方
  • 我已经拥有服务类中的所有条码代码,但问题在于我不希望服务类更新我当前的模型。我遇到的主要问题是我不知道如何让我的虚拟机监听DataReceived 事件。
  • 好吧,据我所知,您的服务并未与 UWP MVVM 分离。对于事件,您是否考虑过纯粹为 VM 客户端公开次要事件?我发现这对我很有效
  • 像VM中的一个事件监听数据接收事件?

标签: c# uwp barcode-scanner


【解决方案1】:

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

我已经拥有服务类中的所有条形码,但问题在于我不希望服务类更新我当前的模型。我遇到的主要问题是我不知道如何让我的虚拟机监听 DataReceived 事件

好吧,据我所知,您的服务并未与 UWP MVVM 分离。对于事件,您是否考虑过纯粹为 VM 客户端公开次要事件?我觉得这对我很有效。

就像VM中的一个事件监听数据接收事件?

是的,但它不一定是听物理event 类型只是概念。 C# 事件意味着可以有多个订阅者,这对于条形码应用程序没有意义。应该只有一个前台阅读器。

这里我将使用Action&lt;string&gt; 将条形码从BarcodeScanner 传递给客户端,在本例中是VM。通过使用Action 并将条形码处理移动到客户端,我们使BarcodeScanner 完全不知道 MVVM。 Windows.ApplicationModel.Core.CoreApplication.MainViewBarcodeScanner 难以置信地与它不应该关心的东西耦合。

首先我们想要解耦一切,所以首先是一个代表条形码扫描仪重要位的接口:

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

接下来我们添加一些命令处理程序。想象一下,我们有一个按钮,单击该按钮会启动条形码订阅。将以下内容添加到 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 控制台应用程序。

【讨论】:

  • 这看起来比我做的好多了。非常感谢您的详细回答,我现在理解得更好了。我会尽快尝试并给您反馈。
  • @Reaperino 先生,欢迎您。我本来想今天早些时候回复你的,但我很遗憾地忘记了(而且我真的需要在 VS 面前)。万事如意!
  • 您好,我有一个关于代码的小问题。除了视图模型的构造函数中带有IBarcodeScanner 的部分之外,我几乎了解所有内容。 IBarcodeScanner 实例是如何实际创建的?就像是视图模型定位器在做它吗?
  • @Reaperino 如果您使用依赖注入,请参阅文档以了解如何设置生命周期。如果您不想或不想注入构造函数,您可以手动创建它并在 VM 上设置属性。最简单的方法是通过视图的代码隐藏并设置 VM 的属性。但是有很多方法
猜你喜欢
  • 1970-01-01
  • 2020-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-01
  • 2012-01-03
相关资源
最近更新 更多