I am trying to update an array of Labels which are on a form from a backgroundworker. Here is my code:
for (int i = 0; i < 6; i++)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
arrLabel[i].Text = values[i].ToString();
});
}
else
{
arrLabel[i].Text = values[i].ToString();
}
}
This does not work, but if I try to change text property of each label instead of the array, it works. How can I fix this? Also is there a shorter/better way of updating form controls from backgroundworkers than what I am doing for every single control on my form?
Edit: here is how I defined the array:
private Label[] arrLabel = new Label[6];
and here is the function that I call to assign the array:
private void makeLabelArrays()
{
for (int i = 0; i < 6; i++)
{
arrLabel[i] = (Label)Groupbox1.Controls["label" + (i + 1).ToString()];
}
}
I'm assuming what some of your code looks like; I may be wrong.
You can use the ReportProgress() method to send two pieces of information back to the UI thread - the percentage of completeness (doesn't really apply in your case so I specified 0) and some piece of data (any object you want, just a number in this case).
Then you can get the data in the ProgressChanged event and execute code that touches the UI.
private List<Label> arrLabel = new List<Label>();
private List<string> values = new List<string>();
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
var bw = (BackgroundWorker)sender;
for (int i = 0; i < 6; i++)
bw.ReportProgress(0, i);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var currentIndex = Convert.ToInt32(e.UserState);
arrLabel[currentIndex].Text = values[0].ToString();
}
Make sure you enable reporting progress, as it's disabled by default.
backgroundWorker1.WorkerReportsProgress = true;
try the following
private delegate void delegateAssignText();
public void AssignText()
{
if (this.InvokeRequired)
{
Invoke(new delegateAssignText(AssignText));
return;
}
for (int i = 0; i < 6; i++)
{
arrLabel[i].Text = values[0].ToString();
}
}
Related
I am trying to sort an array of 5 random integers in a listbox to ascending order. While creating a new thread and calling it separately, the form becomes unresponsive when I select the 'sort' button.
public partial class Form1 : Form
{
const int iSize = 5;
int[] iArray = new int[iSize];
Random rnd = new Random();
public Form1()
{
InitializeComponent();
}
Thread th;
private void btnGenerate_Click(object sender, EventArgs e)
{
for (int i = 0; i < iArray.Length; i++)
{
iArray[i] = rnd.Next(0, 5);
lbxNumbers.Items.Add(iArray[i]);
}
}
public void threadMethod()
{
bubbleSort(iArray);
foreach(int item in iArray)
{
lbxNumbers.Items.Add(item);
}
}
private void btnSort_Click(object sender, EventArgs e)
{
th = new Thread(threadMethod);
lblStatusUpdate.Text = "Sorting...";
}
private void btnClear_Click(object sender, EventArgs e)
{
lbxNumbers.Items.Clear();
}
public static void bubbleSort(int [] iArray)
{
bool isSorted = false;
int lastUnsorted = iArray.Length - 1;
while(!isSorted)
{
for(int i = 0; i < iArray.Length-1; i++)
{
if(iArray[i] > iArray[i+1])
{
swap(iArray, i, i + 1);
isSorted = false;
}
}
}
}
public static void swap(int [] iArray, int i, int j)
{
int tmp = iArray[i];
iArray[i] = iArray[j];
iArray[j] = tmp;
}
}
I am uncertain where the thread actually kicks in. The listbox can generate the array immediately and clear it, but sort makes it freeze. I am also unsure whether I need to use the background worker tool on this form.
To expound on Dour High Arch's comment.
EDIT
There are a couple of issues with your code. UI updates can only be done on the UI thread. You can accomplish this with the SychronizationContext or async/await handles it for you. Also, your bubblesort was missing an exit condition for the while loop, so it would run forever. The following demonstrates how this would work using async/await.
private async void btnSort_Click(object sender, EventArgs e)
{
lblStatusUpdate.Text = #"Sorting...";
await Run();
}
public async Task Run()
{
//This line runs asynchronously and keeps the UI responsive.
await bubbleSort(iArray);
//The remaining code runs on the UI thread
foreach (int item in iArray)
{
lbxNumbers.Items.Add(item);
}
}
public async Task bubbleSort(int[] iArray)
{
await Task.Run(() =>
{
bool isSorted = false;
while (!isSorted)
{
isSorted = true; //You were missing this to break out of the loop.
for (int i = 0; i < iArray.Length - 1; i++)
{
if (iArray[i] > iArray[i + 1])
{
swap(iArray, i, i + 1);
isSorted = false;
}
}
}
});
}
You can not act on UI, or Main thread within of another.
This line :
lbxNumbers.Items.Add(item);
which is invoked from another thread can cause a trouble.
The proper way of handling this scenario in WindowsForms would be using of
Control.Invoke method.
private void _btnOK_Click(object sender, EventArgs e)
{
_label1.Hide();
_label2.Hide();
_label3.Hide();
for(int i = 1; i <= 100; i++)
{
Thread.Sleep(5);
_circularprogressbar.Value = i;
_circularprogressbar.Update();
}
}
private void LoadingScreen_Load(object sender, EventArgs e)
{
_circularprogressbar.Value = 0;
_circularprogressbar.Minimum = 0;
_circularprogressbar.Maximum = 100;
}
}
}
This is my code. What i want to do is, i want to have a text inside the progress bar that shows the percentage of the progress from 1 to 100 percent.
what can i add to my code?
thank you
Here is what i would do:
private void _btnOK_Click(object sender, EventArgs e)
{
_label1.Hide();
_label2.Hide();
_label3.Hide();
for(int i = 1; i <= 100; i++)
{
_circularprogressbar.Value = i;
_percent_lable_name.Text = string.Format("{0}%", _circularprogressbar.Value);
_circularprogressbar.Update();
}
}
private void LoadingScreen_Load(object sender, EventArgs e)
{
_circularprogressbar.Value = 0;
_circularprogressbar.Minimum = 0;
_circularprogressbar.Maximum = 100;
}
}
See if that helps you!
Thanks
Techcraft7 :)
That Thread.Sleep(5) is blocking your entire UI thread. If you want to have your UI responsive, while the progress takes place, you need to make a separate thread for it. Something like this:
private void _btnOK_Click(object sender, EventArgs e)
{
_label1.Hide();
_label2.Hide();
_label3.Hide();
Task.Factory.StartNew(() =>
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(5);
Invoke((Action)(() =>
{
_circularprogressbar.Value = i;
_circularprogressbar.Update();
}));
}
});
}
Note that you will need t use Invoke to BeginInvoke to access UI components from inside that thread.
I have a folder browser button and a combobox which shows selected path. I want it keep previously selected paths. I wrote this code and it works fine. But I wanna know if there is more efficient way to do this.
private void Form1_Load_1(object sender, EventArgs e)
string hurrem, hurrem2, hurrem3, hurrem4, hurrem5;
hurrem = Settings.Default["com1"].ToString();
hurrem2 = Settings.Default["com2"].ToString();
hurrem3 = Settings.Default["com3"].ToString();
hurrem4 = Settings.Default["com4"].ToString();
hurrem5 = Settings.Default["com5"].ToString();
comboBox2.Items.Add(hurrem);
comboBox2.Items.Add(hurrem2);
comboBox2.Items.Add(hurrem3);
comboBox2.Items.Add(hurrem4);
comboBox2.Items.Add(hurrem5);
comboBox2.SelectedIndex = 0;
and
private void button1_Click(object sender, EventArgs e)
{
//..code
Settings.Default["com5"] = Settings.Default["com4"];
Settings.Default["com4"] = Settings.Default["com3"];
Settings.Default["com3"] = Settings.Default["com2"];
Settings.Default["com2"] = Settings.Default["com1"];
Settings.Default["com1"] = path1.ToString();
Settings.Default.Save();
You can use the LimitedStack from here: Limit size of Queue<T> in .NET?. You could then add methods for serializing/deserializing the data using Settings.Default.
public void Save()
{
int i = 0;
foreach (var item in _stack)
{
Settings.Default["com" + i++] = item;
}
Settings.Default.Save();
}
public void Load()
{
for (int i = 0; i < Limit; i++)
{
_stack.Add((T)Settings.Default["com" + i++]);
}
}
Storing the items (assuming you have the limited stack saved in a variable called myStack):
myStack.Push(path1.ToString());
myStack.Save();
Adding the items to the combobox:
for (int i = 0; i < myStack.Limit; i++)
{
comboBox2.Items.Add(myStack.Pop());
}
I have:
private void button1_MouseEnter(object sender, EventArgs e)
{
for (int i = 0; i > 2; i++)
{
button1.Content = Convert.ToString(i);
System.Threading.Thread.Sleep(1000);
}
tekst.Text = "Mouse Enter";
}
When I enter on Button I see only Mouse Enter, but Content on Button don't change. Why? What I can do wrong?
Hi is your for loop correct? It should be i<2 instead of i>2
for (int i = 0; i < 2; i++)
{
Your for loop never execute because you have wrong condition, change it to following code:
for (int i = 0; i < 2; i++)
Also you should use BackgroundWorker (msdn) to update your GUI dynamicly.
private void button1_MouseEnter(object sender, MouseEventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate
{
for (int i = 0; i < 2; i++)
{
this.Dispatcher.Invoke((Action)(() => { btn.Content = Convert.ToString(i); }));
System.Threading.Thread.Sleep(1000);
}
};
worker.RunWorkerCompleted += delegate { tekst.Text = "Mouse Enter"; };
worker.RunWorkerAsync();
}
I have Silverlight application that retrieves data from the database through a WCF Service. What my application have to do is to display the referent data anytime a button gets MouseOvered. I did it in a way that that when a button gets MouseOvered, I called my service and retrieved the data, but it generetad a big delay. Now, I think that another way should be making a list of all objects from the table, and just searching the id in the list when the action is triggered. I started coding, but it resulted in fail (and such an ugly piece of code).
My Working Code
private void MouseOverHarbor(object sender, RoutedEventArgs e)
{
Ellipse thisPath = (Ellipse)sender;
DataRetrieverReference.DataRetrieverClient webService = new DataRetrieverReference.DataRetrieverClient();
webService.GetDataCompleted += new EventHandler<DataRetrieverReference.GetDataCompletedEventArgs>(webService_GetDataCompleted);
webService.GetDataAsync(Convert.ToInt32(thisPath.DataContext));
}
void webService_GetDataCompleted(object sender, WebPortos.DataRetrieverReference.GetDataCompletedEventArgs e)
{
NameField.Text = e.Result.Name;
CityField.Text = e.Result.City;
StateField.Text = e.Result.State;
CompanyField.Text = e.Result.Company;
}
What I tried to do
private List<vwPortos_SEP> harborList;
private int counter;
public Brasil()
{
InitializeComponent();
this.harborList = new List<vwPortos_SEP>();
DataRetrieverClient webService = new DataRetrieverClient();
webService.GetCounterCompleted += new EventHandler<GetCounterCompletedEventArgs>(webService_GetCounterCompleted);
webService.GetCounterAsync();
webService.GetDataCompleted += new EventHandler<DataRetrieverReference.GetDataCompletedEventArgs>(webService_GetDataCompleted);
for (int i = 0; i < counter; i++)
{
webService.GetDataAsync(i);
}
}
void webService_GetDataCompleted(object sender, WebPortos.DataRetrieverReference.GetDataCompletedEventArgs e)
{
MessageBox.Show("It works!");//It doesn't work!
try
{
this.harborList.Add(e.Result);
}
catch (Exception exc)//It doesn't even throw ecxpetions, this method is never reached.
{
MessageBox.Show(exc.Message);
MessageBox.Show(exc.InnerException.Message);
}
}
Maybe I'm missing something really big, but my webService_GetDataCompleted method is never reached.
Thanks in advance folks!
Is this call:
webService.GetCounterAsync();
used to set counter?
If so you're not letting it finish before looping with counter as the termination value. It's highly likely that counter is still 0 so your GetDataAsync calls aren't happening.
for (int i = 0; i < counter; i++)
{
webService.GetDataAsync(i);
}
Move this code into your webService_GetCounterCompleted method, so your code becomes:
public Brasil()
{
InitializeComponent();
this.harborList = new List<vwPortos_SEP>();
DataRetrieverClient webService = new DataRetrieverClient();
webService.GetCounterCompleted +=
new EventHandler<GetCounterCompletedEventArgs>(webService_GetCounterCompleted);
webService.GetCounterAsync();
}
void webService_GetCounterCompleted(object sender,
WebPortos.DataRetrieverReference.GetCounterCompletedEventArgs e)
{
webService.GetDataCompleted +=
new EventHandler<DataRetrieverReference.GetDataCompletedEventArgs>(webService_GetDataCompleted);
for (int i = 0; i < counter; i++)
{
webService.GetDataAsync(i);
}
}
Plus your existing webService_GetDataCompleted method.