async function - first start first end - C# WPF - c#

i have a problem , i have a textbox controle, i activated the keyup function, so when the user write an ID in the system , the programe start searching until the it found the user.
im using it with this example code below
private async void PNationalNo_KeyUp(object sender, KeyEventArgs e)
{
string Ptext = Textbox.text;
string ResposeDataP = "";
await Task.Run(() =>
{
ResposeDataP = RunAsyncGetOnePatient(Ptext, GV.AccountID).Result;
});
Patient1 = JsonConvert.DeserializeObject<PatientM>(ResposeDataP);
if (Patient1 != null)
{
WrongMsg1.Text = "user '" + Patient1 .PatientFullName.ToLower() + "' have this ID number!";
}
else
{
WrongMsg1.Text = "there is no user have this ID!!";
}
}
the problem with this code , sometimes when the code find the result in the last async tanks , 1 of the tasks take more time than the finale one , so the programe print the result of that late function !! not the final one ?!! (( there is no user have this ID!!)), becouse its not the last function in the async code , so how can i solve this problem ?!!
Updated from comments
i mean if the user ID = "123" in the database, when i press 1 this is the first function.
Then when i press 2, the textbox = "12"
When i press 3 textbox = "123"
So it should print in the WrongMsg1.Text that the user is found, but the problem is sometimes the function of textbox = "12" finish after the function of "123", so it print in the"there is no user have this id!!".
did u understand me? because async function number 2 , finished after the last function.

It seems to be a typical race condition. Since Task.Run executes in a background thread you no longer have control over the order of execution which means that the input of the second character is evaluated after the third character. So you have to find a way to make sure the order of execution is still correct when you are back in control.
A possible solution might be to check whether the text in the text box is still the same after the async Task finished:
private async void PNationalNo_KeyUp(object sender, KeyEventArgs e)
{
string Ptext = Textbox.text;
string ResposeDataP = "";
await Task.Run(() =>
{
ResposeDataP = RunAsyncGetOnePatient(Ptext, GV.AccountID).Result;
});
if (Textbox.text != Ptext)
{
return;
}
Patient1 = JsonConvert.DeserializeObject<PatientM>(ResposeDataP);
if (Patient1 != null)
{
WrongMsg1.Text = "user '" + Patient1 .PatientFullName.ToLower() + "' have this ID number!";
}
else
{
WrongMsg1.Text = "there is no user have this ID!!";
}
}

Related

How can I execute a method after a content page is visible in Xamarin Forms?

In my Xamarin Forms app made for android as of now, I have a stack layout that gets populated based on a list that needs to fetch data from local sqlite DB. The data is around 200 rows. The stack needs to be populated when the ContentPage appears in foreground.
Since this process takes a bit of time, the navigation to this page is taking some time which leads to poor performance of app.
I have added that refresh method in constructor of page and also have tried putting it in OnAppearing override method. But the transition occurs only when the stack is populated.
Is there any way to view the page to the user first (make navigation happen) and then populate the stack?
{
private string shipmentId;
public ScanOrderPage( string shipmentID)
{
InitializeComponent();
shipmentId = shipmentID;
TapGestureRecognizer tapEvent = new TapGestureRecognizer();
tapEvent.Tapped += Scan_Button_OnClicked;
clickFrame.GestureRecognizers.Add(tapEvent);
}
protected override async void OnAppearing()
{
base.OnAppearing();
await Initiator();
}
private async Task Initiator()
{
Indicator.IsVisible = true;
bool allGood;
if (!await App.Database.IsShipmentPresent(shipmentId))
{
int getBox = await new ShipmentBoxes().Get(shipmentId);
allGood = getBox == 1;
}
else allGood = true;
if (!allGood) return;
await Populate();
Indicator.IsVisible = false;
}
private async Task Populate()
{
BoxListStack.Children.Clear();
var shipmentData = await App.Database.GetShipmentBoxes(shipmentId);
if (shipmentData == null) return;
ShipmentIDText.Text = shipmentId;
FromText.Text = shipmentData?.FirstOrDefault()?.From;
ToText.Text = shipmentData?.FirstOrDefault()?.To;
TotalBoxesText.Text = "Total Boxes: " + shipmentData.Count.ToString();
int scanCount = 0;
for (int i = 0; i < shipmentData.Count; i++)
{
string boxCode = shipmentData.ElementAt(i).BoxCode;
if (shipmentData.ElementAt(i).Scanned) scanCount += 1;
else
{
int foo = boxCode.LastIndexOf("-", StringComparison.Ordinal);
boxCode = boxCode.Substring(0, foo + 1) + "XXXX";
}
BoxListStack.Children.Add(new ScanBoxes(shipmentData.ElementAt(i).Scanned)
{
Subject = shipmentData.ElementAt(i).Subject + " " + shipmentData.ElementAt(i).SubjectClass,
BoxCode = boxCode
});
}
ScanStatusText.Text = "Boxes Scanned: " + scanCount.ToString() + " | Boxes Pending: " +
(shipmentData.Count - scanCount).ToString();
}```
It is wrong to view that your problem is in timing. Your problem is that you are executing too much long running code on the UI thread which freezes UI. Obviously you could postpone that to the moment when the page is displayed (for example using the Timer), but the problem with frozen UI would remain just it would appear in a bit less annoying form.
As your code appears to have lines that require to be running on UI thread in order to avoid crashes I can't go line by line and fix the problem.
However to run on non-UI thread you use Task.Run . If you need to switch to the UI thread after that you use InvokeOnMainThread.
Why not try infinite scrolling. Load as you scroll through the list
https://youtu.be/sZq8K_64bc0

How to save int in SharedPreferences c# when app is going to be closed?

I want to save number of clicks in SharedPreferences in moment when app going closed. Now I have function for that connected with one button
private void SaveClicks (){
var prefs = Application.Context.GetSharedPreferences("Name",FileCreationMode.Private);
var prefEditor = prefs.Edit();
prefEditor.PutInt("Key", nametest);
prefEditor.Apply();
}
"clicks" is name of int where I storage numbers of clicks in one of buttons
In what way I can do it automatically when app is going closed? Using onDestroy will be good solution?
//update
So I wrote that code:
protected override void OnDestroy()
{
var prefs = Application.Context.GetSharedPreferences("Name", FileCreationMode.Private); // 1
var prefEditor = prefs.Edit(); // 2
prefEditor.PutInt("Key", nametest); // 3
prefEditor.Apply(); // 4
}
And for counting clicks I have something like that
var prefs = Application.Context.GetSharedPreferences("Name", FileCreationMode.Private); // 1
var value1 = prefs.GetInt("key", 0);
if (clicks + value1 <= 499)
{
clicks++;
textViewBattlepackCount.Text = (clicks + value1).ToString() + " clicks!";
progressBarName1.Progress = progressBarName1.Progress + 1;
nametest= clicks + value1;
if (clicks + value1 == 500)
{
AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
alertDialog.SetTitle("You won!");
alertDialog.SetMessage("message");
alertDialog.SetNeutralButton("Ok", delegate
{
alertDialog.Dispose();
});
alertDialog.Show();
clicks = 0;
nametest= 0;
textViewTXTCount.Text = "0";
progressBarName1.Progress = progressBarName1.Progress = 0;
}
But the clicks in onDestroy() are storage only sometimes, one time there is correct number but another time after kill activity and restart app there is old number of clicks. I dont know why.
Sorry for chaotic description
It depends your requirement:
If u just want to store the numbers of click when the activity be killed. Using OnDestory()
If u want to store the numbers of click when the activity is in the background(e.g. you press the home button or another activity started), please using onPause();
onSaveInstanceState() can also be invoked before the activity destroyed, it can restore some temporary data (e.g. the text in EditText).
For your next question.
The SharedPreferences "key" should be covered when you call onDestory(), If not, please track the value of your "nametest" using debug
BTW
prefEditor.PutInt("Key", nametest);
var value1 = prefs.GetInt("key", 0);
Please using the same "Key" - -!!

How do I make one word in a text box end a program?

So one of the forms I have to create is where you enter a first and last name and then it splits the two names and puts them next to the appropriate labels, form design: https://gyazo.com/9b34dca0c1cd464fd865830390fcb743 but when the word stop is entered in any way e.g. Stop, StOp, sToP etc. it needs to end.
private void btnSeparate_Click(object sender, EventArgs e)
{
string strfullname, strgivenname, strfamilyname, strfirstname;
int int_space_location_one, int_space_location_two;
strfullname = txtFullName.Text;
int_space_location_one = strfullname.IndexOf(" ");
strgivenname = strfullname.Substring(0, int_space_location_one);
lblGivenEntered.Text = strgivenname;
strfirstname = strfullname.Remove(0, int_space_location_one + 1);
int_space_location_two = strfirstname.IndexOf(" ");
strfamilyname = strfirstname.Substring(int_space_location_two + 1);
lblFamilyEntered.Text = strfamilyname;
}
This is my current code, I have tried many different ways to get the word stop to end it but it wont work so that's why there is currently no code trying to stop the program, the main problem I get is because it is searching for a space between the first and last name and it obviously doesn't have one for one word it just crashes.
Any help with this would be amazing, thanks in advance.
Just hook up the TextChanged event and go like this:
private void TextChanged(Object sender, EventArgs e)
{
// If text, converted to lower-characters contains "stop" -> Exit
if(txtFullName.Text.ToLower().Contains("stop"))
{
// What I understand as "stopping it".
Application.Exit();
}
}
IF with "stop it" you mean to cancle the operation:
private void btnSeparate_Click(object sender, EventArgs e)
{
// If text, converted to lower-characters contains "stop" -> Exit
if (txtFullName.Text.ToLower().Contains("stop"))
{
// What I understand as "stopping it".
Application.Exit();
}
else
{
// Your code inside the else block
}
}
Short version of everything: Also covering no spaces problem
private void btnSeparate_Click(object sender, EventArgs e)
{
// Save how many words are inside
int wordsInText = txtFullName.Text.Split(' ').Length;
// Save if "stop" was typed into the textbox
bool stopExisting = !txtFullName.Text.ToLower().Contains("stop");
// If text has exactly 3 words and "stop" is NOT existing
if (wordsInText == 3 && !stopExisting)
{
// Save array of splitted parts
string[] nameParts = txtFullName.Text.Split(' ');
// This is never used??
string strfirstname = nameParts[1];
// Set name-parts to labels
lblGivenEntered.Text = nameParts[0];
lblFamilyEntered.Text = nameParts[2];
}
// If text has NOT exactly 3 words and "stop" is NOT existing
else if(wordsInText != 3 && !stopExisting)
{
// If there are no 3 words, handle it here - MessageBox?
}
// If "stop" IS existing
else if(stopExisting)
{
// If text contains "stop" handle it here
// Application.Exit(); <-- if you really want to exit
}
}
You could just check, if one of the entered words is equal to the word "stop". There you need a StringComparions which ignores the case. Or you could parse the entered word into lower/upper-cases.
So if the check is true, you could just end the program with
Environment.Exit(0);
You could just check, if one of the entered words is equal to the word "stop". There you need a StringComparions which ignores the case. Or you could parse the entered word into lower/upper-cases.
So if the check is true, you could just end the program with
Environment.Exit(0);
Code:
if (strfullname.ToLowerInvariant().Contains("stop"))
{
Environment.Exit(0);
}

Site Hangs Before Method Execution

I'm trying to build a simple application that will ask for input based on a provided set of terms. It begins with the click of a button:
protected void InventoryMoveButton_Click(object sender, EventArgs e)
{
DataDisplay.Text = "Inventory Move:";
isDataStringMode = false;
InstructionsLabel.Text = InventoryInstructions; //displays instruction text
InventoryMove();
}
Which displays some text and calls InventoryMove().
void InventoryMove()
{
string[] keyList = { "FROM", "TO", "QUANTITY" }; //moving from somewhere to somewhere
DataDisplay.Text = "hello from inventory move"; //this is a textbox
BuildScreen("Inventory Move", BuildKeyList(keyList));
}
The method provides a list of terms for which it will require values from the user to the method BuildScreen(). BuildKeyList() simply takes those terms and puts them into an ArrayList, which BuildScreen() accepts. BuildScreen() is defined as follows:
void BuildScreen(string action, ArrayList listOfKeys)
{
int input;
DataDisplay.Text += "lol hi from BuildScreen";
//DataTextBox.Text = "";
DataDisplay.Text = action + "\n";
//dataValues.Clear(); //empty previous values as they are not part of the current operation
foreach (string key in listOfKeys)
{
DataDisplay.Text += key + ": ";
input = readValidNumber();
DataDisplay.Text += input + "\n";
dataValues.Add(key, input);
}
}
The problem I'm having is that upon clicking the button, the InstructionsLabel text you see in my first bit of code never changes. So it seems the code is hanging at that point, but that could just be an error caused elsewhere before the text has the chance to change. Why is this code hanging when I click the button?

C# WinForm + Barcode Scanner input, TextChanged Error

UPDATE: SOLUTION AT END
I have a Winform, label1 will display some info returned from a SQL Search using the input (MemberID) received from barcode scanner via txtBoxCatchScanner.
Scenario is people swiping their MemberID Cards under the scanner as they pass through reception and the Winform automatically doing a Search on that MemberID and returning their info including for example "Expired Membership" etc on the receptionist's PC which has the winForm in a corner of her desktop.
I have the Below Code working fine on first swipe (eg. first person)
The number MemberID, for example 00888 comes up in the text box, ADO.NET pulls the data from SQL and displays it fine.
one thing to note maybe, the cursor is at the end of the memberID: 00888|
All good so far, THEN:
when swipe 2 (eg. next person) happens
their number (say, 00999) gets put onto the end of the first in the txtBox eg: 0088800999 so naturally when TextChanged Fires it searches for 0088800999 instead of 00999 ....
I've tried:
txtBoxCatchScanner.Clear();
and
txtBoxCatchScanner.Text = "";
and
reloading the form
at the end of my code to "refresh" the text box
but i guess they trigger the TextChanged Event
How can i refocus or ... clear the old number and cursor back to start of txtBox after the previous swipe has done its stuff...
I'm a beginner so I'm sure the code below is pretty crap....
But if anyone has time, please let me know how fix it to do what i want.
UPDATE:
Ok after much experimenting I''ve managed to get this 1/2 working now hopefully someone more experience can help me to completion! :P
if (txtBoxCatchScanner.Text.Length == 5)
{
label1.Text = txtBoxCatchScanner.Text; // just a label for testing .. shows the memmber ID
txtBoxCatchScanner.Select(0, 5);
}
SO scan 1, say 00888 , then that gets highlighted, scan 2 , say 00997 ... sweet! overwrites (not appends to) 00888 and does it's thing ... scan 2 0011289 ... DOH!!
Problem: not all barcodes are 5 digits!! they are random lengths!! Memeber ID range from 2 digit (eg. 25) to 10 digits, and would grow in the future...
Edit: Something I've discovered that is that the barcodes are read as indvidual key presses. I think this is why answer 1 below does not work and while the big probmlems:
for example with 00675 the input (?output) from the scanner is:
Down: Do
Up: Do
Down: Do
Up: Do
Down: D6
Up: D6
Down: D7
Up: D7
Down: D5
Up: D5
down: Retunn
Up: Return
other info: barcode scanner is: an Opticon OPL6845 USB
Thanks
private void txtBoxCatchScanner_TextChanged(object sender, EventArgs e)
{
Member member = new Member();
member.FirstName = "";
member.LastName = "";
//Get BarCode
//VALIDATE: Is a Number
double numTest = 0;
if (Double.TryParse(txtBoxCatchScanner.Text, out numTest))
{
//IS A NUMBER
member.MemberID = Convert.ToInt32(txtBoxCatchScanner.Text);
//SEARCH
//Search Member by MemberID (barcode)
List<Member> searchMembers = Search.SearchForMember(member);
if (searchMembers.Count == 0)
{
lblAlert.Text = "No Member Found";
}
else
{
foreach (Member mem in searchMembers)
{
lblMemberStatus.Text = mem.MemberStatus;
lblMemberName.Text = mem.FirstName + " " + mem.LastName;
lblMemberID.Text = mem.MemberID.ToString();
lblMessages.Text = mem.Notes;
if (mem.MemberStatus == "OVERDUE") // OR .. OR .. OR ...
{
lblAlert.Visible = true;
lblAlert.Text = "!! OVERDUE !!";
//PLAY SIREN aLERT SOUND
//C:\\WORKTEMP\\siren.wav
SoundPlayer simpleSound =
new SoundPlayer(#"C:\\WORKTEMP\\siren.wav");
simpleSound.Play();
}
else
{
lblAlert.Visible = true;
lblAlert.Text = mem.MemberStatus;
}
}
}
}
else
{
//IS NOT A NUMBER
lblAlert.Text = "INVALID - NOT A NUMBER";
////
//lblMemberName.Text = "";
//lblMemberID.Text = "";
//lblMemberID.Text = "";
}
SOLUTION:
The System won't let me answer my own question for another 3 hours, as I'm a newbie only 1 post, so will put here:
First thanks everyone for your help and Patience.
I Have finally figured a solition, not fully tested yet as its 2am and bed time.
following along from my updates where I had success but hit the variable length of MemberID problem. I've now overcome that with the Code below:
namespace SCAN_TESTING
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void txtBoxCatchScanner_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyValue == (char)Keys.Return)
{
e.Handled = true;
int barcodeLength = txtBoxCatchScanner.TextLength;
txtBoxCatchScanner.Select(0, barcodeLength);
//TEST
label3.Text = barcodeLength.ToString();
//TEST
label2.Text = txtBoxCatchScanner.Text;
}
}
I'll add this to my previous "real" code and test in the morning
But at this stage is doing exactly what I want! =]
Update: Tested it .. works exactly what needed:
private void txtBoxCatchScanner_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyValue == (char)Keys.Return)
{
e.Handled = true;
int barcodeLength = txtBoxCatchScanner.TextLength;
txtBoxCatchScanner.Select(0, barcodeLength);
//
//INSERT ORGINAL CODE HERE. No Changes were needed.
//
}//end of if e.KeyValue ...
}//end txtBoxCatchScanner_KeyUp
Hope that helps anyone in the future!! :)
Thanks again for the 2 very good solutions, I can see how they work, and learnt alot.
Just didn't work in my case - more likely due to myself or my error/not understanding, or scanner type.
I´m not exactly sure what the actual problem is.
txtBoxCatchScanner.Clear();
txtBoxCatchScanner.Text = "";
both trigger the "Changed" Event.
But they also clear the box. So that should be what you want to do.
You could check at the beginning if the box is actually empty, and return in case it is. Like:
if(txtBoxCatchScanner.Text == "" |txtBoxCatchScanner.Text == string.Empty)
return;
So nothing else happens, if the box is empty.
If I misunderstood your problem, please specify and I will try to help.
Regards
EDIT:
Your function should work if it looked something like this:
private void txtBoxCatchScanner_TextChanged(object sender, EventArgs e)
{
Member member = new Member();
member.FirstName = "";
member.LastName = "";
if(txtBoxCatchScanner.Text == "" | txtBoxCatchScanner.Text == string.Empty)
return; // Leave function if the box is empty
//Get BarCode
//VALIDATE: Is a Number
int numTest = 0;
if (int.TryParse(txtBoxCatchScanner.Text, out numTest))
{
//IS A NUMBER
//member.MemberID = Convert.ToInt32(txtBoxCatchScanner.Text);
member.MemberID = numTest; // you already converted to a number...
//SEARCH
//Search Member by MemberID (barcode)
List<Member> searchMembers = Search.SearchForMember(member);
if (searchMembers.Count == 0)
{
lblAlert.Text = "No Member Found";
}
else
{
foreach (Member mem in searchMembers)
{
lblMemberStatus.Text = mem.MemberStatus;
lblMemberName.Text = mem.FirstName + " " + mem.LastName;
lblMemberID.Text = mem.MemberID.ToString();
lblMessages.Text = mem.Notes;
if (mem.MemberStatus == "OVERDUE") // OR .. OR .. OR ...
{
lblAlert.Visible = true;
lblAlert.Text = "!! OVERDUE !!";
//PLAY SIREN aLERT SOUND
//C:\\WORKTEMP\\siren.wav
SoundPlayer simpleSound =
new SoundPlayer(#"C:\\WORKTEMP\\siren.wav");
simpleSound.Play();
}
else
{
lblAlert.Visible = true;
lblAlert.Text = mem.MemberStatus;
}
}
}
}
else
{
//IS NOT A NUMBER
lblAlert.Text = "INVALID - NOT A NUMBER";
////
//lblMemberName.Text = "";
//lblMemberID.Text = "";
//lblMemberID.Text = "";
}
txtBoxCatchScanner.Clear();
}
The barcode scanner you use seems to function as a HID - a keyboard emulation. Every simple barcode scanner I know (and I'm working with them on a daily basis) has the option of specifying a suffix for the scanned barcode. Change the suffix to CRLF and add a default button to your form. Scanning a barcode that ends with CRLF will then automatically "push the button".
Move the code that performs the checks from TextChanged event in to the event handler for the buttons Click event and remove the TextChanged event handler. Then, when the button is clicked, also clear the text box and set the focus back to the text box.
You should be good to go, now.
You can easily check whether the barcode scanner already has the correct suffix configured: Open up Notepad and scan some barcodes. If they all appear on separate lines, then everything's fine. Otherwise you'll need to scan some configuration barcodes from the scanner's manual.
To sum it all up, this should be the code for the button's Click event:
private void btnCheckMember_Click(object sender, EventArgs e)
{
Member member = new Member();
member.FirstName = "";
member.LastName = "";
string memberText = txtBoxCatchScanner.Text.Trim();
txtBoxCatchScanner.Text = String.Empty;
int numTest = 0;
if (String.IsNullOrEmpty(memberText) ||!Int32.TryParse(memberText, out numTest))
{
//IS NOT A NUMBER
lblAlert.Text = "INVALID - NOT A NUMBER";
return;
}
member.MemberID = numTest;
List<Member> searchMembers = Search.SearchForMember(member);
if (searchMembers.Count == 0)
{
lblAlert.Text = "No Member Found";
}
else
{
foreach (Member mem in searchMembers)
{
lblMemberStatus.Text = mem.MemberStatus;
lblMemberName.Text = mem.FirstName + " " + mem.LastName;
lblMemberID.Text = mem.MemberID.ToString();
lblMessages.Text = mem.Notes;
if (mem.MemberStatus == "OVERDUE") // OR .. OR .. OR ...
{
lblAlert.Visible = true;
lblAlert.Text = "!! OVERDUE !!";
SoundPlayer simpleSound = new SoundPlayer(#"C:\\WORKTEMP\\siren.wav");
simpleSound.Play();
}
else
{
lblAlert.Visible = true;
lblAlert.Text = mem.MemberStatus;
}
}
}
This solution avoids the following problems:
The event being triggered upon every character added/removed from the content of the text box (which is also the case when scanning a barcode: They are added one by one as if they were entered on a keyboard)
Resulting from 1. the problem that a member check is performed upon every entered character
Resulting from 2. the problem that member XYZ will never be found if there is a member XY in the database, as the check stops after finding XY
Resulting from 3. the problem that member XY will also not be found, but only member Z, because in 3. the text box is cleared and Z is the only character being entered.
The best way to clear the textBox on the next textChange event.
Insert this line
txtBoxCatchScanner.SelectAll();
at the end of TextChange function.. This will select the text, so that i can be replaced easily on the next event.

Categories

Resources