UWP C# Menuflyout Unable To Display Item Properly During 1st Click - c#

I am having an issue with my code to add button from json where the first attempt i click add button, the menuflyout won't have any respond but second click attempt then it will work properly.
Can advise did i do anything wrong? Thanks.
private async void AddButton_Click(object sender, RoutedEventArgs e)
{
List<ClientList> clientLists;
var jsonSerializer = new DataContractJsonSerializer(typeof(List<ClientList>));
var myStream = await ApplicationData.Current.LocalFolder.OpenStreamForReadAsync(CLIENTSLIST);
clientLists = (List<ClientList>)jsonSerializer.ReadObject(myStream);
var menuFlyout = new MenuFlyout();
int isEmpty = myGrid.Children.Count;
if (isEmpty == 0)
{
foreach (var device in clientLists)
{
var menuFlyoutItem = new MenuFlyoutItem() { Name = device.clientname, Text = device.clientname };
menuFlyoutItem.Tag = device.clientaddress;
menuFlyoutItem.Click += AddMenuFlyoutItem_Click;
menuFlyout.Items.Add(menuFlyoutItem);
}
}else
{
foreach (var device in clientLists)
{
bool toAddButton = true;
foreach (Button btn in myGrid.Children.OfType<Button>())
{
if (btn.Content.ToString() == device.clientname)
{
toAddButton = false;
}
}
if (toAddButton)
{
var menuFlyoutItem = new MenuFlyoutItem() { Name = device.clientname, Text = device.clientname };
menuFlyoutItem.Tag = device.clientaddress;
menuFlyoutItem.Click += AddMenuFlyoutItem_Click;
menuFlyout.Items.Add(menuFlyoutItem);
}
}
}
AddButton.Flyout = menuFlyout;
}

The problem is you are loading the data asynchronously here:
var myStream = await ApplicationData.Current.LocalFolder.OpenStreamForReadAsync(CLIENTSLIST);
When this happens, UI continues executing the Click event, so the button is clicked (and Flyout is null the first time) and Flyout will never display. You should rather load the Flyout before that - either when the page loads or when the data source changes, so that when the user clicks, the flyout is already there. Doing loading in Click is simply too late, if you need an asynchronous operation to finish.
Alternatively you could set the flyout right at the start:
private async void AddButton_Click(object sender, RoutedEventArgs e)
{
var menuFlyout = new MenuFlyout();
AddButton.Flyout = menuFlyout;
List<ClientList> clientLists;
var jsonSerializer = new DataContractJsonSerializer(typeof(List<ClientList>));
var myStream = await ApplicationData.Current.LocalFolder.OpenStreamForReadAsync(CLIENTSLIST);
clientLists = (List<ClientList>)jsonSerializer.ReadObject(myStream);
int isEmpty = myGrid.Children.Count;
if (isEmpty == 0)
{
foreach (var device in clientLists)
{
var menuFlyoutItem = new MenuFlyoutItem() { Name = device.clientname, Text = device.clientname };
menuFlyoutItem.Tag = device.clientaddress;
menuFlyoutItem.Click += AddMenuFlyoutItem_Click;
menuFlyout.Items.Add(menuFlyoutItem);
}
}else
{
foreach (var device in clientLists)
{
bool toAddButton = true;
foreach (Button btn in myGrid.Children.OfType<Button>())
{
if (btn.Content.ToString() == device.clientname)
{
toAddButton = false;
}
}
if (toAddButton)
{
var menuFlyoutItem = new MenuFlyoutItem() { Name = device.clientname, Text = device.clientname };
menuFlyoutItem.Tag = device.clientaddress;
menuFlyoutItem.Click += AddMenuFlyoutItem_Click;
menuFlyout.Items.Add(menuFlyoutItem);
}
}
}
}
This way, the flyout will appear, but will be empty until the asynchronous loading finishes and the items are actually added. Here you are just reading a file, so it should be barely noticeable. Although not as clean as pre-loading the flyout, it should get the job done too.

Related

Make button visible/invisible on ZXing overlay

I'm using the ZXing plugin to scan bar codes and I'm using a custom overlay to display information and make a button visible/invisible when I need to perform an action, which in this case is to set a flag and make the button invisible again.
In this code I set up the scanning plugin:
MyButton_Scan.Click += async (sender, e) =>
{
var selectedEvent = string.Format("{0}", MyEventsSpinner.GetItemAtPosition(MyEventsSpinner.SelectedItemPosition));
if (selectedEvent.ToUpper() != "SELECT EVENT")
{
MobileBarcodeScanner.Initialize(Application);
scanner = new MobileBarcodeScanner();
scanner.UseCustomOverlay = true;
zxingOverlay = LayoutInflater.FromContext(this).Inflate(Resource.Layout.scanner, null);
MyScanScreenButton = zxingOverlay.FindViewById<Android.Widget.Button>(Resource.Id.okButton);
MyScanScreenButton.Click += btnOk_Click;
MyScanScreenButton.Visibility = Android.Views.ViewStates.Invisible;
scanner.CustomOverlay = zxingOverlay;
var opt = new MobileBarcodeScanningOptions();
opt.DelayBetweenContinuousScans = 5000;
//Start scanning
scanner.ScanContinuously(this, opt, HandleScanResult);
} else
{
Utils.showMessage("Please select an event from the drop down list");
}
};
The code that handles the scan result and button click:
void btnOk_Click(object sender, System.EventArgs e)
{
popUpOpen = false;
MyScanScreenButton.Visibility = Android.Views.ViewStates.Invisible;
}
void HandleScanResult(ZXing.Result result)
{
if (!popUpOpen)
{
Boolean ConversionGood = true;
TextView MyTextView = zxingOverlay.FindViewById<TextView>(Resource.Id.ticketInfo);
Int32 convertedResult = 0;
Stream successbeepStream = GetType().Assembly.GetManifestResourceStream("eTicket_Scanner.beep.wav");
Stream failbeepStream = GetType().Assembly.GetManifestResourceStream("eTicket_Scanner.buzzer.wav");
MyTextView.SetBackgroundColor(Color.White);
try
{
convertedResult = Convert.ToInt32(result.Text);
}
catch (Exception ex)
{
ConversionGood = false;
bool isSuccess = _simpleAudioPlayer.Load(failbeepStream);
_simpleAudioPlayer.Play();
popUpOpen = true;
MyScanScreenButton.Visibility = Android.Views.ViewStates.Visible;
MyTextView.SetBackgroundColor(Color.Red );
MyTextView.Text = "Not a valid ticket for this event.\nErrorif applicable): " + ex.Message;
}
string scanResult = "";
if (ConversionGood)
{
scanResult = MyEventsService.VerifyScannedCode(MyUser.Username, MyUser.Password, currentEventID, convertedResult);
if (scanResult == "Y")
{
MyTextView.SetBackgroundColor(Color.Green);
bool isSuccess = _simpleAudioPlayer.Load(successbeepStream);
popUpOpen = true;
MyTextView.SetBackgroundColor(Color.Green);
MyTextView.Text = MyEventsService.GetTicketInfo(MyUser.Username, MyUser.Password, currentEventID, convertedResult);
_simpleAudioPlayer.Play();
MyScanScreenButton.Visibility = Android.Views.ViewStates.Visible;
}
else
{
bool isSuccess = _simpleAudioPlayer.Load(failbeepStream);
popUpOpen = true;
MyTextView.SetBackgroundColor(Color.Red);
MyTextView.Text = MyEventsService.GetFailedScanTicketInfo(MyUser.Username, MyUser.Password, currentEventID, convertedResult);
_simpleAudioPlayer.Play();
MyScanScreenButton.Visibility = Android.Views.ViewStates.Visible;
}
}
}
}
Its not a very complicated app, scan barcodes and verify the code, set the color of the textview background and display the info. The "Ok" button is never visible, but if I click in the space where the button should appear, it executes the button click code. I'm assuming that there is some sort of thread issue here with the interface, but anything I've tried with the thread hasn't worked. Anyone have any ideas?

Google Places Autocomplete does not populate the address

I have an active Google Places autocomplete working with Xamarin Forms or Cross Platform. I have a working solution that auto populates the address when the user types in the address. My problem is when the user selects it from the list the address does not go to the search_bar.text… The search bar just remains with the text that was typed? how can I get the text when selected to populate in the search bar.
I am new to Xamarin forms and C#.
public Createbusinessaccount ()
{
InitializeComponent ();
search_bar.ApiKey = GooglePlacesApiKey;
search_bar.Type = PlaceType.Address;
search_bar.Components = new Components("country:us"); // Restrict results to Australia and New Zealand
search_bar.PlacesRetrieved += Search_Bar_PlacesRetrieved;
search_bar.TextChanged += Search_Bar_TextChanged;
search_bar.MinimumSearchText = 2;
results_list.ItemSelected += Results_List_ItemSelected;
}
void Search_Bar_PlacesRetrieved(object sender, AutoCompleteResult result)
{
results_list.ItemsSource = result.AutoCompletePlaces;
spinner.IsRunning = false;
spinner.IsVisible = false;
if (result.AutoCompletePlaces != null && result.AutoCompletePlaces.Count > 0)
results_list.IsVisible = true;
}
void Search_Bar_TextChanged(object sender, TextChangedEventArgs e)
{
if (!string.IsNullOrEmpty(e.NewTextValue))
{
results_list.IsVisible = false;
spinner.IsVisible = true;
spinner.IsRunning = true;
}
else
{
results_list.IsVisible = true;
spinner.IsRunning = false;
spinner.IsVisible = false;
}
}
async void Results_List_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem == null)
return;
var prediction = (AutoCompletePrediction)e.SelectedItem;
results_list.SelectedItem = null;
var place = await Places.GetPlace(prediction.Place_ID, GooglePlacesApiKey);
if (place != null)
await DisplayAlert(
place.Name, string.Format("Lat: {0}\nLon: {1}", place.Latitude, place.Longitude), "OK");
}
In your ItemSelected method, you need to set the text of the searchbar:
async void Results_List_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem == null)
return;
var prediction = (AutoCompletePrediction)e.SelectedItem;
search_bar.Text = prediction.Name? // Your property here
results_list.SelectedItem = null;
var place = await Places.GetPlace(prediction.Place_ID, GooglePlacesApiKey);
if (place != null)
await DisplayAlert(
place.Name, string.Format("Lat: {0}\nLon: {1}", place.Latitude, place.Longitude), "OK");
}
I am still trying to fix this, it only adds the street name and number not the whole address

Set UI element visible in Xamarin C# and then call asynchronous function

Can anybody help me with this code in my Xamarin project. I am trying to set a loading wheel (to signify that an action is happening and to let the user know to wait) when the "Login" button is clicked. For some reason since the function is asynchronous the loading wheel is never set to visible when the API code is run. It just fails to show up when I click login, however, it still does the login function.
// Defined up above in the file
var loginButton = new Button
{
Text = "Login",
};
loginButton.BackgroundColor = Color.Navy;
loginButton.TextColor = Color.White;
loginButton.Clicked += OnLoginButtonClicked;
async void OnLoginButtonClicked(object sender, EventArgs e)
{
loadingWheel.IsVisible = true;
try
{
var restUrl = "*******";
var content = string.Empty;
using (var client = new HttpClient())
{
string body = "{\"UserName\":\"" + usernameEntry.Text + "\", \"Password\":\"" + passwordEntry.Text + "\"}";
var contentType = new StringContent(body, Encoding.UTF8, "application/json");
var result = client.PostAsync(restUrl, contentType).Result;
content = await result.Content.ReadAsStringAsync();
}
if (content.ToLower() != "false")
{
var menuPage = new MenuPage();
NavigationPage = new NavigationPage(new HomePage());
RootPage = new Views.MainPage();
RootPage.Master = menuPage;
RootPage.Detail = NavigationPage;
MainPage = RootPage;
}
else
{
messageLabel.Text = "Username or password incorrect. Please try again.";
passwordEntry.Text = string.Empty;
}
}
catch (Exception ex)
{
messageLabel.Text = "Please check the internet connection for the connectivity.";
}
}
If I comment out the entire try block then the loading wheel does show up. It just does not work with the code in there.
Can anybody help me solve this problem? Thanks.
I think you can try with BeginInvokeOnMainThread
Device.BeginInvokeOnMainThread (() => {
loadingWheel.IsVisible = true;
});
UPDATE
I have also create this REPO... it works without BeginInvodeOnMainThread
public class MyPage6 : ContentPage
{
ActivityIndicator _ac = new ActivityIndicator { IsVisible = false, IsRunning = false };
public MyPage6()
{
Button b = new Button {Text = "Press for ActivityIndicator" };
b.Clicked += B_Clicked;
Content = new StackLayout
{
Children = {
_ac,
b,
new Label { Text = "Hello ContentPage" }
}
};
}
async void B_Clicked(object sender, EventArgs e)
{
_ac.IsRunning = true;
_ac.IsVisible = true;
await Task.Delay(2000);
_ac.IsRunning = false;
_ac.IsVisible = false;
}
}

ZXing.Net Back Navigation C#

I am using ZXing.Net camera barcode scanner, xamarin forms, and C# and it appears to be working good. But I have an issue where if I go the the next page via Navigation.PushAsync(), then click the Back Navigation Button, the ZXingScannerPage camera will not reload...(it will only be a still image of the last pic taken)...how do I reload the ZXingScannerPage so that the camera is actively upon pressing the Back Navigation? Is the anyway to refresh the camera view attached to the page?
Use the following code. Stop the scanning as soon as scanning is done. Don't do a manual manuver.
Entry objScanner= new Entry();
objScanner.Placeholder = "Barcode";
objScanner.Keyboard = Keyboard.Numeric;
objScanner.HorizontalOptions = LayoutOptions.StartAndExpand;
objScanner.WidthRequest = Application.Current.MainPage.Width - 40;
objScanner.SetBinding(Entry.TextProperty, "ElementValue", BindingMode.TwoWay);
objScanner.BindingContext = control;
layout.Children.Add(objScanner);
objScanner.Focused += async (s, e) =>
{
var scanPage = new ZXingScannerPage();
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();
objScanner.Text = result.Text;
// await DisplayAlert("Scanned Barcode", result.Text, "OK");
});
};
};
The solution that I found that works for me to allow back navigation using ZXing Scanner page is to remove all instances of ZXing Scanner page before push a new instance of the page to the navigation stack. In your navigation.cs, when you get ready to push the page, use this:
foreach(var x in _navigation.Navigation.NavigationStack.ToList())
{
if((x.GetType() == typeof(/* name of your scanner page */)))
{
_navigation.Navigation.RemovePage(x);
}
}
var page = new /* your scanner page */();
_navigation.PushAsync( /* your scanner page */);
I have found a workaround which may be useful.
On content page, create a local content variable.
If I instantiate the scanner and add it to Content in OnAppearing method, then set Content = null OnDisappearing method. Nulling the Content seems to trigger the necessary cleanups up the stack.
Here's my code:
public class QrCodeScanPage : ZXingScannerPage
{
View _content;
public QrCodeScanPage()
{
InitScanner();
}
void InitScanner()
{
IsAnalyzing = true;
IsScanning = true;
DefaultOverlayTopText = "Align the barcode within the frame";
DefaultOverlayBottomText = string.Empty;
OnScanResult += ScanPage_OnScanResult;
Title = "Scan Code";
var item = new ToolbarItem
{
Text = "Cancel",
Command = new Command(async () =>
{
IsScanning = false;
await Navigation.PopAsync();
})
};
if (Device.RuntimePlatform != Device.iOS)
item.IconImageSource = "toolbar_close.png";
ToolbarItems.Add(item);
}
void ScanPage_OnScanResult(ZXing.Result result)
{
Device.BeginInvokeOnMainThread(async () =>
{
IsScanning = false;
IsAnalyzing = false;
await Navigation.PushAsync(new QrCodeScanResultPage());
});
}
protected override void OnAppearing()
{
IsScanning = true;
IsAnalyzing = true;
base.OnAppearing();
if (Content != null)
{
_content = Content;
}
if (Content == null)
{
Content = _content;
}
}
protected override void OnDisappearing()
{
base.OnDisappearing();
Content = null;
}
}

White tests - folder browser dialog

I have problem with getting of FolderBrowserDialog in white. I think that it should be assigned as a modal window but it isn't.
FolderBrowserDialog in DialogService.cs:
public FolderBrowserResult ShowFolderbrowserDialog(string storageFolder)
{
var dialog = new FolderBrowserDialog
{
Description = storageFolder
};
var result = new FolderBrowserResult
{
Result = dialog.ShowDialog() != DialogResult.OK,
Path = dialog.SelectedPath
};
return result;
}
Method called after click on browse button:
private void OnBrowseForTargetFolder(object sender, RoutedEventArgs e)
{
var result = dialogService.ShowFolderbrowserDialog(Properties.Resources.StorageFolder);
if (result.Result) return;
Project.PathToStorage = result.Path;
completePath = string.Format("{0}\\{1}", result.Path, Guid.NewGuid());
Directory.CreateDirectory(completePath);
}
Test:
public class LoggerTests
{
private Application application;
private MainWindowPage mainWindowPage;
[TestInitialize]
public void TestInitialize()
{
application = Application.Launch(#"PML.exe");
StartBlankApplication();
}
[TestMethod]
public void StartExistingProject()
{
mainWindowPage.StartExistingProjectButton.Click();
var modalWindows = new List<Window>();
Retry.For(() =>
{
modalWindows = mainWindowPage.applicationWindow.ModalWindows();
}, TimeSpan.FromSeconds(5));
var mod = modalWindows;
}
private MainWindowPage StartBlankApplication()
{
var appWindow = application.GetWindow("PML");
mainWindowPage = new MainWindowPage(appWindow);
return mainWindowPage;
}
private NewProjectConfigurationPage ConfigureBlankProject()
{
Window secondAppWindow = null;
Retry.For(() =>
{
secondAppWindow = application.GetWindow("PML");
}, TimeSpan.FromSeconds(5));
var newProjectConfiguration = new NewProjectConfigurationPage(secondAppWindow);
newProjectConfiguration.VesselName.Text = "Test";
newProjectConfiguration.BrowseButton.Click();
return newProjectConfiguration;
}
}
In StartExistingProject method is problem that variable mod is empty. And no FolderBrowserDialog is opened. But when I run app normally everything runs OK.
Solved - There must be setted owner to modal dialog. So
var wrapper = new WindowWrapper(this);
dialog.ShowDialog(wrapper)
solved my problem.

Categories