我当前在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中使用返回的值?
答案 0 :(得分:2)
使用MVVM时,WPF开发人员也存在类似的模式,您需要获取/更新视图模型(VM)公开的模型。也许它们在数据库中。不必使用丑陋的DB代码来污染漂亮的VM,而是可以将“服务”传递到VM中。现在,“服务”并不一定意味着SOA /微服务,可能只是不同项目中的另一类。关键是您将所有条形码物品放在此处,当收到物品时,它可能会触发您的VM监听的事件,或者只是将其排队等待VM可以通过服务接口请求。
我已经在服务类中拥有所有条形码代码,并且出现了问题,因为我不希望服务类更新我的当前模型。我遇到的主要问题是我不知道如何使我的VM监听DataReceived事件
好吧,据我所知,您的服务并未与UWP MVVM脱钩。对于该事件,您是否考虑过纯粹为VM客户端公开次事件?我觉得这很适合我。
就像VM中的事件一样,监听数据接收事件?
是的,但是不必只是物理event
类型的列表而已。 C#事件暗示可以有多个订阅者,这对于条形码应用程序实际上没有任何意义。应该只有一个前台阅读器。
在这里,我将使用Action<string>
将条形码从BarcodeScanner
传递到客户端(在本例中为VM)。通过使用Action
并将条形码处理移至客户端,我们使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;
}
接下来,我们添加一些命令处理程序。想象一下,我们有一个按钮,单击该按钮即可启动条形码订阅。将以下内容添加到虚拟机:
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控制台应用程序。