Is this button action programmed correctly - c#

Take a look at my first app, am I on the right track? I have a simple form, user enters value to be looked up in an Array. If found return value with index. I use VS 2019/C#.
private async void ButtonSingleGetValues_Click(object sender, RoutedEventArgs e)
{
//Tasks:
//Get Value from TextBox on Form
//Validate TextBox (TextBoxSingleArray.Text) is a number
//Check if Number exists in Array
//Number Exists, Return Number to Form TextBox to show it exists
//then return Index of Number to Form TextBox in Array
//Here is the single Array
int[] singleDimension = new int[5] { 1, 5, 10, 15, 20 };
//Grab value from user input textbox for single array
bool intSingleTryParse = int.TryParse(TextBoxSingleArray.Text, out int ValueSingle);
if (intSingleTryParse) //bool is true, continue
{
int ArrayValue = Array.Find(singleDimension, element => element == ValueSingle);
if (ArrayValue > 0)
{
TextBoxSingleArrayValue.Text = ArrayValue.ToString();
int ArrayPosition = Array.IndexOf(singleDimension, ValueSingle);
TextBoxSinglePosition.Text = ArrayPosition.ToString();
}
else
{
MessageDialog MsgNotFound = new MessageDialog("Did not find number in array!");
await MsgNotFound.ShowAsync();
}
}
else
{
MessageDialog MsgNope = new MessageDialog("You not a number!");
await MsgNope.ShowAsync();
}
}
}
}

My version would be:
private async void ButtonSingleGetValues_Click(object sender, RoutedEventArgs e)
{
//I wouldn't use the word "single" in an array of ints because single is a floating point number type
//use plurals for collections
//do not need to specify array size with initializer
int[] numbers = new int[] { 1, 5, 10, 15, 20 };
//name your result bool more simply, do not mention single
//name your text box more simply
//windows controls like textbox tend to be named with the textbox word at the end
bool success = int.TryParse(FindIntTextBox.Text, out int findInt);
//personally, I prefer "test bad input, then return if it's bad"
//instead of ending up with nested nested code blocks that run for screens at a time
//it's kinda anathema to the "never use return mid method" crowd though
if (!success)
{
await new MessageDialog("Not a valid integer in the ... box").ShowAsync();
return;
}
//local variables are namedLikeThis, private are typically _namedLikeThis and public are NamedLikeThis
//do not use Array.Find on simple ints etc: you know what you're finding
//array.Find would be used for looking up eg a person by name
//you already know the value to find and just want its index
int foundIndex = Array.IndexOf(numbers, findInt);
//If there is a way to structure a test so that there is
//either a resulting one line operation to perform or a ten line op, i
//tend to place the the shorter code in the if and the longer
//code in the else. This way the condition that applies to
//the else is likely still visible on screen whereas if the if is
//long the else can lose meaning without a scroll. I could
//also have used the "if bad return" here but I do subscribe
//to "avoid return mid method if possible" and we've already started our processing
if(foundIndex == -1)
await new MessageDialog("not a known number").ShowAsync();
else
{
//not sure why you'd bother unless the int parse tolerated eg spaces
FindIntTextBox.Text = findInt.ToString();
FoundIndexTextBox.Text = foundIndex.ToString();
}
}
}
I'd make the async await calls a single line await x.YAsync(z); rather than declaring a variable just for the task. We tend to only do that when we need to create a task, do some more work, then await the task

Related

Comparing string from label and input(textBox) and showing result in label. Plus operation on array

I'm writing a FlashCard app in Windows Form.
Right now I'm trying to do is read word from string array and pass it to label. Then asking user to write the translation of this word. And finally pass the result to label box.
Here is my code:
public partial class EnglishPolishScreen : Form
{
//English words array
string[] words = new string[] { "word1", "word2", "word3", "word4" };
// meanings words array
string[] wordB = new string[] { "slowo1", "slowo2", "slowo3", "slowo4" };
int element = 0;
Thread thread;
public EnglishPolishScreen()
{
InitializeComponent();
}
private void CloseAppEvent(object sender, FormClosingEventArgs e)
{
Application.Exit();
}
private void button1_Click(object sender, EventArgs e)
{
thread = new Thread(Threadd);
thread.Start();
}
private void Threadd()
{
englishWord.Text = words[element];
counterLabel.Text = element + 1 + " of " + words.Length;
if (answerBox.Text.Equals(wordB[element]))
{
resultLabel.Text = "Good";
element++;
}
else
resultLabel.Text = "Bad";
if (element == words.Length)
{
element = 0;
}
}
private void EnglishPolishScreen_Load(object sender, EventArgs e)
{
englishWord.Text = words[element];
}
Edited
Guys, Why I have to click two times in button to see next item from array? And why I can see "bad" answer straight after I click button? The "Good" answer shows up after second click.
Edited v2.xD
Sorted. Anyway is it good way to write code like this? If not how It could look better? Thanks
Regards
On button click, it is going through the whole for loop, i.e. the entire list of meanings, you would have to break the for loop as soon as a right match is found. So just use break; after resoultLabel.Text = "Good answer!";. Also as pointed out by #Neil, it is not good to use UI in a separate background thread.
By the way, I am not able to wrap my head around the logic of chances. For giving chances you would have to declare a global variable which would get added/subtracted when the a bad answer is found after Iterating through whole for loop, disallowing any further trial for the word.

How can I increase one of two numbers in a comparative statement with a button click

I'm making a little UWP app in C# that simulates drawing straws. The problem that I'm having is that I want the user to get to a point where they click a button, and the variable baseNumber (initially set to 2 -- the fewest amount of drawn straws to make any sense) is compared to a randomly generated number that is passed through from another button event. I have this part down.
The part that is giving me fits is that I want the next user to then click the same button, and I want the baseNumber incremented by 1, and have that new number compared to the losingStraw. If that user is declared safe, I want the number to increment again for the next user with their button click, until ultimately the numbers equal each other, a user draws the short straw, and another code path is taken.
I'm very new to coding, so what I want might not even be possible. Here's an example of what I've tried, below. I have also tried variants on a do / do while and a for loop.
If I could somehow stop something like a for loop at each pass and make it wait for user input (the button press), that would be ideal. I couldn't figure that out either though.
Any help or ideas you can offer would be greatly appreciated!
private void drawButton_Click(object sender, RoutedEventArgs e)
{
int baseNumber = 2;
int losingStraw = Convert.ToInt32(drawButton.Tag);
if (baseNumber < losingStraw)
{
instructions.Text = "You are safe!";
baseNumber = baseNumber++; // that doesn't work at all. I was hoping that baseNumber
}
else
{
instructions.Text = "You have drawn the losing straw";
}
}
You have to write baseNumber++; instead of baseNumber = baseNumber++; since ++ aka the unary operators helps increases integer value by one. Thus baseNumber++; means baseNumber = baseNumber + 1;
Edit
In your case the variable int baseNumber = 2; has been declare and initialized in the event itself thus every-time the event is fired the baseNumber will be initialized by 2 and you would not be able to see any changes. Thus you can do something like this
int baseNumber; // you can do 2 here as well
public YourFormName()
{
baseNumber = 2;
}
private void drawButton_Click(object sender, RoutedEventArgs e)
{
int losingStraw = Convert.ToInt32(drawButton.Tag);
if (baseNumber < losingStraw)
{
instructions.Text = "You are safe!";
baseNumber = baseNumber++; // that doesn't work at all. I was hoping that baseNumber
}
else
{
instructions.Text = "You have drawn the losing straw";
}
}
Just put the baseNumber outside the method, can be globally declared so that everytime you increment it, it will never to reset to 2 again when entered on butotn click :) -HAPPY CODING-
public class YourClass
{
int baseNumber = 2;
private void drawButton_Click(object sender, RoutedEventArgs e)
{
int losingStraw = Convert.ToInt32(drawButton.Tag);
if (baseNumber < losingStraw)
{
instructions.Text = "You are safe!";
baseNumber = baseNumber++; // that doesn't work at all. I was hoping that baseNumber
}
else
{
instructions.Text = "You have drawn the losing straw";
}
}
}

How to progress my Five Dice game

I am learning to program, in particular starting with C# where I have been experimenting with dice games to become more familiar and to improve myself. I am now trying to create a basic game in windows forms, where two players roll 5 dice and keep record of their score.
Rules:
Begin with 5 dice
If a 1 or 4 appears, player scores no points and those dice are removed. Otherwise add all the dice to total
Continue with remaining dice until there a no dice left over
So far I have an Image array which stores all my dice images in my resources and a button which will roll the dice. What I need help with specifically is being able to remove the penalized dice (setting that particular one back to blank) and allowing the player to continue rolling their remaining dice until none are left.
At the moment I am unsure as to where I can further this and maybe I am biting off more than I can chew. I am loving coding and any help would be much appreciated.
Here's an image of the interface:
public partial class Form1 : Form
{
Image[] diceImages;
int[] dice;
int[] diceResults;
Random random;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
diceImages = new Image[7];
diceImages[0] = Properties.Resources.blank;
diceImages[1] = Properties.Resources.one;
diceImages[2] = Properties.Resources.two;
diceImages[3] = Properties.Resources.three;
diceImages[4] = Properties.Resources.four;
diceImages[5] = Properties.Resources.five;
diceImages[6] = Properties.Resources.six;
dice = new int[5] { 0, 0, 0, 0, 0 };
random = new Random();
diceResults = new int[6] { 0, 0, 0, 0, 0, 0 };
}
private void btn_rollDice_Click(object sender, EventArgs e)
{
RollDice();
GetResults();
ResetResults();
}
private void RollDice()
{
for (int i = 0; i < dice.Length; i++)
{
dice[i] = random.Next(1, 7);
switch (dice[i])
{
case 1:
diceResults[0]++;
break;
case 2:
diceResults[1]++;
break;
case 3:
diceResults[2]++;
break;
case 4:
diceResults[3]++;
break;
case 5:
diceResults[4]++;
break;
case 6:
diceResults[5]++;
break;
}
}
lbl_dice1.Image = diceImages[dice[0]];
lbl_dice2.Image = diceImages[dice[1]];
lbl_dice3.Image = diceImages[dice[2]];
lbl_dice4.Image = diceImages[dice[3]];
lbl_dice5.Image = diceImages[dice[4]];
}
private void GetResults()
{
bool oneRoll = false, fourRoll = false;
for (int i = 0; i < diceResults.Length; i++)
{
if (diceResults[i] == 1 && diceResults[i] == 4)
{
oneRoll = true;
fourRoll = true;
}
}
}
private void ResetResults()
{
}
}
The code you posted has at least a couple of oddities that don't seem to fit with your description:
The code simply increments an element in an array (diceResults) when a given die value is rolled (i.e. the element corresponds to the die value, not the die's order in the game). From your description, I would have expected the code to simply add the die value to a single sum variable.
In your GetResults() method, your code compares the individual element values in the diceResults to the values 2 and 5. In other words, for each possible die value, if that value comes up twice or five times, you set both flags. There are a number of reasons this is strange, but the biggest, most obvious one is that a single variable (i.e. the element diceResults[i]) can never have two different values at the same time. I.e. that array element will never be both 2 and 5, as the if statement requires.
Given those issues, I'm more inclined to focus on the original specification than to trust the code too much in terms of trying to understand what your intended behavior of the code actually is. :)
It seems that the basic question is how best to remove die from play. The suggestion (in the comments above) to use a list to track the dice is certainly a workable one. In that approach, one would iterate through the list to set each element, and if the roll for a given element ever comes up as 1 or 4, remove that element before moving on.
Having done that, one would just iterate through the list again to set the die value images, using the "blank" image for any die beyond the length of the list.
But there is a simpler way, and based on your statement "setting that particular one back to blank", which seems to imply that each blank die should appear in the same position in which it was rolled, it seems like that simpler way might be preferable to you.
Specifically, after rolling the dice, just scan through the dice array and reset any 1 and 4 values to 0, and use this 0 value as a special "sentinel" value to indicate that die is now blank.
Note that however you do this (with a list, or just setting values to 0), there is also the question of whether to show the user the actual 1 and 4 rolls, or to immediately set those rolls to a blank die. I'm going to assume the former, but it would be very easy to implement it the other way instead. (As always, the beginning of good code is a good specification…right now, your specification is a bit light on detail, and thus is vague and ambiguous).
Taking that approach, your code might look something more like this:
public partial class Form1 : Form
{
#region Declaration
Image[] diceImages;
Label[] labels;
int[] dice;
int diceTotal;
bool checkOnesAndFours;
Random random;
#endregion
#region Initialiazation;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Initializing an array this way eliminates the chance of having
// a typo in the array index for the assignment.
diceImages = new Image[]
{
Properties.Resources.blank,
Properties.Resources.one,
Properties.Resources.two,
Properties.Resources.three,
Properties.Resources.four,
Properties.Resources.five,
Properties.Resources.six
};
// Arrays are always initialized with the elements having their default
// values, so there's no need to specify `0` values for `int` arrays explicitly
dice = new int[5];
random = new Random();
diceTotal = 0;
// For the purposes of setting the dice images, it will be helpful
// to keep the control references in an array. This is both convenient
// and, again, helps guard against typographical errors
labels = new Label[]
{
lbl_dice1,
lbl_dice2,
lbl_dice3,
lbl_dice4,
lbl_dice5
};
}
#endregion
#region Private Methods
private void btn_rollDice_Click(object sender, EventArgs e)
{
RollDice();
}
private void RollDice()
{
bool rolledOneOrFour = false;
int rollTotal = 0;
for (int i = 0; i < dice.Length; i++)
{
if (checkOnesAndFours)
{
// First, clear any 1 or 4 from the previous roll
if (dice[i] == 1 || dice[i] == 4)
{
dice[i] = 0;
}
// Then, ignore any blank die
if (dice[i] == 0)
{
continue;
}
}
dice[i] = random.Next(1, 7);
if (dice[i] == 1 || dice[i] == 4)
{
rolledOneOrFour = true;
}
rollTotal += dice[i];
}
if (!rolledOneOrFour)
{
diceTotal += rollTotal;
}
checkOnesAndFours = true;
for (int i = 0; i < labels.Length; i++)
{
labels[i].Image = diceImages[dice[i]];
}
}
#endregion
}
NOTE: it was not entirely clear to me what you mean to do when a 1 or 4 comes up. Taking what you wrote literally, I understand it to mean that if any die shows a 1 or 4 on a roll, that none of the dice count for that roll. The above code is implemented with that understanding in mind.
It occurs to me that you might have instead meant that only the dice that show 1 or 4 are not counted for the roll, and that the other dice for that roll are still included. It would not be hard to change the above to accommodate that alternative specification.
NOTE: you'll also notice that I have made other changes to the code not technically required in order to address the immediate question. I added comments in the code itself to try to explain why I made those changes, and why I feel they make the code better.
Just for grins, here's a version that does use a list instead:
public partial class Form1 : Form
{
#region Declaration
Image[] diceImages;
Label[] labels;
List<int> dice;
int diceTotal;
bool checkOnesAndFours;
Random random;
#endregion
#region Initialiazation;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Initializing an array this way eliminates the chance of having
// a typo in the array index for the assignment.
diceImages = new Image[]
{
Properties.Resources.blank,
Properties.Resources.one,
Properties.Resources.two,
Properties.Resources.three,
Properties.Resources.four,
Properties.Resources.five,
Properties.Resources.six
};
// Lists must be initialized explicitly with their initial values, as by default
// they are initially empty.
dice = new List<int>(Enumerable.Repeat(0, 5));
random = new Random();
diceTotal = 0;
// For the purposes of setting the dice images, it will be helpful
// to keep the control references in an array. This is both convenient
// and, again, helps guard against typographical errors
labels = new Label[]
{
lbl_dice1,
lbl_dice2,
lbl_dice3,
lbl_dice4,
lbl_dice5
};
}
#endregion
#region Private Methods
private void btn_rollDice_Click(object sender, EventArgs e)
{
RollDice();
}
private void RollDice()
{
bool rolledOneOrFour = false;
int rollTotal = 0;
for (int i = 0; i < dice.Count; i++)
{
// Clear any 1 or 4 from the previous roll
if (checkOnesAndFours && (dice[i] == 1 || dice[i] == 4))
{
// Remove this die from play
dice.RemoveAt(i);
// The next list element to examine is now at the current i value
// and the for loop is going to increment i when the continue
// is executed, so decrement i in anticipation of that
// so that the loop moves on to the correct next element
i--;
continue;
}
dice[i] = random.Next(1, 7);
if (dice[i] == 1 || dice[i] == 4)
{
rolledOneOrFour = true;
}
rollTotal += dice[i];
}
if (!rolledOneOrFour)
{
diceTotal += rollTotal;
}
checkOnesAndFours = true;
for (int i = 0; i < labels.Length; i++)
{
labels[i].Image = i < dice.Count ? diceImages[dice[i]] : diceImages[0];
}
}
#endregion
}
Finally, note that neither of the above addresses a couple of other issues with the code:
Initializing the dice labels at the beginning of a game.
Resetting the entire game once the game has ended (i.e. there are no more dice left).
I leave these two items as an exercise for the reader (of course, if you run into problems with those specific issues, you may always post another question on Stack Overflow asking about each specifically).

Collection was modified in foreach loop C#

I know there are MANY similiar questions, but I can't seem to get to the bottom of this.
In my program I execute a verification method which should compare two ascii HEX files with eachother (one is local, the other is read from a USB device). Some code:
private void buttonVerify_Click(object sender, EventArgs e)
{
onlyVerifying = true;
Thread t = new Thread(verifyProgram);
}
private void verifyProgram()
{
verifying = true;
externalFlashFile.Clear();
// After this method is finished, the returned data will end up in
// this.externalFlashFile since this listen to the usb's returned data
hexFile.readExternalFlashForVerify(usbDongle, autoEvent);
externalFlashFile.RemoveAt(0);
//externalFlashFile.RemoveAt(externalFlashFile.Count - 1);
hexFile.verifyProgram(externalFlashFile);
}
public void verifyProgram(List<string> externalProgram)
{
byte[] originalFile = null; // Will be modified later with given size
byte[] externalFile = new byte[4096];
int k = 0, errors = 0;
// Remove last line which contains USB command data
externalProgram.RemoveAt(externalProgram.Count - 1);
foreach (String currentLine in externalProgram)
{
for (int i = 0; i < 64; i += 2)
{
string currentDataByte = currentLine.Substring(i, 2);
externalFile[k] = Convert.ToByte(currentDataByte, 16);
k++;
}
progress += steps;
}
//... compare externalFile and originalFile
When executing the readExternalFlashForVerify the USB is responding with requested data. This data is parsed and calls an eventhandler:
public void usbDongle_OnDataParsed(object sender, EventArgs e)
{
if (verifying)
{
usbDongle.receivedBytesString.Trim();
externalFlashFile.Add(usbDongle.receivedBytesString.Substring(2, 32 * 2));
// Allow hexFile continue its thread processing
autoEvent.Set();
}
}
The first run is always completes correctly. The following executions, at the third or fourth iteration of the foreach, I get an extra element in externalProgram. This is not a global variable (argument in function call) and the function is not called anywhere else. This ofcourse throws an exception.
I tried adding .ToList() to externalProgram in the foreach but that didn't do any difference. How can my externalProgram be modified during this execution?
EDIT: I never found the cause of this, but replacing the foreach with a hard-coded for-loop solved the issue at hand. Not an optimal solution, but don't have much time on this.
// The list should never be larger than 128 items
for (int j = 0; j < 0x7f ; j++)
{
string currentLine = externalProgram[j];
// ...
Usually when you receive an exception with a message like that it is caused by multiple accesses from different threads to a list.
What I suggest you is to use a lock when you add and remove items from that list, so you're sure the indexes to that collection are not changing. You have to think what would happen if you try to remove the last element (of index 3, for example) of a collection when someone else removes a previous item (changing the lenght of the collection to 3...).
This example: Properly locking a List<T> in MultiThreaded Scenarios? describes better what I mean.
Probably this line is a problem:
externalProgram.RemoveAt(externalProgram.Count - 1);
If verifyProgram is called multiple times, it will remove more and more lines from externalProgram list passed by reference

textbox to accept integers

I've found different help through this website but still can't seem to convert a string to int. I've tried many different ways. Here are two of them. On button_click I need to read the textboxes and convert them to int so I can perform standard logic on them. (a > b functions). Below the first section is what I'm using to force the use of numbers during entry into the text boxes.
private void write_button_Click(object sender, EventArgs e)
{
int mat_od1 = int.Parse(matod_box.Text); //Input string in wrong format.
int mat_id1 = int.Parse(matid_box.Text);
int fod1 = int.Parse(fod_box.Text);
int fid1 = int.Parse(fid_box.Text);
int hp1 = int.Parse(hp_box.Text);
//This next section is just to show something else I've tried.
decimal mat_od = Convert.ToDecimal(matod_box.Text); //Same error.
decimal mat_id = Convert.ToDecimal(matid_box.Text);
decimal fod = Convert.ToDecimal(fod_box.Text);
decimal fid = Convert.ToDecimal(fid_box.Text);
decimal hp = Convert.ToDecimal(hp_box.Text);
decimal pass_od = mat_od;
}
private void fod_box_TextChanged(object sender, EventArgs e)
{
try
{
int numinput = int.Parse(fod_box.Text);
if (numinput < 1 || numinput > 500)
{
MessageBox.Show("You must enter a number between 0 and 500.");
}
}
catch (FormatException)
{
MessageBox.Show("You need to enter a number.");
fod_box.Clear();
}
Any help would be appreciated.
instead of int.Parse() you should use int.TryParse(string,out int)
this way you would be able to chek the output and decide wether the string was correctly parsed or not
int i;string s="";
if(int.TryParse(s,out i))
{
//use i
}
else
{
//show error
}
The int.parse conversion should work, as in this sample:
string s = "111";
int i;
if (int.TryParse(s, out i))
{
Console.Write(i);
}
else
{
Console.Write("conversion failed");
}
Are you sure you actually provide legal input for your ints? In any case, you should use TryParse like I did in my sample. Theres no need to use try..catch where you can use boolean methods provided by the framework, which will get you the same result..
All depends on what you are allowing to be put in the text box.
If it might not be a string that can be converted to an integer, including blank, then something like
int value;
if (int.TryParse(SomeString, out value)
{
// it is an int
}
else
{
// it's not an int, so do nothing raise a message or some such.
}
In addition to using Int32.TryParse in the button Click event handler as others have pointed out, you need to be careful what you're doing in the TextBox Changed event handler. You're code here is flawed:
private void fod_box_TextChanged(object sender, EventArgs e)
{
try
{
int numinput = int.Parse(fod_box.Text);
...
}
catch (FormatException)
{
MessageBox.Show("You need to enter a number.");
fod_box.Clear();
}
Calling foo_box.Clear() will clear any text from the textbox, calling the TextChanged handler to be executed again (unless the TextBox was already empty). So if you enter a non-numeric value, your message box will be displayed twice - the first time when it attempts to parse your non-numeric value, the second time when it attempts to parse an empty string as a result of the call to Clear().
In general, I'd avoid doing validation in the Changed event handler.

Categories