at the beginning I will say that I am a beginner in programming, but I want to make a program for work that will randomize the name of an employee. Only names that are checked in the checkbox. It currently randomizes me either from all names or from the most recently selected ones. Names so far marked as: N1, N2, N3, N4, N5:
My code below:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MNW_v._1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void groupBox1_Enter(object sender, EventArgs e)
{
}
private void N1_CheckedChanged(object sender, EventArgs e)
{
}
private void N2_CheckedChanged(object sender, EventArgs e)
{
}
private void N3_CheckedChanged(object sender, EventArgs e)
{
}
private void N4_CheckedChanged(object sender, EventArgs e)
{
}
private void N5_CheckedChanged(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
string[] tablica = new string[] { N1.Text, N2.Text, N3.Text, N4.Text, N5.Text };
Random rn = new Random();
int obj = rn.Next(0, tablica.Length);
Console.Write(obj);
if (N1.Checked)
{
MessageBox.Show(N1.Text);
}
else if (N2.Checked)
{
MessageBox.Show(N2.Text);
}
else if (N3.Checked)
{
MessageBox.Show(N3.Text);
}
else if (N4.Checked)
{
MessageBox.Show(N4.Text);
}
else if (N5.Checked)
{
MessageBox.Show(N5.Text);
}
}
}
}
I've already tried to create an additional class, but it seems that then checking the boxes does not affect the operation of the program.
I'd rather have an array of CheckBoxes, not their Texts:
using System.Linq;
...
private void button1_Click(object sender, EventArgs e) {
// So we have all CheckBoxes of interest in the collection (array)
// we can query
var boxes = new CheckBox[] {
N1, N2, N3, N4, N5
};
// How many checked CheckBoxes do we have
int count = boxes.Count(box => box.Checked);
// If we have at least one checked CheckBox, we can get random one
string randomName = count > 0
? boxes.Where(box => box.Checked).ElementAt(Random.Shared.Next(count)).Text
: ""; // If none of boxes has been checked
MessageBox.Show(randomName);
}
If Random.Shared is not supported by your version of .Net, you can use a field:
using System.Linq;
...
private static readonly Random random = new Random();
private void button1_Click(object sender, EventArgs e) {
var boxes = new CheckBox[] {
N1, N2, N3, N4, N5
};
int count = boxes.Count(box => box.Checked);
string randomName = count > 0
? boxes.Where(box => box.Checked).ElementAt(random.Next(count)).Text
: "";
MessageBox.Show(randomName);
}
Here's the simplest way I could come up with to solve the problem:
private Random rn = new Random();
private void button1_Click(object sender, EventArgs e)
{
CheckBox[] tablica =
new[] { N1, N2, N3, N4, N5 }
.Where(n => n.Checked)
.ToArray();
if (tablica.Any())
{
CheckBox cb = tablica[rn.Next(0, tablica.Length)];
MessageBox.Show(cb.Text);
}
}
It simply create an array of checkboxes that are checked from the initial set of 5 checkboxes. It then ensures at least one is in the array and displays the message for one at random.
Here is a slightly less efficient (although with 5 checkboxes the difference is negligible) way to do it that I think is slightly neater:
private void button1_Click(object sender, EventArgs e)
{
CheckBox cb =
new[] { N1, N2, N3, N4, N5 }
.Where(n => n.Checked)
.OrderBy(x => rn.Next())
.FirstOrDefault();
if (cb != null)
{
MessageBox.Show(cb.Text);
}
}
As I understand it, you want to pick one of the checked names at random.
The other answers mostly iterate a list or array every time a new value is picked. This one iterates the checkboxes only once to attach the CheckedChanged event. (This isn't such a big distinction with only 5 to pick from, but think scale!)
Now the checked state is observable, allowing a list of currently-checked checkboxes to be maintained. The change event is handled by adding or removing the sender from this list.
Then, when you click the Pick button, the _random (which is a member field, not instantiated each time) is called to to pick an index guaranteed to be less than the Count of this list-of-checked-checkboxes.
For example, this code as shown will produce N4, N3, N2, N4, N2... and so on.
For testing purposes, seeding the Random with a value of 2 when instantiated ensures the same pseudo-random sequence is generated every time. (Otherwise, the default seed value of Random is derived from the system clock and produces a new sequence every time this code is run.)
public partial class MainForm : Form
{
public MainForm() => InitializeComponent();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
buttonPick.Click += button1_Click;
foreach (CheckBox checkbox in Controls.Cast<Control>().Where(_ => _ is CheckBox))
{
checkbox.CheckedChanged += onAnyCheckedChanged;
}
}
List<CheckBox> _checkedCheckboxes = new List<CheckBox>();
private void onAnyCheckedChanged(object? sender, EventArgs e)
{
if(sender is CheckBox checkbox)
{
if(checkbox.Checked) _checkedCheckboxes.Add(checkbox);
else _checkedCheckboxes.Remove(checkbox);
Text = $"{_checkedCheckboxes.Count} Selected";
}
}
Random _random = new Random(2);
private void button1_Click(object sender, EventArgs e)
{
BeginInvoke(() => // Don't block the click event
{
if(_checkedCheckboxes.Count == 0){
MessageBox.Show("Check some names first");
}
else{
int randomIndex = _random.Next(0, _checkedCheckboxes.Count);
MessageBox.Show($"Picked: {_checkedCheckboxes[randomIndex].Text}");
}
});
}
}
Your code creates an array, generates a random number, then ignores both and simply chooses the first selected name. What you need to do is create an List of selected names
var names = new List<string>();
if (N1.Checked) names.Add(N1.Text);
if (N2.Checked) names.Add(N2.Text);
...
now generate a random index
Random rn = new Random();
int obj = rn.Next(0, names.Count);
now use that
Console.WriteLine(names[obj]);
I guess this code should help you:
public partial class Form1 : Form
{
private readonly Random random;
public Form1()
{
InitializeComponent();
random = new Random(Guid.NewGuid().GetHashCode());
}
private void ButtonClick(object sender, EventArgs e)
{
List<string> namesList = new List<string>();
foreach (Control control in Controls)
{
CheckBox checkBox = control as CheckBox;
if (checkBox != null && checkBox.Checked)
{
namesList.Add(checkBox.Name);
}
}
if (namesList.Count == 0)
{
MessageBox.Show("Check at least one option!");
}
else
{
int randomIndex = random.Next(0, namesList.Count);
MessageBox.Show(namesList[randomIndex]);
}
}
}
Quick explanation:
About new Random(Guid.NewGuid().GetHashCode()); initialisation. This code provides different random values every time you start your application.
I get all controls of form using Controls property. That way you can create any amount of checkboxes on form without editing this block of code.
Hope this will help you :)
Related
I have a GUI that is using a text field for a user int input.
a combo box that has drop downs for calculations (sum, add, div, mult. etc.)
then I click a button to calculate.
the Results show in bottom text field.
I am having trouble getting the combobox to string, so that I can make the right calculation.
The first part also needs the combobox to be selected on "Initialize" to add the value to the result text field. After that is added, all combobox choices after will utilize the initialized value in the results field and the user input field for any function calls.
namespace GUI
{
public partial class Form1 : Form
{
static void Main()
{
ApplicationConfiguration.Initialize();
Application.Run(new Form1());
}
public Form1()
{
InitializeComponent();
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
string path = #"C:\Users\calculations.txt";
StreamReader sr = new StreamReader(path);
string x = sr.ReadToEnd();
string[] y = x.Split('\n');
for (int i = 0; i < y.Length; i++)
{
comboBox1.Items.Add(y[i]);
}
}
//Button to calculate Resualt
private void button1_Click(object sender, EventArgs e)
{
string x = comboBox1.SelectedItem.ToString();
int b = int.Parse(textBox2.Text);
if(x == "Initialize")
textBox1.Text = (b).ToString();
else if (textBox1 == null)
Console.WriteLine("Please Initialize value");
if(x == "Sum")
textBox1.Text = (b + b).ToString();
}
//Result text box
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
//User number input box
private void textBox2_TextChanged(object sender, EventArgs e)
{
}
}
}
Values in calculation.txt
Initialize
Sum
Subtract
Product
Power
Log
So I have 2 list boxes within my form. Listbox1 contains different types of items that have a price and Listbox2 contains how much of that item you want to purchase. How do I update my price label so when I select both options from each list box it updates the label and gives me a price. Here's an example to help you better understand.
I select the $1.50 Chocolate Chip Cookie item in my ListBox1 and in ListBox2 I select the 1 Dozen Cookie item. So I would want my priceLabel to update to $18.00. How would I do this?
As of now I have tried creating some code in the listBox1_SelectedIndexChanged method but I am returned these 3 following values... $0.00...$2.00...$4.00
Here's my code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void label1_Click(object sender, EventArgs e)
{
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
double index = listBox1.SelectedIndex;
double index2 = listBox2.SelectedIndex;
double total = index * index2;
label9.Text = total.ToString("C");
}
private void label5_Click(object sender, EventArgs e)
{
}
private void label9_Click(object sender, EventArgs e)
{
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void monthCalendar1_DateChanged(object sender, DateRangeEventArgs e)
{
const int ESTIMATED_ARRIVAL = 3;
label10.Text = monthCalendar1.SelectionStart.AddDays(ESTIMATED_ARRIVAL).ToShortDateString();
}
private void listBox2_SelectedIndexChanged(object sender, EventArgs e)
{
}
}
In listBox1_SelectedIndexChanged(object sender, EventArgs e) you use listBox1.SelectedIndex; and listBox2.SelectedIndex;, if you refer to ListBox.SelectedIndex Property
ListBox.SelectedIndex Property
Gets or sets the zero-based index of the currently selected item in a
ListBox.
Property Value
Int32
A zero-based index of the currently selected item. A value of negative one (-1) is returned if no item is selected.
it just return index of selected item, so for your purpose you must get value of selected item.
I hope this code be a good guide for you:
Add handler of SelectedIndexChanged event of both list boxes to this method:
private void ListBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (this.listBox1.SelectedIndex > -1 && this.listBox2.SelectedIndex > -1)//You can set default SelectedIndex for list boxes and remove this
{
string s1 = this.listBox1.Items[this.listBox1.SelectedIndex].ToString();
string s2 = this.listBox2.Items[this.listBox2.SelectedIndex].ToString();
//Now we extracting the number from string
//NOTE this is a simple implementation. You must change it as your needs.
//for example
//s1 = $1.50 Chocolate Chip Cookie
//s2 = 1 Dozen Cookie
int index = s1.IndexOf(' ');//get the index of first space after 1.50 (Number) in s1
s1 = s1.Substring(1, index);
index = s2.IndexOf(' ');//get the index of first space after 1 (Number) in s2
s2 = s2.Substring(0, index);
if (double.TryParse(s1, out double p1) && double.TryParse(s2, out double p2))
{
const int DOZEN = 12;
double result = p1 * (p2 * DOZEN);
//or
//remove const int DOZEN = 12; and simply
//double result = p1 * (p2 * 12);
this.label9.Text = result.ToString("C");
}
else
{
MessageBox.Show("Can not parse double values.");
}
}
}
I have Label on my Form, for example:
lblUser.Text = "John";
I need the text to change when user clicks on it
private void lblUser_Click(object sender, EventArgs e)
{
lblUser.Text = "Mike";
}
So if user clicks again, the text should changes back. How I can switch this text unlimited number of times? Count of names can be more than 2.
Text should be changed
John -> Mike -> Jack -> John -> Mike -> Jack -> John -> ...
and so on.
First, let's organize available names into a collection (let it be an array):
private static string[] s_Names = new [] {
"John", "Mike", "Jack", "Thomas",
};
then we can change name on Click with a help of Linq:
using System.Linq;
...
private void lblUser_Click(object sender, EventArgs e) {
lbUser.Text = s_Names
.SkipWhile(name => name != lbUser.Text) // scroll up to the current name
.Skip(1) // skip current name
.FirstOrDefault() ?? s_Names.First(); // restart if s_Names is exhausted
}
Edit: Same idea, no Linq solution (we are looking for index of current name add 1 and use modulo arithmetics to restart the sequence of names)
private void lblUser_Click(object sender, EventArgs e) {
lbUser.Text = s_Names[(Array.IndexOf(s_Names, lbUser.Text) + 1) % s_Names.Length];
}
Use an array to store the Names you want it to switch from (Or list if you want to add items at runetime):
private string[] Names = new string[]{"John","Mike"}; //Etc.
Now you want to store which name is being shown with an index:
private int NameIndex = 0;
Finally, you can iterate through the array whenever the button is clicked:
private void lblUser_Click(object sender, EventArgs e)
{
lblUser.Text = Names[NameIndex%Names.Length];
NameIndex++;
}
If you have just two names that you want to switch you can do the bellow:
class Program {
private const string nameOne = "John";
private const string nameTwo = "Mike";
private void lblUser_Click (object sender, EventArgs e) {
if (lblUser.Text == nameOne)
{
lblUser.Text = nameTwo;
}else{
lblUser.Text = nameOne;
}
}
}
int nameCounter = 0; // Variable to track displayed element
var usernameList = new List<string> { "John", "Mike", "Bill", "Andy" }; // list of values to cycle
lblUser.Text = usernameList[nameCounter++];
private void lblUser_Click(object sender, EventArgs e)
{
nameCounter = nameCounter < usernameList.Count ? nameCounter : 0;
lblUser.Text = usernameList[nameCounter++];
}
checking the label and changing it based on the requirement.
private void button1_Click(object sender, EventArgs e)
{
int index = names.IndexOf(label1.Text);
index = (index + 1) % (names.Count);
label1.Text = names[index];
}
I know this questions gets asked a bit (at least from what I found here so far), but I can't really wrap my head around it. Already tried it with the example from msdn but still no succes. Here is what I'm trying to do: I have a USB-Counter connected to a TLL-ruler. I want to read the value constantly in a loop and write the readout to a textbox without blocking the main UI. I know from other questions that I should use Invoke or Backgroundworker, but have not really found an example which I understood and could use to adjust my code accordingly. The code without any modification, to keep it simple, is as follows:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace USB_Counter
{
public partial class Form1 : Form
{
[DllImport("HS_UC.dll", EntryPoint = "HS_UC_Close")] //+further DLL imports (driver for USBCounter)
public static extern int HS_UC_Close(int CounterNo);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) //initialize COunter
{
int A = HS_UC_Init(1, 3);
string B = Convert.ToString(A);
MessageBox.Show(B); //to check if there is no error
}
private void button2_Click(object sender, EventArgs e)
{
HS_UC_SetDistCode(1, 20, 40, 4, 0, 0); //set Reference
HS_UC_SetRefMode(1, 1);
}
private void button3_Click(object sender, EventArgs e)
{
int a = 1;
int A = 0; //variable that takes counter value (the one I want)
int B = 0; //variable that takes counter status
do
{
HS_UC_GetCounter(1, ref A, ref B);
decimal C = (Convert.ToDecimal(A) / 100);
textBox1.Text = "Das ist: " + C;
textBox1.Update();
} while (a == 1);
}
}
}
Now this works as intendet, but as mentioned it blocks the main UI thread (obviously). If anyone found a similar question with some helpful tips to get started with this multithreading topic or any helpful tips regarding my question directly, that would be greatly appreciated.
Best regards from Berlin,
Chris
Update: got it working with following attempt:
private void Counter_Read()
{
int a = 1;
do
{
int A = 0;
int B = 0;
HS_UC_GetCounter(1, ref A, ref B);
decimal C = (Convert.ToDecimal(A) / 100);
UpdateTextBox(C);
} while (a == 1);
}
public void UpdateTextBox(decimal C)
{
if (InvokeRequired)
{
this.Invoke(new Action<decimal>(UpdateTextBox), new object[] { C });
return;
}
textBox1.Text = "Das ist: " + C;
textBox1.Update();
}
private void button3_Click(object sender, EventArgs e)
{
System.Threading.Thread t = new System.Threading.Thread(() => Counter_Read());
t.Start();
}
From that I get a decimal output which i constantly updating and still am able to utilize the other buttons.
outsource the loop code into a method. Inside the method you will need to use BeginInvoke to write to the TextBox
private void DoTheLoop()
{
int a = 1;
int A = 0; //variable that takes counter value (the one I want)
int B = 0; //variable that takes counter status
do
{
HS_UC_GetCounter(1, ref A, ref B);
decimal C = (Convert.ToDecimal(A) / 100);
textBox1.BeginInvoke(new Action(()=>{textBox1.Text = "Das ist: " + C;}));
} while (a == 1);
}
First version using a normal Thread:
Create a Thread and start it with the new method when the button3 is clicked
private void button3_Click(object sender, EventArgs e)
{
System.Threading.Thread t = new System.Threading.Thread(()=>DoTheLoop());
t.Start();
}
This should not block your GUI and the textbox will show the values
Second Version using a BackgroundWorker:
Create a BackgroundWorker and register the DoWork event:
System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
private void Form1_Load(object sender, EventArgs e)
{
worker.DoWork += Worker_DoWork;
}
inside the eventhandler call the same method DoTheLoop():
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
DoTheLoop();
}
start the worker in the button click event:
private void button1_Click(object sender, EventArgs e)
{
worker.RunWorkerAsync();
}
Same result in the end :)
You may want to take a look a this link MSDN.
However, a quick tip would be to register a method for the DoWork event and then execute the RunAsynchronously method.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Mod
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
int c = 0;
private void button1_Click(object sender, EventArgs e)
{
TextBox txtRun = new TextBox();
txtRun.Name = "txtDynamic" + c++;
txtRun.Location = new System.Drawing.Point(20, 18 + (20 * c));
txtRun.Size = new System.Drawing.Size(200,15);
this.Controls.Add(txtRun);
}
private void button2_Click(object sender, EventArgs e)
{
List<string>tilelocation = List<string>();
tilelocation.Add(); //What goes in this method's arguments?
}
}
}
Here is my code. Button1 creates a theoretically infinite # of textboxes, but I wish to add the text in these dynamically generated textboxes to a list. How can this be done?
[EDIT]
And how can I display them all in a messagebox, each on separate lines?
You need to keep a reference to the control.
The other secret is that you have to keep it in the ViewState so it's available between post backs.
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
int c = 0;
private List<TextBox _lstTextBoxList;
public List<TextBox> lstTextBoxList {
get {
if(_lstTextBoxList == null) {
_lstTextBoxList = ViewState["lstTextBoxList"] as List<TextBox>;
}
return _lstTextBoxList;
}
set { ViewState["lstTextBoxList"] = _lstTextBoxList = value; }
}
private void button1_Click(object sender, EventArgs e) {
TextBox txtRun = new TextBox();
txtRun.Name = "txtDynamic" + c++;
txtRun.Location = new System.Drawing.Point(20, 18 + (20 * c));
txtRun.Size = new System.Drawing.Size(200,15);
this.Controls.Add(txtRun);
lstTextBoxList.Add(txtRun);
}
private void button2_Click(object sender, EventArgs e) {
// Not sure of your goal here:
List<string> tilelocation = List<string>();
tilelocation.Add(lstTextBoxList[lstTextBoxList.Count - 1]);
// I would assume you wanted this:
List<string> strValues = lstTextBoxList.Select<TextBox,string>(t => t.Text).ToList();
}
}
but I wish to add the text in these dynamically generated textboxes to
a list. How can this be done?
You should use new List<string> like:
private void button2_Click(object sender, EventArgs e)
{
List<string> tilelocation = new List<string>();
foreach(TextBox tb in this.Controls.OfType<TextBox>().Where(r=> r.Name.StartsWith("txtDynamic"))
titlelocation.Add(tb.Text);
//if you want a string out of it.
string str = string.Join(",", titlelocation);
MessageBox.Show(str);
}
EDIT: For each text on new line use:
string str = string.Join(Environment.NewLine, titlelocation);
MessageBox.Show(str);
I don't know why you want to use a seperate button2 click event to add the text. Why don't you use a global variable tilelocation, and add the text to this list in the button1_click event? Something like:
txtRun.Text=Your Text;
tilelocation.add(Your Text);
If you want to display them in a message box, then add codes:
string str="";
foreach(text in tilelocation)
{
str+=text;
}
MessageBox.Show(str);