Reputation: 597
I'm using the zxing barcode scanner in xamarin android forms and I can get it to scan one barcode with no issues, but I want to be able to discard the scan they have taken and have the ability to take a another scan.
I'm also using MVVM. Here is my xaml...
<Grid VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<forms:ZXingScannerView x:Name="zxingView"
IsTorchOn="{Binding TorchON}"
IsScanning="{Binding IsScanning}"
IsAnalyzing="{Binding IsAnalyzing}"
Result="{Binding Result, Mode=TwoWay}"
ScanResultCommand="{Binding ScanCommand}"
/>
<forms:ZXingDefaultOverlay
x:Name="scannerOverlay"
BottomText="Place the red line over the barcode you'd like to scan." />
<Button Grid.Row="1" Text="Toggle Flash" Command="{Binding FlashToggleCommand}"></Button>
</Grid>
And this is my page model
private string barcode = string.Empty;
public string Barcode
{
get { return barcode; }
set { barcode = value; }
}
private bool _isAnalyzing = true;
public bool IsAnalyzing
{
get { return _isAnalyzing; }
set
{
if (!Equals(_isAnalyzing, value))
{
_isAnalyzing = value;
OnPropertyChanged("IsAnalyzing");
}
}
}
private bool _isScanning = true;
private bool _torchON = false;
private DynamicContainerPageModel _hhtScreen;
private readonly IDeviceManager _deviceManager;
public ScanningViewPageModel(IDeviceManager deviceManager)
{
_deviceManager = deviceManager;
}
public override void Init(object initData)
{
base.Init(initData);
_hhtScreen = initData as DynamicContainerPageModel;
}
public bool IsScanning
{
get { return _isScanning; }
set
{
if (!Equals(_isScanning, value))
{
_isScanning = value;
OnPropertyChanged("IsScanning");
}
}
}
public bool TorchON
{
set
{
if (_torchON != value)
{
_torchON = value;
OnPropertyChanged("TorchON");
}
}
get { return _torchON; }
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public Command ScanCommand
{
get
{
return new Command(() =>
{
IsAnalyzing = false;
IsScanning = false;
Device.BeginInvokeOnMainThread(async () =>
{
Barcode = Result.Text;
var response = await CoreMethods.DisplayAlert("Barcode found", "Found: " + Result.Text, "Keep",
"Scan Again");
if (response)
{
//Save the value into the model
_deviceManager.BeginInvokeOnMainThread(() =>
{
_hhtScreen.SelectedControl.Text = barcode;
});
//close page
await this.CoreMethods.PopPageModel(false);
}
else
{
Result = null;
IsAnalyzing = true;
IsScanning = true;
}
});
IsAnalyzing = true;
IsScanning = true;
});
}
}
public Command FlashToggleCommand
{
get { return new Command(async () => { TorchON = !TorchON; }); }
}
public Result Result { get; set; }
When I press scan again on my pop up, I find it a bit hit and miss whether the scanning camera activates again or not, majority of the time it just freezes. Am I doing something wrong? Is there a better way to get the control to rescan?
Upvotes: 1
Views: 1609
Reputation: 41
How I solve this:
Xaml
<coreControls:ContainerLayout.Content>
<Grid>
<ContentView
x:Name="contentViewCamera"/>
<coreControls:SvgImage
SvgSource="img_qr_background"
VerticalOptions="FillAndExpand"
Aspect="AspectFill"/>
<Grid
x:Name="mainLayout"
RowDefinitions="48,*,*,*">
<contentViews:HeaderView Title="Canjear cupon"
TitleColor="{AppThemeBinding Light={StaticResource LightWhiteColor}, Dark={StaticResource DarkWhiteColor}}"
RightIconSvg="ic_flash_w"
Margin="6,0" />
<material:MaterialEntry
Grid.Row="3"
HorizontalOptions="Center"
VerticalOptions="Center"
WidthRequest="240"
HeightRequest="42"
BackgroundColor="#70000000"
BorderColor="#70000000"
Placeholder="Ingresa el codigo"/>
</Grid>
</Grid>
</coreControls:ContainerLayout.Content>
code behind
public partial class RedeemCouponPage
{
private ZXingScannerView _scannerView;
public RedeemCouponPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
//mainLayout.Padding = PageHelper.GetPageSafeArea(new Thickness(0, 20), this);
}
protected override void OnViewModelSet()
{
base.OnViewModelSet();
var options = new MobileBarcodeScanningOptions
{
AutoRotate = false,
TryHarder = true,
TryInverted = true,
DelayBetweenAnalyzingFrames = 5,
DelayBetweenContinuousScans = 5,
PossibleFormats = new List<BarcodeFormat>() { BarcodeFormat.QR_CODE }
};
ViewModel.InitializeCameraCommand = new MvxCommand(() =>
{
_scannerView = new ZXingScannerView()
{
Options = options,
ScanResultCommand = ViewModel.ScanResultCommand
};
_scannerView.SetBinding(ZXingScannerView.IsScanningProperty, nameof(ViewModel.IsBusy));
_scannerView.SetBinding(ZXingScannerView.IsAnalyzingProperty, nameof(ViewModel.IsBusy));
_scannerView.SetBinding(ZXingScannerView.IsTorchOnProperty, nameof(ViewModel.IsFlashActive));
_scannerView.SetBinding(ZXingScannerView.ResultProperty, nameof(ViewModel.Result), BindingMode.TwoWay);
contentViewCamera.Content = _scannerView;
});
}
}
viewModel
public class RedeemCouponViewModel: BaseViewModel
{
private readonly ICouponService CouponService = Mvx.IoCProvider.Resolve<ICouponService>();
public IMvxCommand InitializeCameraCommand { get; set; }
private ZXing.Result _result;
public ZXing.Result Result
{
get => _result;
set
{
SetProperty(ref _result, value);
BarCodeText = value.Text;
}
}
private bool _isFlashActive;
public bool IsFlashActive
{
get => _isFlashActive;
set => SetProperty(ref _isFlashActive, value);
}
private string _barCodeText;
public string BarCodeText
{
get => _barCodeText;
set => SetProperty(ref _barCodeText, value);
}
private Coupon _coupon;
public Coupon Coupon
{
get => _coupon;
set => SetProperty(ref _coupon, value);
}
public override void ViewAppeared()
{
base.ViewAppeared();
if (DeviceInfo.DeviceType == DeviceType.Physical)
InitializeCameraCommand.Execute();
IsBusy = true;
}
public IMvxAsyncCommand ScanResultCommand => new MvxAsyncCommand(async () =>
{
if (Result == null)
{
IsBusy = true;
return;
}
else
IsBusy = false;
Coupon = await CouponService.Get(BarCodeText);
PopupIsVisible = true;
}, () => IsBusy);
public IMvxCommand ConfirmRedeemCommand => new MvxCommand(()=>
{
DisplaySuccessView("Canjeado!","El cupon ha sido canjeado con exito.");
if (DeviceInfo.DeviceType == DeviceType.Physical)
InitializeCameraCommand.Execute();
PopupIsVisible = false;
IsBusy = true;
});
public IMvxCommand BackToScanerCommand => new MvxCommand(() =>
{
PopupIsVisible = false;
if (DeviceInfo.DeviceType == DeviceType.Physical)
InitializeCameraCommand.Execute();
IsBusy = true;
});
}
Upvotes: 0
Reputation: 1438
I've hit a very similar problem in the past. What I had to end up doing was defining the Scanner view in the code behind, then conditionally add/remove from the view as needed. This is what mine ended up looking like:
The XAML:
<!--
The barcode scanner grid. The actual barcode scanner is
created and added to the grid in the code behind class.
-->
<Grid x:Name="ScannerViewGrid"
Grid.Row="3"
HorizontalOptions="FillAndExpand"
IsVisible="{Binding IsBarcodeScannerRunning}"
VerticalOptions="FillAndExpand" />
The code behind:
private ZXingDefaultOverlay scannerOverlay;
private ZXingScannerView scannerView;
private void CreateNewScannerView()
{
var vm = BindingContext.DataContext as SearchViewModel;
ScannerViewGrid.Children.Clear();
scannerOverlay = null;
scannerView = null;
scannerOverlay = new ZXingDefaultOverlay();
scannerOverlay.ShowFlashButton = false;
scannerView = new ZXingScannerView();
scannerView.SetBinding(ZXingScannerView.ResultProperty, nameof(vm.BarcodeScanResult), BindingMode.OneWayToSource);
scannerView.SetBinding(ZXingScannerView.ScanResultCommandProperty, nameof(vm.BarcodeScanResultCommand));
scannerView.IsScanning = true;
ScannerViewGrid.Children.Add(scannerView);
ScannerViewGrid.Children.Add(scannerOverlay);
}
private void RemoveScannerView()
{
ScannerViewGrid.Children.Clear();
scannerView.IsScanning = false;
scannerView.IsAnalyzing = false;
scannerView.RemoveBinding(ZXingScannerView.ResultProperty);
scannerView.RemoveBinding(ZXingScannerView.ScanResultCommandProperty);
scannerView = null;
scannerOverlay = null;
}
Upvotes: 1