I'm making an application for a friend's birthday, where a window with changing compliments is supposed to pop up.
The window freezes, however, and the labels don't change. I Googled it, and read something about using Backgroundworker in order to seperate the GUI-thread from the changing process.
Still doesn't work.
This is my code:
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.Threading;
namespace ProjectL
{
public partial class Form1 : Form
{
private MessageHandler theHandler = new MessageHandler();
private BackgroundWorker theBackgroundWorker = new BackgroundWorker();
public Form1()
{
InitializeComponent();
}
private void StartButton_Click(object sender, EventArgs e)
{
StartButton.Visible = false;
theBackgroundWorker.DoWork += new DoWorkEventHandler(theBackgroundWorker_doYourWork);
//theBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(theBackgroundWorker_doYourWork);
theBackgroundWorker.RunWorkerAsync();
theHandler.RunMessage(hBDLabel, youAreLabel, mainLabel, this);
}
void theBackgroundWorker_doYourWork(object sender, DoWorkEventArgs e)
{
theHandler.RunMessage(hBDLabel, youAreLabel, mainLabel, this);
}
}
}
This is what's supposed to happen from the background, using a class I've named MessageHandler:
class MessageHandler
{
public List<String> GenerateComplimentTexts()
{
List<String> stringList = new List<String>();
//Adding a bunch of compliments into a List<String>
return stringList;
}
public void RunMessage(Label hBDLabel, Label youAreLabel, Label mainLabel, Form1 form)
{
List<String> stringList = GenerateComplimentTexts();
Thread.Sleep(2000);
form.Text = "Happy Birthday Goose!!!";
hBDLabel.Text = "Happy Birthday Goose!";
Thread.Sleep(3000);
youAreLabel.Text = "You are...";
Thread.Sleep(2000);
foreach (String e in stringList)
{
mainLabel.Text = e;
//form.Test = e
Thread.Sleep(1000);
}
Thread.Sleep(3000);
mainLabel.Text = "";
youAreLabel.Text = FinalMessage;
}
private String _finalMessage = "FINAL MESSAGE";
public String FinalMessage {get {return _finalMessage;}}
}
Still, nothing changes on my window. Everything is pretty much frozen, except for the text in the top-bar of the form itself, if I choose to uncomment
form.Text = e;
Any advice?
You could achieve it using Timers instead of a BackgroundWorker.
It would probably look like this :
MainForm :
public partial class Form1 : Form
{
private Timer timer;
private MessageHandler theHandler = new MessageHandler();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
timer = new Timer();
timer.Interval = 1000;
timer.Tick += TimerOnTick;
// Initialize the other labels with static text here
}
private void TimerOnTick(object sender, EventArgs eventArgs)
{
theHandler.ShowNext(label1);
}
private void StartButton_Click(object sender, EventArgs e)
{
timer.Start();
}
}
And the MessageHandler class :
public class MessageHandler
{
private List<String> compliments = new List<string>();
private int index = 0;
public MessageHandler()
{
GenerateComplimentTexts();
}
private void GenerateComplimentTexts()
{
List<String> stringList = new List<String>();
//Adding a bunch of compliments into a List<String>
compliments = stringList;
}
public void ShowNext(Label label)
{
label.Text = compliments.ElementAt(index);
index = (index >= compliments.Count - 1) ? 0 : index + 1;
}
}
To update the UI, use the ReportProgress method of the background worker. ReportProgress raises the ProgressChanged event and you can update your UI from there. Be sure to set the WorkerReportsProgress property of the backgroundworker to true.
Try this code:
private void StartButton_Click(object sender, EventArgs e)
{
StartButton.Visible = false;
theBackgroundWorker.DoWork += new DoWorkEventHandler(theBackgroundWorker_doYourWork);
theBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(theBackgroundWorker_ProgressChanged);
theBackgroundWorker.RunWorkerAsync();
}
void theBackgroundWorker_doYourWork(object sender, DoWorkEventArgs e)
{
var w = sender as BackgroundWorker;
List<String> stringList = GenerateComplimentTexts();
Thread.Sleep(2000);
w.ReportProgress(0, "Happy Birthday Goose!!!");
w.ReportProgress(1, "Happy Birthday Goose!");
Thread.Sleep(3000);
w.ReportProgress(2, "You are...");
Thread.Sleep(2000);
foreach (String e in stringList)
{
w.ReportProgress(3, e);
Thread.Sleep(1000);
}
Thread.Sleep(3000);
w.ReportProgress(3, "");
w.ReportProgress(2, FinalMessage);
}
void theBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var msg = e.UserState.ToString();
switch (e.ProgressPercentage)
{
case 0:
form.Text = msg;
break;
case 1:
hBDLabel.Text=msg;
break;
case 2:
youAreLabel.Text =msg;
break;
case 3:
mainLabel.Text=msg;
break;
}
}
Have a try with this:
1.) Copy Timer to Form
2.) Copy the following Code into the timer1_tick-Event.
private int _counter;
private int _currentIndex;
private void timer1_Tick(object sender, EventArgs e)
{
switch (_counter)
{
case 1:
form.Text = "Happy Birthday Goose!!!";
hBDLabel.Text = "Happy Birthday Goose!";
break;
case 2:
timer1.Interval = 3000;
break;
case 3:
youAreLabel.Text = "You are...";
break;
case 4:
if (stringlist.count = (_currentIndex -1))
{
timer1.Enabled = false;
}else {
mainLabel.Text = e;
timer1.Interval = 1000;
return;
}
break;
}
_counter ++;
}
Not the most elegant way, but it should work.
Related
I am new in the programming world and trying to do this little app for the first time working with WPF but I can't "print" my results from an array in the label.
public partial class MainWindow : Window
{
Stopwatch clock = new Stopwatch();
DispatcherTimer tick = new DispatcherTimer();
public int I = 0;
public string[] temporaryResult = new string[5];
public bool start = false;
public MainWindow()
{
InitializeComponent();
updates();
}
private void updates()
{
tick.Start();
tick.Tick += Tick;
}
private void Tick(object? sender, EventArgs e)
{
Display.Content = Math.Round(clock.Elapsed.TotalMilliseconds, 0);
test.Content = temporaryResult[I];
}
private void Start_Click(object sender, RoutedEventArgs e)
{
if (!start)
{
clock.Reset();
clock.Start();
start = true;
}
else
{
clock.Stop();
start = false;
temporaryResult[I] = clock.Elapsed.TotalMilliseconds.ToString();
I++;
Debug.Print(temporaryResult[1]);
}
}
}
In method tick, I try to show my result with
test.Content = temporaryResult[I];
It doesn't work. But when I use
test.Content = temporaryResult[0];
It works at least once.
Thanks for helping Xerillio!
You are incrementing I (I++) after you insert a value at temporaryResult[I] so I points to an empty index.
hi, i have send form context to another class to run function there, but the fun show out the messagebox but not update form text , why ?
i need the function start to update form title
i sending form context to timer class as parameter
the function start work in interval but the method cant update the
form control
this is form class
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication8
{
public partial class Form1 : Form
{
public Form1()
{
//form constractor
InitializeComponent();
}
timer mytimer;
private void Form1_Load(object sender, EventArgs e)
{
}
private void Elipse()
{ //the code for timer :) //yazeed coding
//MessageBox.Show(" ");
this.Text=" jkghjg";
}
private void button1_Click(object sender, EventArgs e)
{
mytimer = new timer();
mytimer.elipse += Elipse;
mytimer.Interval = 1000;
mytimer.start(this);
}
}
}
this is timer class
class timer
{
bool isstart;
public delegate void Del();
public Del elipse;
public static void UserRep() { }
public bool Enable { get; set; }
public int Interval { get; set; }
public timer()
{
Enable = false;
Interval = 1000;//default is 1 sec
isstart = false;
elipse = new Del(UserRep);
}
public void start(Form Context)
{
Context.BeginInvoke((MethodInvoker)async delegate
{
Enable = true;
isstart = true;
do
{
await Task.Delay(Interval);
new Thread(() =>
{
elipse(); //the replacment running now (interface thrad protected)
}).Start();
}
while (isstart);
Enable = false;
});
}
public void stop()
{
isstart = false;
}
}
AND thanks
As you are starting a new thread, you need to call elipse in the Form context:
Replace:
new Thread(() =>
{
elipse();
}).Start();
with
new Thread(() =>
{
Context.BeginInvoke(elipse);
}).Start();
Thanks Robert, good improvement , the code running successfully i add the post to see what others say, for code correct :) i keep this method like this for other developer to understand that they can add more code here
i find lot of post that they use long code to perform small task,
here is light code for many methods used in many project and helpful
this code is same as Microsoft timer implemented by me
here it is
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication8
{
public partial class Form1 : Form
{
public Form1()
{
//form constractor
InitializeComponent();
}
timer mytimer;
private void Form1_Load(object sender, EventArgs e)
{
//perform event click for button automaticly
button1.PerformClick();
}
private void Elipse()
{
//the code for timer :) //yazeed coding ,,,, write any other code
new Thread(() => MessageBox.Show("Successful")).Start();
Text = DateTime.Now.TimeOfDay.Seconds.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
mytimer = new timer();
mytimer.elipse += Elipse;
mytimer.Interval = 1000;
mytimer.Enable = true;
mytimer.start(this);
}
}
}
class timer
{
bool isstart;
internal delegate void Del();
internal Del elipse;
static void UserRep() { }
internal bool Enable { get; set; }
internal int Interval { get; set; }
internal timer(int val=100)
{
Enable = false;
Interval = val>0?val:100;//default is 1 sec
isstart = false;
elipse = new Del(UserRep);
}
internal void start(Form Context)
{
new Thread(async () =>
{
isstart = true;
do
{
await Task.Delay(Interval);
new Thread(() => Context.BeginInvoke((MethodInvoker)delegate
{
elipse(); //the replacment running now (interface thrad protected)
})).Start();
}
while (isstart);
}).Start();
}
internal void stop()
{
isstart = false;
}
}
I want to create a C# form in which two text box show two different numbers.
After clicking on start button both numbers should start incrementing at same time should increment slowly so we can see them increment and clicking on stop button should stop increment.
Both text box are not related to each other any way.
public partial class Form1 : Form
{
Thread t1 = new Thread(new ThreadStart(increment1));
public static int fNumber = 0, sNumber = 0,flag = 0;
public Form1()
{
InitializeComponent();
}
private void Start_Click(object sender, EventArgs e)
{
t1.Start();
}
private void button4_Click(object sender, EventArgs e)
{
}
private void number1_TextChanged(object sender, EventArgs e)
{
}
private void Form1_Load(object sender, EventArgs e)
{
}
public static void increment1()
{
Form1 frm = new Form1();
for (int i = fNumber;i<1000;i++)
{
frm.number1.Text = Convert.ToString(i);
}
}
}
public void Increment1()
{
for (int i = fNumber;i<1000;i++)
{
number1.Text = Convert.ToString(i);
number2.Text = Convert.ToString(i);
}
}
And just generally avoid static for threads. You can access your textboxes directly without the word this if you named your textboses number1 and number2. In a static method the variables need to be static aswell or you will get a compilation error.
For visibility, functions should have first letter in upper case. So you can distinguish them more easily from variables.
You are creating a new instance of Form1 which is wrong. Use the current Form1 that started the Thread.
Try
public partial class Form1 : Form
{
private Timer timer1;
public static int fNumber = 0, sNumber = 0,flag = 0;
public Form1()
{
timer1 = new Timer();
timer1.Interval = 1000;
timer1.Tick += timer1_Tick;
InitializeComponent();
}
private void Start_Click(object sender, EventArgs e)
{
timer1.Start();
}
private void button4_Click(object sender, EventArgs e)
{
timer1.Stop();
}
private void number1_TextChanged(object sender, EventArgs e)
{
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void timer1_Tick(object sender, EventArgs e)
{
int i = 0;
int.TryParse(this.number1.Text, out i);
i++;
if(this.number1.InvokeRequired)
{
this.number1.BeginInvoke((MethodInvoker) delegate()
{
this.number1.Text = Convert.ToString(i);
});
}
else
{
this.number1.Text = Convert.ToString(i);
}
}
}
For safety measures, check if invoking is required.
Replace this.number1.Text = Convert.ToString(i); with this block of code.
if(this.number1.InvokeRequired)
{
this.number1.BeginInvoke((MethodInvoker) delegate()
{
this.number1.Text = Convert.ToString(i);
});
}
else
{
this.number1.Text = Convert.ToString(i);
}
I am using c# Windows Form Application and ftpWebRequest, I am doing a directory listing. I have a listbox that will display folders, by using the event DoubleClick in my listbox, the double clicked folder or item in my listbox will show its content. And now my problem is I don't know how to go back to the previous directory by using back button.
Here is my Code File:
namespace myFTPClass
{
public class myFTP
{
public string user;
public string pass;
public delegate void cThread1(string thread1);
public event EventHandler EH;
public List<string> myDIR = new List<string>();
public void getDirectoryList(string getDirectory)
{
try
{
FtpWebRequest fwr = FtpWebRequest.Create(getDirectory) as FtpWebRequest;
fwr.Credentials = new NetworkCredential(user, pass);
fwr.UseBinary = true;
fwr.UsePassive = true;
fwr.KeepAlive = true;
fwr.Method = WebRequestMethods.Ftp.ListDirectory;
StreamReader sr = new StreamReader(fwr.GetResponse().GetResponseStream());
while (!sr.EndOfStream)
{
myDIR.Add(sr.ReadLine());
}
}
catch(Exception we)
{
myDIR.Clear();
string msg = we.Message;
}
}
void myCallBackMethod(IAsyncResult ar)
{
cThread1 myThread = (cThread1)((System.Runtime.Remoting.Messaging.AsyncResult)ar).AsyncDelegate;
myThread.EndInvoke(ar);
if (EH != null) EH(this, null);
}
public void Async_getDirectoryList(string dir)
{
AsyncCallback ac = new AsyncCallback(myCallBackMethod);
cThread1 myThread = new cThread1(getDirectoryList);
myThread.BeginInvoke(dir, ac, null);
}
}
}
And Here is my Form1:
namespace my_ftp_v0._01
{
public partial class Form1 : Form
{
myFTP ftp = new myFTP();
public Form1()
{
InitializeComponent();
this.Load += new EventHandler(Form1_Load);
btn_connect.Click += new EventHandler(btn_connect_Click);
listBox1.DoubleClick += new EventHandler(listBox1_DoubleClick);
btn_back.Click += new EventHandler(btn_back_Click);
ftp.EH += new EventHandler(ftp_EH);
}
void btn_back_Click(object sender, EventArgs e)
{
}
void listBox1_DoubleClick(object sender, EventArgs e)
{
string forward = "ftp://127.0.0.1/" + listBox1.SelectedItem.ToString();
listBox1.Items.Clear();
ftp.myDIR.Clear();
ftp.Async_getDirectoryList(forward);
}
void Form1_Load(object sender, EventArgs e)
{
txt_dir.Text = "ftp://127.0.0.1/";
txt_pass.PasswordChar = '‡';
}
void ftp_EH(object sender, EventArgs e)
{
if (InvokeRequired)
{
EventHandler eh = new EventHandler(ftp_EH);
this.Invoke(eh, new object[] { sender, e });
return;
}
for (int i = 0; i < ftp.myDIR.Count; i++)
{
listBox1.Items.Add(ftp.myDIR[i]);
}
}
void btn_connect_Click(object sender, EventArgs e)
{
ftp.Async_getDirectoryList(txt_dir.Text);
ftp.user = txt_user.Text;
ftp.pass = txt_pass.Text;
}
}
}
Move your SetDirectoryList to its own method
Add a Stack object to your class to track your requests
When the user double clicks add the request to the stack and then set the directory.
When the user hits the back
button, check if the stack has a request, if it does, pop it off and
call the set directory method.
Something like this...
public partial class Form1 : Form
{
myFTP ftp = new myFTP();
Stack _requestStack = new Stack();//Stack to store requests
public Form1()
{
InitializeComponent();
this.Load += new EventHandler(Form1_Load);
btn_connect.Click += new EventHandler(btn_connect_Click);
listBox1.DoubleClick += new EventHandler(listBox1_DoubleClick);
btn_back.Click += new EventHandler(btn_back_Click);
ftp.EH += new EventHandler(ftp_EH);
}
void btn_back_Click(object sender, EventArgs e)
{
if(_requestStack.Count > 0)
{
string directoryPath = (string)_requestStack.Pop();
SetDirectoryList(directoryPath);
}
}
void listBox1_DoubleClick(object sender, EventArgs e)
{
string directoryPath = listBox1.SelectedItem.ToString();
_stack.Push(directoryPath);
SetDirectoryList(directoryPath);
}
void SetDirectoryList(string directoryPath)
{
string forward = "ftp://127.0.0.1/" + directoryPath;
listBox1.Items.Clear();
ftp.myDIR.Clear();
ftp.Async_getDirectoryList(forward);
}
void btn_back_Click(object sender, EventArgs e)
{
create.server = create.server.TrimEnd('/');
create.server = create.server.Remove(create.server.LastIndexOf('/')+1);
listBox1.Items.Clear();
ftp.myDIR.Clear();
ftp.Async_getDirectoryList("");
}
I've already done this code to my back button and it works properly.
My problem is that I want the form still display data when it increases but the form is blocked and I cannot do anything with it.
This is my code :
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
while (true)
for (int i = 1; i < 11; i++)
richTextBox1.Text += "here" + i + "/n";
}
}
}
How I can prevent form from blocking?
private void Form1_Load(object sender, EventArgs e)
{
BackgroundWorker BWorker = new BackgroundWorker();
BWorker.DoWork += BWorker_DoWork;
BWorker.RunWorkerAsync();
}
void BWorker_DoWork(object sender, DoWorkEventArgs e)
{
// while(true) is meaningless.
for (int i = 1; i < 11; i++)
{
Action UpdateUI = () => { richTextBox1.Text += "here" + i + "/n"; };
this.BeginInvoke(UpdateUI);
}
}
Split your working cycle into steps by utilizing timer
private int _workStep;
private void button1_Click(object sender, EventArgs e)
{
_workStep = 0;
timerWork.Start();
}
private void timerWork_Tick(...)
{
switch(workStep)
{
case 0:
... // do something
if(something)
_workStep = 1;
case laststep:
timerWork.Stop();
}
}
or put work into thread (by using Thread, BackgroundWorker or Task), but then you must use Invoke/BeginInvoke when updating something in the user interface (accessing controls).
You can do this to get a responsive ui
delegate void DisplayInvoker(string text);
private void DisplayinRichTextbox(string text)
{
if (this.InvokeRequired)
this.BeginInvoke(new DisplayInvoker(DisplayinRichTextbox), text);
return;
}
richTextBox1.Text += text;
}
private void button1_Click(object sender, EventArgs e)
{
// some synchronization would have to be done kill old
// pool threads when the button is hit again
//
ThreadPool.QueueUserWorkItem((x) =>
{
while (true)
for (int i = 1; i < 11; i++)
DisplayinRichTextbox("here" + i + "/n");
});
}