I've been creating a autocheckout bot, and I'm very new to C#, and coding in general. I've gotten pretty far, almost done with the program, or so I thought, and now I want to have the ability to create multiple tasks, or run the task method multiple times. I also want to be able to input a different "profile", which has defined strings, such as login email, password, ect., on each task seperately. I'm very stuck and I have no idea where to even begin, maybe if someone could point me in the right direction for me to get started? last time I got some amazing help from this community, and it helped me a lot. Here is my current attempt:
public void KeywordTask1()
{
Start();
LogIn();
FindProductByKeyword();
stopwatch.Start();
AddToCart();
Checkout();
TimeSpan CheckoutTime = stopwatch.Elapsed;
}
public void KeywordTask2()
{
Start();
LogIn();
FindProductByKeyword();
stopwatch.Start();
AddToCart();
Checkout();
TimeSpan CheckoutTime = stopwatch.Elapsed;
}
I have buttons that start those tasks, but I also want the varibles to change, like a status text that I have set in my windows form. here is my GUI if it helps you understand my code a bit better:
https://gyazo.com/c6e9334e04aeb223e0afade6da8bec4e
Please let me know if you need anything else from me! I'm not sure if this is allowed but I'm willing to pay for someone to help me through this, not very much because I'm only 16 haha, but anyway, Thank you!
Well, I am not sure if this is what you want but if you want to run method with different variable value then create method:
public void MyMethod(string myVariable)
{
//do something with myVariable
}
And you can pass different variable value when calling method:
MyMethod("123abc");
or something like:
var newVariable="123abc"
MyMethod(newVariable);
To start method with different input create method with variable
public void MyUsername(string username)
{
MessageBox.Show(username);
}
private void buttonUsername1_Click(object sender, EventArgs e)
{
MyUsername("Adam");
}
private void buttonUsername2_Click(object sender, EventArgs e)
{
MyUsername("Jack");
}
To start method multiple time use loop
private void btnDoAllTask_Click(object sender, EventArgs e)
{
int count = 0;
// run 5 times
while(count < 5)
{
count++;
MyUsername("username" + count.ToString());
}
}
or loop through array
private void btnDoAllTask_Click(object sender, EventArgs e)
{
var listUsername = new string[] { "one", "two", "three" };
foreach (var username in listUsername)
{
MyUsername(username);
}
}
Related
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.
Simply put, I want to make a program that counts down (+ voice) but when numbers like 1mil comes this takes longer than 1 sec to pronounce so I would like to find out how I can get the "EventHandler" to run and how I can use it (i do not need code for counting etc. but how to create an EventHandler and where i need to write the Code when its been called)
https://learn.microsoft.com/en-us/dotnet/api/system.speech.synthesis.speechsynthesizer.speakcompleted?view=netframework-4.8
I cant just use Text.Speak("") cuz this leads to desync with the Text printed. I need that callback to start a new Speak and sync it with Text.
Sry... i hate to ask ppl but after 3h i surrender pls help me
SpeechSynthesizer synth = new SpeechSynthesizer();
int counting = 0;
private void TTS() //First trigger
{
synth.SetOutputToDefaultAudioDevice();
synth.SelectVoiceByHints(VoiceGender.Female, VoiceAge.NotSet, 0);
textBox1.Text = "1";
synth.Speak("1");
counting = 1;
synth.SpeakCompleted += synth_SpeechOver;
}
//public event EventHandler<System.Speech.Synthesis.SpeakCompletedEventArgs> SpeakCompleted; deleted
public void synth_SpeechOver(object sender, EventArgs e)
{
synth.SetOutputToDefaultAudioDevice();
synth.SelectVoiceByHints(VoiceGender.Female, VoiceAge.NotSet, 0);
counting++;
synth.Speak(counting.toString());
}
void Form1_SpeakCompleted(object sender, EventArgs e)
{
}
//(Form1_SpeakCompleted is just for testing (doesnt work)
Callback only works for SpeakAsync
https://learn.microsoft.com/en-us/dotnet/api/system.speech.synthesis.speechsynthesizer.speakcompleted?view=netframework-4.8
The SpeechSynthesizer raises the SpeakCompleted event at the completion of any of the SpeakAsync or SpeakSsmlAsync methods.
I have something like :
private void Form1_Load(object sender, EventArgs e)
{
Hide();
string ngdx = "*ngdx";
string atdx = "*atdx";
for (;;)
{
try
{
string[] convertngdx = Directory.GetFiles("D:\\folder", ngdx);
string[] convertatdx = Directory.GetFiles("D:\\folder", atdx);
foreach (var convertngd in convertngdx)
{
File.Move(convertngd, Path.ChangeExtension(convertngd, ".ngd"));
}
foreach (var convertatd in convertatdx)
{
File.Move(convertatd, Path.ChangeExtension(convertatd, ".atd"));
}
}
catch
{
}
}
}
I start my app and every time a .ngdx and .atdx file is send to the folder it automatically converts it to .ngd and .atd.
My problem is that it instantly converts them , and I want it to wait for a second before converting them.
I used System.Threading.Thread.Sleep(1000); but it doesn't quite seem to work,I think because when I run my app the System.Threading.Thread.Sleep(1000); is called and then after a second it is never called again.
The idea is every time a new .ngdx or .atdx is send to the folder I want it to wait for a second before converting them.
As an alternative to using an infinite for loop (which will tie up the UI thread) you could use a FileSystemWatcher. (example)
In my program there are multiple players who's stats can all be increased at the same time.
Player 1 is represented by staminaTextBox[0] skillTextBox[0] LuckTexBox[0]
Player 2 is represented by staminaTextBox[1] skillTextBox[1] LuckTexBox[1]
etc.
I need my IncreaseStat method to deal with 3 different type of Textbox Overload e.g. Stamina, Skill, Luck
private void StaminaIncBtn_Click(object sender, EventArgs e)
{
IncreaseStat(staminaText[0]);
}
private void LuckIncBtn_Click(object sender, EventArgs e)
{
IncreaseStat(luckText[0]);
}
private void IncreaseStat(TextBox statText)
{
for (int i = 0; i < 5 ; i++)
{
statText[i].Text = "Altered";
}
}
This method is used to increase all 5 players stats at the same time.
It works fine if they are not control arrays, however I need them to be. I get the error
"Cannot apply indexing with [] to an expression of type 'System.Windows.Forms.TextBox". It applies to that fact that inside the method I am saying statText[i].
I do not understand how to get around this problem. Any suggestions would be more than welcome.
Thank you for your time.
Would this be sufficient (with possible changes by yourself for your need)?
private void IncreaseStat() {
foreach (TextBox textBox in this.Controls.OfType<TextBox>().Where(x => x.Name.Contains("stamina") ||
x.Name.Contains("skill") ||
x.Name.Contains("Luck"))) {
textBox.Text = "Altered";
}
}
I made a program that loads a bunch of computer information. In the Form_Load event I have it initialize 3 (that number will grow) panels of information. One that has a bunch of unit information seems to make the program load rather slowly. I've tried to speed it up a bunch by switching from WMI to using Native calls, which helped a bunch. Soon though I'm going to have network information posted as well. I used to load that panel but i disabled it for a little bit till I work out the bugs in my other panels. So while learning how I can use a seperate thread to update my battery information I figured that I might be able to create seperate threads in my unit information panel so that it might could load faster. I dont know that any of my information would cause concurrent issues, but i can work on that.
I want to start small so what if i change this
private void Form1_Load(object sender, EventArgs e)
{
unitInformationPanel1.PopulateUnitInformation();
batteryInformationPanel1.InitializeBatteries();
magStripeReaderPanel1.SetupPointOfSale();
}
to this
private void Form1_Load(object sender, EventArgs e)
{
Thread infoThread = new Thread(new ThreadStart(unitInformationPanel1.PopulateUnitInformation));
infoThread.Start();
batteryInformationPanel1.InitializeBatteries();
magStripeReaderPanel1.SetupPointOfSale();
}
would the info thread be terminated when populate unit info is done? or would it be better to move that thread creation into PopulateUnitInformation? here is what it looks like.
public void PopulateUnitInformation()
{
unitModelLabel.Text = Properties.Settings.Default.UnitModelString;
serialNumberLabel.Text = Properties.Settings.Default.UnitSerialString;
biosVersionLabel.Text = UnitBios.GetBiosNumber();
osLabel.Text = OS.getOSString();
cpuLabel.Text = UnitCpu.GetCpuInfo();
var hdd = HddInfo.GetHddInfo();
diskNameLabel.Text = hdd.Name;
diskCapacityLabel.Text = hdd.Capacity;
diskFirmwareLabel.Text = hdd.Firmware;
memoryLabel.Text = MemoryInformation.GetTotalMemory();
NetworkPresenceInformation.GetAdapatersPresent();
biometricLabel.Text = BiometricInformation.IsPresent ? "Present" : "Not Present";
var networkAdaptersPresense = NetworkPresenceInformation.GetAdapatersPresent();
bluetoothLabel.Text = networkAdaptersPresense[0] ? "Present" : "Not Present";
wifiLabel.Text = networkAdaptersPresense[1] ? "Present" : "Not Present";
cellularLabel.Text = networkAdaptersPresense[2] ? "Present" : "Not Present";
}
--
wow i just ran it with the infothread and it still took some time to load (might be the 12 panels i created in the main thread. but it loaded the 12 frames and the unit information panel populated its information after everything loaded. That was cool, but is it safe? is it somewhat easy to make 12 threads for my panels? or is that dumb?
EDIT
this is what i did for stopwatch.
Stopwatch programTimer;
public Form1()
{
programTimer = Stopwatch.StartNew();
InitializeComponent();
SetupDebugWindow();
TerminateKeymon();
UnitModel.SetModel();
UnitSerialNumber.SetSerialNumber();
}
private void Form1_Shown(object sender, EventArgs e)
{
audioBrightnessPanel1.UpdateBrightnessTrackbar();
applicationLauncherPanel1.LoadApplications();
programTimer.Stop();
Console.WriteLine("Load Time: {0}",programTimer.ElapsedMilliseconds);
timer1.Start();
}
Will this be accurate?
EDIT 2 6/18/2012
Well I took the advice of using backgroundworker. Please let me know if i did this right.
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
void BackgroundWorker1DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
unitInformationPanel1.PopulateUnitInformation();
batteryInformationPanel1.InitializeBatteries();
magStripeReaderPanel1.SetupPointOfSale();
}
You've asked a very broad question, but I'm going to give some general advice. If you want more specific information, you should consider deleting this question and posting more specific individual questions.
First and foremost, you should very strongly consider using something like the System.Threading.Task class for your multithreaded operations. There is a ton of information online about how to get started with it and how you can use Tasks to manage asynchronous operations. The short story is that if you're spinning up your own thread (as you're doing above), you almost certainly should be using something else to do that for you.
Adding multithreading to your code will not, in the strictest sense of the word, make it any "faster"; they will always take the same amount of total processor time. What it can and will do is two things: free up the UI thread to be responsive and allow you to split that "total processor time" across multiple cores or processors, should those be available to the system. So, if you have operation X that takes 10 seconds to complete, then just shifting operation X to another thread will not make it complete any faster than 10 seconds.
No, what you are doing above is not safe. I'm assuming that somewhere you've turned off checking for cross-thread communication errors in your app? Otherwise, that code should throw an exception, assuming this is a WinForms or WPF application. This is one reason to use Tasks, as you can easily separate the part of your process that actually takes a long time (or isn't UI related), then add a task continuation that uses the results and populates the UI elements within a properly synchronized context.
So my final approach this was as follows. I felt that my Main Form was doing more than it should. Sticking with the single responsibility principle I decided that MainForm should only be responsible for one thing, showing and displaying all 12 panels (now down to 11, i turned one into a menu item). So moved all the multithreading out of mainform and into program.cs. I found that this was even a little more difficult. What I did find though was a simple solution that allows me to not even worry about multithreading at all. It was the Idle event. Here is what i chose to do.
[STAThread]
static void Main()
{
DateTime current = DateTime.Now;
DateTime today = new DateTime(2012,7,19);
TimeSpan span = current.Subtract(today);
if (span.Days<0)
{
MessageBox.Show("Please adjust Time then restart Aspects","Adjust Time");
Process.Start("timedate.cpl").WaitForExit();
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Idle += new EventHandler(Application_Idle);
mainForm = new MainForm();
mainForm.Closing += new CancelEventHandler(mainForm_Closing);
#if !DEBUG
TerminateKeymon();
StartSerial();
SetupDefaultValues();
EmbeddedMessageBox(0);
#endif
Application.Run(mainForm);
}
}
static void Application_Idle(object sender, EventArgs e)
{
Application.Idle -= Application_Idle;
mainForm.toolStripProgressBar1.Increment(1);
UnitInformation.SetupUnitInformation();
mainForm.toolStripProgressBar1.Increment(1);
Aspects.Unit.HddInfo.GetHddInfo();
mainForm.toolStripProgressBar1.Increment(1);
for (int i = 0; i < mainForm.Controls.Count; i++)
{
if (mainForm.Controls[i] is AbstractSuperPanel)
{
try
{
var startMe = mainForm.Controls[i] as AbstractSuperPanel;
startMe.StartWorking();
mainForm.toolStripProgressBar1.Increment(1);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + mainForm.Controls[i].ToString());
}
}
}
mainForm.toolStripProgressBar1.Value = 0;
}
to sum up what that does is is I add a idle listener event. Once the thead goes idle (basically meaning that Mainform is finished drawing and making all 12 panels and is showing on my desktop) I then kill the idle event listener and tell all my panels and classes to start working one at a time, updating my progress bar as I go. It works great. The load time is still the same as it was before, but there is window visibile after only a few seconds. Maybe not the best use of resources, but i think the solution is simple and straight forward.
I had a question somewhat related to this for Mobile app development a few months back (see How to write a Trigger?), and Marc "the man" Gravell posted back with a simple class that I modified to return data to my main application whenever the thread was complete.
The actual class I put into use has loads of pointless data (for you), so I'm going to paste in a revised version of Mr. Gravell's code using techniques which I used to make them work:
First, I had to create my own EventArgs class:
public class SuperEventArgs : EventArgs {
private object data;
public SuperEventArgs(object data) : base() {
this.data = data;
}
public object Data { get { return data; } }
}
Using that, here is a class I created to pass my data back to the main thread:
public delegate event DataChangedHandler(object sender, SuperEventArgs e);
public class Simple1 {
private object parameter1, parameter2;
private Control parent;
#if PocketPC
public delegate void MethodInvoker(); // include this if it is not defined
#endif
public Simple1(Control frmControl, object param1, object param2) {
parent = frmControl;
parameter1 = param1;
parameter2 = param2;
}
public event DataChangedHandler DataChanged;
public void Start() {
object myData = new object(); // whatever this is. DataTable?
try {
// long routine code goes here
} finally {
if (DataChanged != null) {
SuperEventArgs e = new SuperEventArgs(myData);
MethodInvoker methInvoker = delegate {
DataChanged(this, e);
};
try {
parent.BeginInvoke(methInvoker);
} catch (Exception err) {
Log(err); // something you'd write
}
}
}
}
}
Back in the actual main thread of execution, you'd do something like this:
public partial class Form1 : Form {
private Simple1 simple;
public Form1() {
object query = new object(); // something you want to pass in
simple = new Simple1(this, query, DateTime.Now);
simple.DataChanged += new DataChangedHandler(simple1_DataChanged);
Thread thread = new Thread(simpleStart);
thread.Start();
}
private void simpleStart() {
if (simple != null) {
simple.Start();
}
}
private void simple1_DataChanged(object sender, SuperEventArgs e) {
MyFancyData fancy = e.Data as MyFancyData;
if (fancy != null) {
// populate your form with the data you received.
}
}
}
I know it looks long, but it works really well!
This is not anything I have actually tested, of course, because there isn't any data. If you get to working with it and you experience any issues, let me know and I'll happily help you work through them.
~JoeP