Есть ли лучший способ использовать сканер штрих-кода? - PullRequest
3 голосов
/ 27 мая 2019

В настоящее время я использую BarcodeScanner в приложении UWP.Для его реализации я следовал некоторым учебникам по документам 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;
            [...] 
        }
    }
}

И как только я получаю данные, это вызываетevent:

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

И, как вы можете видеть, я как бы вынужден делать все в этом методе (обновлять экземпляры других классов и т. д.).

В настоящее время я вызываю BarcodeScanner следующим образом в ViewModel:

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

Но у меня нет контроля над моим CurrentPoste экземпляром, и то, что я бы сделал, больше похоже на:

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

Есть ли способ вернуть значение сканера, чтобы использовать возвращенное значение в моей ViewModel?

1 Ответ

2 голосов
/ 28 мая 2019

Что ж, подобный шаблон существует для разработчиков WPF при использовании MVVM, и вам нужно получить / обновить модели, которые предоставляет ваша модель представления (VM).Возможно, они есть в базе данных.Вместо того, чтобы загрязнять вашу красивую виртуальную машину уродливым кодом БД, в нее может быть передана «служба».Теперь «serivce» не обязательно означает SOA / microservices, возможно, это просто еще один класс в другом проекте.Дело в том, что вы помещаете туда все свои штрих-коды, и когда что-то получено, возможно, оно запускает событие, которое слушает ваша виртуальная машина, или, возможно, оно просто ставит его в очередь где-то, готовое для вашей виртуальной машины, чтобы запросить через интерфейс службы.*

У меня уже есть весь штрих-код в классе обслуживания, и есть проблема, потому что я не хочу, чтобы класс обслуживания обновлял мою текущую модель.Основная проблема, с которой я столкнулся, заключается в том, что я не знаю, как сделать так, чтобы моя виртуальная машина прослушивала событие DataReceived

Что ж, из того, что я вижу, ваш сервис не отделен от UWP MVVM.Для события, вы рассматривали возможность выставления вторичного события исключительно для клиента виртуальной машины?Я считаю, что это хорошо работает для меня.

Как событие в виртуальной машине, слушающее событие получения данных?

Да, но это не обязательно должно бытьПрислушиваясь к физическому типу event, просто концепция.События C # подразумевают, что может быть несколько подписчиков, что на самом деле не имеет смысла для приложений со штрих-кодом.Должен быть только один читатель переднего плана.

Здесь я буду использовать Action<string> для передачи штрих-кода от BarcodeScanner клиенту, в данном случае виртуальной машине.Используя Action и перенося обработку штрих-кода на клиент, мы сохраняем BarcodeScanner полностью не осведомленными о MVVM.Windows.ApplicationModel.Core.CoreApplication.MainView делает BarcodeScanner невероятно связанным с вещами, о которых не должно заботиться.

Прежде всего, мы хотим отделить все, так что в первую очередь это интерфейс, представляющий важные биты сканера штрих-кода:

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

    }
}

Так в чем же проблема?

В итогевы как бы оказались втянутыми в проблему циклической зависимости, при которой виртуальная машина зависела от BarcodeScanner, а BarcodeScanner зависела от API представления - о чем она не должна знать.Даже с хорошей попыткой абстракций в BarcodeScanner в отношении IScannable (к сожалению, не в случае с Poste), слой сканирования делает предположения относительно типа пользователей, использующих его.Это было просто по вертикали .

С этим новым подходом вы можете очень использовать его для других типов приложений, включая консольные приложения UWP, если вам нужно.

...