I have a Xamarin.Forms application. I use ZXing.Mobile.Forms to scan a QR code; however, when I point the camera to a QR code the OnScanResult event is called twice. This is my code, a faithful reproduction of the one suggested here:
private async void onScanQRCode(object sender, EventArgs e)
{
var scanPage = new ZXingScannerPage(); // executed just once
scanPage.OnScanResult += (result) => {
// Stop scanning
scanPage.IsScanning = false; // executed twice (confused)
// Pop the page and show the result
Device.BeginInvokeOnMainThread(() => {
Navigation.PopAsync();
/* Business code */
Account scannedAccount = URLInterpreter.Accept(result.Text);
ViewModel.ProcessNewAccount(scannedAccount);
/* End of business code */
});
};
// Navigate to our scanner page
await Navigation.PushAsync(scanPage);
}
And this is the XAML for the button that calls the method above:
<Button x:Name="btnScanCode" Text="Scan QR Code" Clicked="onScanQRCode" />
Any pointers?
UPDATE
Apparently, this only happens when I'm debugging the app and the phone is connected to Visual Studio. If I launch the app without attaching the debugger, the event is fired once.
I had the same issue, this answer helped me to resolve it: Xamarin.Forms ZXing.Net.Mobile loosing current page after scan result on iOS 10
Your code should be:
private async void onScanQRCode(object sender, EventArgs e)
{
var scanPage = new ZXingScannerPage();
bool scanFinished = false;
scanPage.OnScanResult += (result) => {
// Stop scanning
scanPage.IsScanning = false;
// Pop the page and show the result
Device.BeginInvokeOnMainThread(async () => {
if (!scanFinished)
{
scanFinished = true;
Account scannedAccount = URLInterpreter.Accept(result.Text);
ViewModel.ProcessNewAccount(scannedAccount);
await Navigation.PopAsync();
}
});
};
// Navigate to our scanner page
await Navigation.PushAsync(scanPage);
}
I tested on iOS 10.3.1 and ZXing.Net.Forms.Mobile 2.2.9.
Related
Here is the Code and where i am sending request to fetching the data and first time it is loading fast and when i am coming back from anuyother page then this page is taking too much time that too when i am using Device.BeginInvokeOnMainThread() within Task.Run().
==============================================================================
public Home()
{
if (HomeModel.GetInstance().GetHomeDataPopulate())
{
FireContentPageInitialized();
}
else
{
CallRequest();
}
}
private async void CallRequest()
{
await Task.Run(async () =>
{
await Task.Delay(3000);
SetBannerData();
SetBrandsData();
SetHotDealsData();
SetNewlyAddedData();
SetRecentlyViewed();
SetRecentlyPurchased();
});
}
//Sending Requests to fetching data in subscribed events...
private void SetBannerData()
{
ModelController.OnGetHomeBannerDataSuccess += ModelController_OnGetHomeBannerDataSuccess;
ModelController.OnGetHomeBannerDataFailure += ModelController_OnGetHomeBannerDataFailure;
ModelController.FetchBannerData();
}
//Fetching Data and binding with UI Itemsource...
private void ModelController_OnGetHomeBannerDataSuccess(object sender, CustomEventArgs eventArgs)
{
stackBanner.ItemsSource = null;
stackBanner.ItemsSource = HomeModel.GetInstance().GetHomeBanners();
}
==============================================================================
This is my Code..and i added comment line where i am binding UI. I can't add lots of code here because it's too lengthy so i have added only one method which is binding UI and other methods are also doing the same.
You are trying to update view by another thread. To update view created by main thread from another thread you must do something like this. Example pseudo code (I don't know your methods).
await Task.Run(() => {
try
{
Device.BeginInvokeOnMainThread(() =>
{
SetBannerData();
SetBrandsData();
SetHotDealsData();
SetNewlyAddedData();
SetRecentlyViewed();
SetRecentlyPurchased();
}
}
catch (Exception ex)
{
}
I am still beginner in Xamarin .
I created an application to connect with machine using Mobile app ( Xamarin.forms ).
I created a button , when clicked , i receive the data ( number of product produced ) in a Entry ( read only ) .
<ContentPage Title="Production Data">
<StackLayout>
<Label Text="Packs Produced"></Label>
<Entry x:Name="Packs" IsReadOnly="True"></Entry>
<Button x:Name="ReadData" Text="Read" Clicked="ReadData_Clicked"></Button>
</StackLayout>
</ContentPage>
Then
private void ReadData_Clicked(object sender, EventArgs e)
{
Packs.Text = string.Format();
}
I need to update the packs.text every 5 sec .
I did the same with another application using windows Form using timer ( when ReadData button is clicked , it enable the timer ) and timer_tick ( to read the data every 5 second).
Could this be done using xiamarin ?
I did the same with another application using windows Form using timer ( when ReadData button is clicked , it enable the timer ) and timer_tick ( to read the data every 5 second).Could this be done using xiamarin ?
If you want to do something like Timer in Xamarin.forms Android, you could try code below.
Device.StartTimer(TimeSpan.FromSeconds(30), () =>
{
// Do something
return true; // True = Repeat again, False = Stop the timer
});
I use the number i to update the value in entry each 5 seconds for your reference.
private void ReadData_Clicked(object sender, EventArgs e)
{
int i = 0;
Device.StartTimer(TimeSpan.FromSeconds(5), () =>
{
// Do something
Packs.Text = i.ToString();
i++;
return true; // True = Repeat again, False = Stop the timer
});
}
If you want to do more in IOS, UWP, you could check the code in the link.
https://xamarinhelp.com/xamarin-forms-timer/
You can implement this by easily using Matcha.BackgroundService plugin.More info https://github.com/winstongubantes/MatchaBackgroundService
You can initiate the task on every X seconds or minutes using this plugin.
In your button click, You can start the timer service like
private void Button_Tapped(object sender, EventArgs e)
{
//Register Periodic Tasks
BackgroundAggregatorService.Add(() => new PeriodicCall(5));
//Start the background service
BackgroundAggregatorService.StartBackgroundService();
Device.BeginInvokeOnMainThread( () =>
{
Packs.Text = string.Format();
});
}
Create a class named PeriodicCall and in your Periodic Call
public class PeriodicCall : IPeriodicTask
{
public PeriodicCallTest(int seconds)
{
Interval = TimeSpan.FromSeconds(seconds);
}
public TimeSpan Interval { get; set; }
public Task<bool> StartJob()
{
// Messeging center used for updating the value.
MessagingCenter.Send<Object>(new Object(), "FetchValue");
return true;
}
}
In your xaml.cs where you want to get the value
MessagingCenter.Subscribe<Object>(this, "FetchValue", async (sender) =>
{
Device.BeginInvokeOnMainThread( () =>
{
Packs.Text = string.Format();
});
});
I am using the zing mobile scanner and it works well however I am calling the same action from a different button and it's refusing to scan it brings up the preview window ok but won't invoke the scan.
public async void BtnScanStockTakeItem_Clicked(object sender, EventArgs e)
{
var scanPage = new ZXingScannerPage();
scanPage.ToggleTorch();
scanPage.IsScanning = true;
await Navigation.PushAsync(scanPage);
scanPage.OnScanResult += (result) =>
{
// Stop scanning
scanPage.IsScanning = false;
// Pop the page and show the result
Device.BeginInvokeOnMainThread(async () =>
{
await Navigation.PopAsync();
}
}
I am then calling the above from another button method lets say the saved function
private async void SaveFunction(object sender, EventArgs e)
{
foreach (var item in transferList)
{
int z = await restServices.PostStockTakeTransaction(item);
}
Preferences.Set("StockTakeWarehouse", pickStockTake.SelectedIndex);
WarehouseName = pickStockTake.SelectedItem.ToString();
bool x = await DisplayAlert("Test", "Item Saved", "ReScan", "Cancel");
if (x)
{
BtnScanStockTakeItem_Clicked(sender, e);
//this is where it rescans the item
}
}
String thing is I am getting no logcat errors or nothing the viewport of the scanner shows but just won't accept the scan some reason same barcode format as before.
For anyone else who faces a simlar problem it was the fact my camera thread was not properly called I had to do
Device.BeginInvokeOnMainThread(async () =>
{
BtnScanStockTakeItem_Clicked(sender, e);
});
Works as expected
I have a requirement for my xamarin cross platform application that as soon as app start up .QR Scanner set in to read the code. on completing scanning a beep will be ring up.and app again ready for next scanning how can i get this done. what i have done is on button click scanner start, its read code, then i have to press button again to start it again.
public HomePage()
{
Button scanBtn = new Button
{
Text = "Scan Barcode",
HorizontalOptions = LayoutOptions.FillAndExpand,
};
scanBtn.Clicked += async (sender, args) =>
{
var scanResult = await Acr.BarCodes.BarCodes.Instance.Read();
if (!scanResult.Success)
{
await this.DisplayAlert("Alert ! ", "Sorry ! \n Failed to read the Barcode !", "OK");
}
else
{
var endpoint = new EndpointAddress("http://192.168.15.33/SMS/WebServices/SMSService.svc");
var binding = new BasicHttpBinding
{
Name = "basicHttpBinding",
MaxBufferSize = 2147483647,
MaxReceivedMessageSize = 2147483647
};
TimeSpan timeout = new TimeSpan(0, 0, 30);
binding.SendTimeout = timeout;
binding.OpenTimeout = timeout;
binding.ReceiveTimeout = timeout;
_client = new SMSServiceClient(binding, endpoint);
_client.ValidateStudentAsync("123-admin");
_client.ValidateStudentCompleted += _client_ValidateStudentCompleted; ;
// await this.DisplayAlert("Scan Successful !", String.Format("Barcode Format : {0} \n Barcode Value : {1}", scanResult.Format, scanResult.Code), "OK");
}
};
Content = new StackLayout
{
Children = {
scanBtn
}
};
}
and in app.cs
public class App : Application
{
public App()
{
// The root page of your application
MainPage = new HomePage();
}
protected override void OnStart()
{
MainPage = new HomePage();
}
protected override void OnSleep()
{
MainPage = new HomePage();
}
protected override void OnResume()
{
MainPage = new HomePage();
}
}
You can use ZXing.Net.Mobile for Forms to read QR codes. To initialize this plugin you should call method to init into each project (Android, iOS, UWP) like this:
For Android in MainActivity.cs class call:
ZXing.Net.Mobile.Forms.Droid.Platform.Init();
For iOS in AppDeletage.cs class call
ZXing.Net.Mobile.Forms.iOS.Platform.Init();
And finally to read QR Codes:
private async void Scan() {
var scanPage = new ZXingScannerPage();
scanPage.OnScanResult += (result) => {
// Stop scanning
scanPage.IsScanning = false;
// Pop the page and show the result
Device.BeginInvokeOnMainThread( async () => {
await Navigation.PopAsync();
await DisplayAlert("Scanned Barcode", result.Text, "OK");
});
};
// Navigate to our scanner page
await Navigation.PushAsync(scanPage);
}
I have a problem with Xamarin.Forms ver. 2.3.4.224 and Plugin.Media ver. 2.6.2. The problem occurs after taking about 20 photos (depends from the device): basically the app crashes without any apparently reason.
If you want to replicate the error, I created a test project for you on GitHub. With my iPad Air or iPad Pro after about 30 photos (video iPad Air - iPad Pro). All devices are iOS ver. 10.3.1 and they have enough space to storage photos.
The app is very simple: you have two buttons one for taking a picture and the other one to pick a photo. If you take photos one after another, after about 20 (32 in an iPad Air) the app crashes. I'm just take photos with the Plugin.Media nothing more.
Any ideas are welcome.
Update
In my project I had a reference to Refractored.MvvmHelpers and I noticed if I remove it, I can take more pictures. I created my BaseViewModel with INotifyPropertyChanged and I noticed I can take more photos.
I created then a new project (you can find it on GitHub under cameratesteasy) without MVVM and there is just the code to take a photo like:
public partial class cameratesteasyPage : ContentPage
{
int count = 0;
public cameratesteasyPage()
{
InitializeComponent();
CrossMedia.Current.Initialize();
}
void UpdateCount()
{
count++;
CountLabel.Text = $"{count} times";
}
async void StartCameraTapped(object sender, System.EventArgs args)
{
using (var file = await CrossMedia.Current.TakePhotoAsync(
new StoreCameraMediaOptions {}))
{
if (file == null)
return;
UpdateCount();
}
}
async void StartCameraTakeTapped(object sender, System.EventArgs args)
{
var file = await CrossMedia.Current.PickPhotoAsync();
if (file == null)
return;
UpdateCount();
}
}
In this case the app shut down after 52 photos. I saved the log for Xcode and you can see it here.
I used Xamarin Profile and the memory level is always low. After about 30 photos, an error occurs in Xamarin Profiler
Finally I could create a Xamarin Profiler file
Also I noticed this kind of error occurs on iPads. The same app in an iPhone is working fine (apparently) or I didn't find up to now the number of photos before crashing.
Update /2
I decided to implement a native function for taking photo.
Interface
public interface ICamera
{
void TakePicture();
}
Implementation
using System;
using cameratest.iOS;
using Foundation;
using UIKit;
using Xamarin.Forms;
[assembly: Xamarin.Forms.Dependency(typeof(Camera_iOS))]
namespace cameratest.iOS
{
public class Camera_iOS : ICamera
{
static UIImagePickerController picker;
static Action<NSDictionary> _callback;
static void Init()
{
if (picker != null)
return;
picker = new UIImagePickerController();
picker.Delegate = new CameraDelegate();
}
class CameraDelegate : UIImagePickerControllerDelegate
{
public override void FinishedPickingMedia(
UIImagePickerController picker, NSDictionary info)
{
var cb = _callback;
_callback = null;
picker.DismissModalViewController(true);
cb(info);
}
}
public static void TakePicture(UIViewController parent,
Action<NSDictionary> callback)
{
Init();
picker.SourceType = UIImagePickerControllerSourceType.Camera;
_callback = callback;
parent.PresentModalViewController(picker, true);
}
public static void SelectPicture(UIViewController parent,
Action<NSDictionary> callback)
{
Init();
picker.SourceType = UIImagePickerControllerSourceType.PhotoLibrary;
_callback = callback;
parent.PresentModalViewController(picker, true);
}
public void TakePicture()
{
var rc = UIApplication.SharedApplication.KeyWindow.RootViewController;
TakePicture(rc, (obj) =>
{
var photo = obj.ValueForKey(
new NSString("UIImagePickerControllerOriginalImage")) as UIImage;
var documentsDirectory =
Environment.GetFolderPath(Environment.SpecialFolder.Personal);
// hardcoded filename, overwritten each time
string jpgFilename = System.IO.Path.Combine(documentsDirectory,
"Photo.jpg");
NSData imgData = photo.AsJPEG();
NSError err = null;
if (imgData.Save(jpgFilename, false, out err))
{
Console.WriteLine("saved as " + jpgFilename);
}
else
{
Console.WriteLine("NOT saved as " +
jpgFilename + " because" + err.LocalizedDescription);
}
});
}
}
}
With this code after about 30 photos, the app crashes. The only difference is with this code I can receive some alert from ReceiveMemoryWarning. If you have an interest, I updated the code on GitHub.