I want to create a method which makes my application wait X number of seconds, then continues on down a line of scripts. For example, this is the code that I have so far, after reading many similar help topics:
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 WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
methods.WriteTextToScreen(label1, "Hello!");
methods.sleepFor(1);
methods.WriteTextToScreen(label1, "Welcome!");
methods.sleepFor(1);
methods.WriteTextToScreen(label1, "Allo!");
}
public class methods
{
public static int timeSlept;
public static void WriteTextToScreen(Label LabelName, string text)
{
LabelName.Text = text;
}
public static void sleepFor(int seconds)
{
timeSlept = 0;
System.Timers.Timer newTimer = new System.Timers.Timer();
newTimer.Interval = 1000;
newTimer.AutoReset = true;
newTimer.Elapsed += new System.Timers.ElapsedEventHandler(newTimer_Elapsed);
newTimer.Start();
while (timeSlept < seconds)
{
Application.DoEvents();
}
Application.DoEvents();
}
public static void newTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
timeSlept = IncreaseTimerValues(ref timeSlept);
Application.DoEvents();
}
public static int IncreaseTimerValues(ref int x)
{
int returnThis = x + 1;
return returnThis;
}
}
}
}
What I want to do is have my program do the methods.WriteTextToScreen(label1, "Hello!")
then wait for 1 second, then continue on in the same fashion. The problem is that the Form I'm displaying the text on doesn't show up at all until it has written "Allo!" onto the screen, so the first time it appears it already says that. Am I doing something wrong, or is there just no way to do this?
The form doesn't show until it has been constructed i.e. all the code in Form1 is run. See here for info on form constructors: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.form.aspx
To fix your problem you could move the writeTextToScreen and sleep code into the forms on load method. See http://msdn.microsoft.com/en-us/library/system.windows.forms.form.onload.aspx
Related
Just started learning Visual Studio and I'm trying to make a soundboard in Windows Forms.
ButtonMaker(); is the function I use to make a button for each soundfile in my directory so I don't have to make 70 different buttons for every sound, but when I run the program nothing shows up in the forms window. Anybody know why? I've tried calling the function in Main() and in the initial Form1 class but nothing happens in either. Forms class file here:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace MySoundBoard
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
///Tried running it here
}
private void Form1_Load(object sender, EventArgs e)
{
///Tried running it here
}
private void ButtonMaker()
{
string[] files = Soundfiles.GetFile();
foreach (var item in files)
{
string btnName = item.ToUpper();
Button btNname = new Button();
btNname.Text = item;
int x = 40;
int y = 40;
btNname.Location = new Point(x, y);
x = x + 50;
if (x>900)
{
x = 40;
y = y + 30;
}
}
}
private void button1_Click(object sender, EventArgs e)
{
}
}
}
Here is the SoundFiles class:
using System.IO;
using System;
using System.Text;
using System.Diagnostics;
using WMPLib;
namespace MySoundBoard
{
class Soundfiles {
WMPLib.WindowsMediaPlayer Player;
static public string[] GetFile() {
string txtPath = #"C:\Documents\path\to\sound effects";
string[] files =
Directory.GetFiles(txtPath, "*ProfileHandler.cs", SearchOption.TopDirectoryOnly);
return files;
}
public void PlayFile(String url)
{
Player = new WMPLib.WindowsMediaPlayer();
Player.URL = url;
Player.controls.play();
}
}
}
And the main project file(not that worked with yet):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MySoundBoard
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
///Tried running it here
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
As said, I'm very new to this language and any help would be appriciated!
You create the buttons but never add them to the form. Just add
this.Controls.Add(btNname);
The next thing is, that your buttons will not do something. You'll need to add an event handler as well.
btNname.Click += ...;
In order to know which button plays which sound, you need to find a way to have that association. A hacky approach is
btNname.Tag = item;
and then evaluate Tag later
I have a problem changing text from another class in another namespace. I have the first Form1 class :
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;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
static Form1 mainForm;
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AllocConsole();
public static String LinkToApi = "http://google.com/api/";
public static Comunicator comunicator;
public static int debug = 5;
public Form1()
{
InitializeComponent();
AllocConsole(); // allow console
if(Form1.debug >= 3) Console.WriteLine("Application started");
comunicator = new Comunicator();
mainForm = this;
}
private void TestButton_Click(object sender, EventArgs e)
{
TestButton.Text = "Loading";
comunicator.TestConnection();
}
}
}
and this Comunicator class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Collections.Specialized;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
using System.Threading;
namespace WindowsFormsApplication1
{
public class Comunicator
{
private String action = "idle";
public static Thread Start(Action action)
{
Thread thread = new Thread(() => { action(); });
thread.Start();
return thread;
}
public Comunicator()
{
}
public void TestConnection()
{
if (Form1.debug >= 3) Console.WriteLine("Testing connection");
// thread test
Start(new Action(ApiTest));
}
public void ApiTest()
{
if (Form1.debug >= 3) Console.WriteLine("API test begin");
// Create a request for the URL.
WebRequest request = WebRequest.Create("http://www.bogotobogo.com/index.php");
// If required by the server, set the credentials.
request.Credentials = CredentialCache.DefaultCredentials;
// Get the response.
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
// Display the status.
Console.WriteLine(response.StatusDescription);
// Get the stream containing content returned by the server.
Stream dataStream = response.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
// Display the content.
Console.WriteLine(responseFromServer);
// Cleanup the streams and the response.
reader.Close();
dataStream.Close();
response.Close();
// Console.Read();
if (Form1.debug >= 3) Console.WriteLine("API test end");
// Form1.StaticTestButton.Text = "Loaded"; <---- CHANGE HERE
}
}
}
which is not even a form class (I want to keep everything nice and clean). I want to change the TestButton text into "LOADED" but i get an error when I try to do that as if Form1.TestButton does not exist in Comunicator class.
I have tried to instantiate the class, I made a couple of variables static ... nothing, still getting error.
What is the problem? How may I solve this?
The request must be asynchronous, that's why I am using threads.
You should separate concerns, and you shouldn't communicate with UI in class which is not related to UI.
You should rewrite your code.
But as quick fix you should do the following.
In class Comunicator, you can do such field.
private readonly Action<string> _notifySimpleMessageAction;
Then add to Communicator constructor parameter notifyFunction. Code in constructor:
_notifySimpleMessageAction = notifyFunction
After that you should create Communicator in following manner:
communicator = new Communicator((notification)=>
{
StaticTestButton.BeginInvoke((MethodInvoker)(() => StaticTestButton.AppendText(notification)));
});
Then at the end of your method you should do
_notifySimpleMessageAction("Loaded")
Controller class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ControllerDemonstrator
{
public class Controller
{
public event EventHandler CommunicatorDataLoaded;
public event EventHandler FormTestConnection;
private Form1 _form;
private Communicator _communicator;
public Form1 MainForm
{
get { return _form; }
}
public Controller()
{
_form = new Form1(this);
_form.TestConnection += _form_TestConnection;
_form.FormClosed += _form_FormClosed;
_communicator = new Communicator(this);
_communicator.DataLoaded += _communicator_DataLoaded;
}
public void Start()
{
_form.Show();
}
void _form_FormClosed(object sender, System.Windows.Forms.FormClosedEventArgs e)
{
// put any code to clean up the communicator resources (if needed) here
// --------------------------------------------------------------------
_communicator = null;
// Then exit
// ---------
Application.Exit();
}
private void _communicator_DataLoaded(object sender, EventArgs e)
{
if (null != CommunicatorDataLoaded)
{
CommunicatorDataLoaded(sender, e);
}
}
private void _form_TestConnection(object sender, EventArgs e)
{
if (null != FormTestConnection)
{
FormTestConnection(sender, e);
}
}
}
}
Basic form with one button (_testButton):
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 ControllerDemonstrator
{
public partial class Form1 : Form
{
public event EventHandler TestConnection;
public Form1(Controller controller)
{
InitializeComponent();
controller.CommunicatorDataLoaded += controller_CommunicatorDataLoaded;
}
void controller_CommunicatorDataLoaded(object sender, EventArgs e)
{
_testButton.Text = "Loaded";
}
private void _testButton_Click(object sender, EventArgs e)
{
if (null != TestConnection)
{
TestConnection(this, new EventArgs());
}
}
}
}
Communicator class (everything has been stripped out, you will need to add in your logic):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ControllerDemonstrator
{
public class Communicator
{
public event EventHandler DataLoaded;
public Communicator(Controller controller)
{
controller.FormTestConnection += controller_FormTestConnection;
}
private void controller_FormTestConnection(object sender, EventArgs e)
{
// put your code that does the connection here
// -------------------------------------------
if (null != DataLoaded)
{
DataLoaded(this, new EventArgs());
}
}
}
}
And in your Program.cs (assuming that is how you are starting your application):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ControllerDemonstrator
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Controller c = new Controller();
Application.Run(c.MainForm);
}
}
}
With this kind of design, the communicator doesn't know about the form and vice verse. You can expand it out to have different kind's of communicators/forms/etc and have the controller keep track of everything. It is also much easier to test code like this as you can test each separate piece on it's own since they don't depend on each other. This is a quick and dirty implementation. Do some research on the Model View Controller design pattern (not Microsoft MVC for asp.Net, but the actual design pattern). It is more code up-front to code an application with the MVC design pattern but it makes it easier to test and more maintainable.
How to make the cycle, running up for 30 seconds? After 30 seconds, the cycle must be repeated with changing one variable(i++).
P.S. I've never used a timer and I need a detailed solution.
the microsoft example seems pretty good. https://msdn.microsoft.com/en-us/library/system.threading.timer%28v=vs.110%29.aspx
here is a cut down version, ive set it for 3 sec rather than 30 so you can verify the result
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
public static void Main(string[] args)
{
example e = new example();
e.timer();
Console.Read();
}
public class example
{
public volatile int i;
public void timer()
{
TimerCallback tcb = callback;
Timer t = new Timer(tcb, i, 3000, 3000);
}
public void callback(object state)
{
Console.WriteLine(this.i);
this.i++;
}
}
}
}
Delay.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace LearnThread
{
class Delay
{
public int Convert()
{
int ErrorCode = 1;
//something
//takes long time. about 9 hours.
return ErrorCode;
}
}
}
Form1.cs
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;
using System.Threading;
namespace LearnThread
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
Delay delay = new Delay();
Thread t = new Thread(delay.Convert);
//something
MessageBox.Show("Success");
}
}
}
Delay delay = new Delay(); is error here as it is expecting return value. I want the return value as it is contains errorcode. How can I do that? Background worker is better than Thread? Please help. (I should not lose control on the form when delay.Convert() is running.)
As mentioned by Juergen, you can make ErrorCode a class member and then access it once the thread has completed execution. This would require you to create a new instance of the Delay class if you are trying to run multiple Convert in parallel.
You can also use a delegate to get the return value to a variable in the btnStart_Click function as follows:
private void button1_Click(object sender, EventArgs e)
{
Delay delay = new Delay();
int delayResult = 0;
Thread t = new Thread(delegate() { delayResult = delay.Convert(); });
t.Start();
while (t.IsAlive)
{
System.Threading.Thread.Sleep(500);
}
MessageBox.Show(delayResult.ToString());
}
If you plan to run Convert in parallel here, you would have to create as many local variable as required or handle it someother way.
Make the ErrorCode a class member. This way you can get it afterwards.
class Delay
{
public int ErrorCode { get; private set; }
public void Convert()
{
ErrorCode = 1;
...
}
}
private void btnStart_Click(object sender, EventArgs e)
{
Delay delay = new Delay();
Thread t = new Thread(delay.Convert);
//something
int error = delay.ErrorCode;
MessageBox.Show("Success");
}
This program is writing numbers from 1 to 5000 in thread, but main form freezes anyway.
Where is an error? Thanks in advance.
Code:
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;
using System.Net;
using System.IO;
using System.Threading;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
int how, current;
bool job;
Object lockobj = new Object();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Started!");
how = 5000;
current = 0;
job = true;
Thread worker = new Thread(Go);
worker.Name = "1";
worker.Start();
}
private void Go()
{
while (job)
{
if (current < how)
{
lock (lockobj)
{
current++;
}
log(string.Format("Thread #{0}: {1}", Thread.CurrentThread.Name, current));
}
else
{
job = false;
}
}
}
private void log(string text)
{
Action A = new Action(() =>
{
richTextBox1.AppendText(text + System.Environment.NewLine);
});
if (richTextBox1.InvokeRequired)
this.BeginInvoke(A);
else A();
}
}
}
Because most of your work will be spent in
if (richTextBox1.InvokeRequired)
this.BeginInvoke(A);
and while you invoke the form it is locked.
Do some real work, like Thread.Sleep(1000); :-) , instead of current++; and your form will be response between the updates.
It freezes because you are rendering on the textbox very quickly and the GUI doesn't have time to keep in sync. Remember that this rendering happens on the main GUI thread and by calling BeginInvoke to update the textbox so rapidly actually consumes all the resources of this main GUI thread. Try lowering the frequency at which you are logging to avoid this behavior.