How can i add items to listView to specific tab? - c#

private int numberofallmessages = 0;
private int countMsg = 0;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
OpenPop.Pop3.Pop3Client PopClient = new OpenPop.Pop3.Pop3Client();
PopClient.Connect("mail", 110, false);
PopClient.Authenticate("me", "me",
OpenPop.Pop3.AuthenticationMethod.UsernameAndPassword);
int messageCount = PopClient.GetMessageCount();
numberofallmessages = messageCount;
allMessages = new List<OpenPop.Mime.Message>(messageCount);
for (int i = messageCount; i > 0; i--)
{
if (backgroundWorker1.CancellationPending == true)
{
e.Cancel = true;
return;
}
allMessages.Add(PopClient.GetMessage(i));
int nProgress = (messageCount - i + 1) * 100 / messageCount;
backgroundWorker1.ReportProgress(nProgress, PopClient.GetMessageCount().ToString() + "/" + i);
}
PopClient.Disconnect();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pbt.Value = e.ProgressPercentage;
pbt.Text = e.ProgressPercentage.ToString() + "%";
pbt.Invalidate();
label8.Text = e.UserState.ToString();
label8.Visible = true;
lstMail.Items.Add(allMessages[countMsg].Headers.Subject + " " + allMessages[countMsg].Headers.DateSent);
countMsg += 1;
}
In the ProgressChanged event i'm adding the items to the listView(lstMail).
lstMail.Items.Add(allMessages[countMsg].Headers.Subject + " " + allMessages[countMsg].Headers.DateSent);
But this line will keep adding also the DateSent to the first tab and not to the date tab:
There is a subject tab and a date tab and i want that the part
allMessages[countMsg].Headers.DateSent
Will be under the date tab.

Change this line:
lstMail.Items.Add(allMessages[countMsg].Headers.Subject + " " + allMessages[countMsg].Headers.DateSent);
To:
lstMail.Items.Add(new ListViewItem(new string[]
{
"", //From Column
allMessages[countMsg].Headers.Subject, //Subject Column
allMessages[countMsg].Headers.DateSent.ToString() //Date Column
}));
Hope this helps.

This is the standard way of adding items to columns in a listView.
ListViewItem item1 = new ListViewItem("Something");
item1.SubItems.Add("SubItem1a");
item1.SubItems.Add("SubItem1b");
item1.SubItems.Add("SubItem1c");
ListViewItem item2 = new ListViewItem("Something2");
item2.SubItems.Add("SubItem2a");
item2.SubItems.Add("SubItem2b");
item2.SubItems.Add("SubItem2c");
ListViewItem item3 = new ListViewItem("Something3");
item3.SubItems.Add("SubItem3a");
item3.SubItems.Add("SubItem3b");
item3.SubItems.Add("SubItem3c");
ListView1.Items.AddRange(new ListViewItem[] {item1,item2,item3});
Also see:
C# listView, how do I add items to columns 2, 3 and 4 etc?

Related

How to delete an item row in C# in a 2D array in forms

Hello I was wondering how can I delete a row from the array in C# forms using a button, like you write which line# you want to delete and then pressing the button will delete that item from the list. The idea is to delete a row using "button 2" that reads the number written in a "textbox". And also how to show a warning message if the row doesn't exist. I cant use the "list<>" or "grid thing". Thanks if any of you have a little hand to lend me.
`
public partial class Form1 : Form
{
int pos = 0;
int counter = 0;
string[,] sales = new String[50, 5];
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string product;
double price, subtotal, total;
int qty;
textBox1.BackColor = Color.White;
if (textBox1.Text == "")
{
textBox1.BackColor = Color.Red;
textBox1.Focus();
MessageBox.Show("MISSING PRODUCT!!!");
return;
}
textBox2.BackColor = Color.White;
if (textBox2.Text == "")
{
textBox2.BackColor = Color.Red;
textBox2.Focus();
MessageBox.Show("MISSING PRODUCT!!!");
return;
}
textBox3.BackColor = Color.White;
if (textBox3.Text == "")
{
textBox3.BackColor = Color.Red;
textBox3.Focus();
MessageBox.Show("MISSING PRODUCT!!!");
return;
}
product = textBox1.Text;
price = double.Parse(textBox2.Text);
qty = int.Parse(textBox3.Text);
subtotal = price * qty;
sales[pos, 0] = pos.ToString();
sales[pos, 1] = product;
sales[pos, 2] = price.ToString();
sales[pos, 3] = qty.ToString();
sales[pos, 4] = subtotal.ToString();
pos++;
textBox5.Text = "";
total = 0;
for (int i = 0; i < sales.GetLength(0); i++)
{
textBox5.Text += sales[i, 0] + " " + sales[i, 1] + " " + sales[i, 2] + " " + sales[i, 3] + " " + sales[i, 4] + " " + "\r\n";
//<>
if (sales[i, 4] != null)
{
total += int.Parse(sales[i, 4]);
}
}
textBox4.Text = total.ToString();
textBox1.Text = "";
textBox2.Text = "";
textBox3.Text = "";
textBox1.Focus();
}
private void button2_Click(object sender, EventArgs e)
{
// what to put here
}
}
`
Context
I'm going to give you two answers here as you aren't clear on whether or not you are required to use an array instead of a List<>. I'm also a little unsure about what exactly you're asking for, as you haven't given any examples of what you hope the output would be, or anything like that, so I apologize if I totally miss the mark. Just let me know and I'll change my answer to match what you're asking more appropriately :)
Using an array
private void button2_Click(object sender, EventArgs e)
{
//Checks to see if the desired row to delete exists
if(sales[pos][0] is null)
{
//Lets the user know the index is already empty
MessageBox.Show("Index has no product listed.");
return;
}
//Loops through each item at the pos index
for(int i = 0; i < sales.GetLength(1); i++)
{
//remove
sales[pos][i] = null;
}
/*******************************************************
* I'm unsure if you would want to drop the index of all
* of the arrays with a larger index number down one, so
* this next bit of code will do this, but if you don't
* want this functionality, just ignore this.
********************************************************/
//Loops through every index from pos to the second to last index
for(int i = pos; i < sales.GetLength(0) - 1; i++)
{
//Loops through every index of a product
for(int j = 0; j < sales.GetLength(1); j++)
{
//Sets current row to the value in the next
sales[i][j] = sales[i+1][j];
}
}
}
Using a List<>
/*****************************************************
* I've changed the name of the variable to pascal case
* as this is Microsoft's documentation standard.
* This also shows how you would need to change the
* declaration of the Sales object.
*****************************************************/
List<List<string>> Sales = new List<List<string>>();
private void button2_Click(object sender, EventArgs e)
{
//Checks if the index is out of bounds of the list.
if(pos > Sales.Count() - 1)
{
//Lets the user know the index out of bounds
MessageBox.Show("Index is out of bounds.");
return;
}
//Remove the product at the selected index
Sales.RemoveAt(pos);
}
Products
I notice that you seem to be trying to make a list of products to be sold. I would recommend that you create a Product class, which holds all of the information that a product has. A quick mock up of that could look something like this:
public class Product
{
/**********************************************************
* If you are unfamiliar with how properties work,
* I recommend looking into them more, but quick, simplified
* explanation is they are just variables you access from
* other classes that have access to this one by
* saying, in this case, Product.AttributeOne, and you
* can even set it by writing Product.AttributeOne = "foo";
**********************************************************/
public string AttributeOne { get; set; }
public string AttributeTwo { get; set; }
public string AttributeThree { get; set; }
public string AttributeFour { get; set; }
public string AttributeFive { get; set; }
}
Using this class could simplify other code you have, and make your array/list one dimensional. I would highly recommend making products an object, just as it would give you more functionality for a lot less code. I'll show you how the code for button1_Click and button2_Click() would look like with this change
public partial class Form1 : Form
{
int pos = 0;
int counter = 0;
List<Product> Sales = new List<Product>();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string product;
double price, subtotal, total;
int qty;
textBox1.BackColor = Color.White;
if (textBox1.Text == "")
{
textBox1.BackColor = Color.Red;
textBox1.Focus();
MessageBox.Show("MISSING PRODUCT!!!");
return;
}
textBox2.BackColor = Color.White;
if (textBox2.Text == "")
{
textBox2.BackColor = Color.Red;
textBox2.Focus();
MessageBox.Show("MISSING PRODUCT!!!");
return;
}
textBox3.BackColor = Color.White;
if (textBox3.Text == "")
{
textBox3.BackColor = Color.Red;
textBox3.Focus();
MessageBox.Show("MISSING PRODUCT!!!");
return;
}
product = textBox1.Text;
price = double.Parse(textBox2.Text);
qty = int.Parse(textBox3.Text);
subtotal = price * qty;
Sales.ElementAt(pos).AttributeOne = pos.ToString();
Sales.ElementAt(pos).AttributeTwo = product;
Sales.ElementAt(pos).AttributeThree = price.ToString();
Sales.ElementAt(pos).AttributeFour = qty.ToString();
Sales.ElementAt(pos).AttributeFive = subtotal.ToString();
pos++;
textBox5.Text = "";
total = 0;
for (int i = 0; i < Sales.Count; i++)
{
textBox5.Text += Sales.ElementAt(i).AttributeOne + " " + Sales.ElementAt(i).AttributeTwo + " " + Sales.ElementAt(i).AttributeThree + " " + Sales.ElementAt(i).AttributeFour + " " + Sales.ElementAt(i).AttributeFive + " " + "\r\n";
//<>
if (Sales.ElementAt(i).AttributeFive != null)
{
total += int.Parse(sales[i, 4]);
}
}
textBox4.Text = total.ToString();
textBox1.Text = "";
textBox2.Text = "";
textBox3.Text = "";
textBox1.Focus();
}
private void button2_Click(object sender, EventArgs e)
{
//Checks if the index is out of bounds of the list.
if(pos > Sales.Count() - 1)
{
//Lets the user know the index out of bounds
MessageBox.Show("Index is out of bounds.");
return;
}
//Remove the product at the selected index
Sales.RemoveAt(pos);
}
}

How to annotate a changed item in a listbox with a *

I just need help on how to annotate a changed item in the list box if the user changes something using the text boxes provided.
namespace HW1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string[] values = new string[5];
values[0] = textBox1.Text;
values[1] = textBox2.Text;
values[2] = textBox3.Text;
values[3] = textBox4.Text;
values[4] = textBox5.Text;
string[] temp = new string[5];
temp[0] = textBox1.Text;
temp[1] = textBox2.Text;
temp[2] = textBox3.Text;
temp[3] = textBox4.Text;
temp[4] = textBox5.Text;
if(temp != values)
{
listBox1.SelectedIndex = 0 + "*";
listBox1.Text = values[1] + "*";
listBox1.Text = values[2] + "*";
listBox1.Text = values[3] + "*";
listBox1.Text = values[4] + "*";
}
listBox1.Items.Clear();
for (int i = 0; i < values.Length; i++)
{
listBox1.Items.Add(values[i].ToString());
}
}
private void button2_Click(object sender, EventArgs e)
{
Application.Exit();
}
}
}
The program will simply replace the old input from the text box with the new without displaying a * next to the item that has changed.
Your code doesn't actually compile... not sure how this line would ever work...
listBox1.SelectedIndex = 0 + "*";
Anyway - the main problem is that your for loop adds in values without the star to the list box
for (int i = 0; i < values.Length; i++)
{
listBox1.Items.Add(values[i].ToString()); //values[i] never has a star stored in it!
}
How about something like this...?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string[] values = new [] { textBox1.Text, textBox2.Text, textBox3.Text, textBox4.Text, textBox5.Text };
for (int i = 0; i < values.Length; i++)
{
if (listBox1.Items.Count < i + 1)
{
listBox1.Items.Add(values[i].ToString());
continue;
}
string unedited = listBox1.Items[i].ToString();
if (!string.IsNullOrEmpty(unedited) && unedited.Last() == '*')
unedited = listBox1.Items[i].ToString().Substring(0, listBox1.Items[i].ToString().Length - 1);
if (unedited != values[i])
listBox1.Items[i] = values[i] + "*";
else
listBox1.Items[i] = values[i];
}
}
private void button2_Click(object sender, EventArgs e)
{
Application.Exit();
}
}
This compares the list items to the textbox values.
If the textbox value doesn't exist, a listbox item is created.
If the listbox item doesn't match the textbox value, it has a * appended to it.
If an existing value (ignoring the star) is the same as the textbox value, it is updated to ensure the star is removed.

How to convert Listview dates to local time zone?

I have 3 columns in the ListView. From,Subject,Date
I'm using the OpenPop library.
private int numberofallmessages = 0;
private int countMsg = 0;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
OpenPop.Pop3.Pop3Client PopClient = new OpenPop.Pop3.Pop3Client();
PopClient.Connect("mail", 110, false);
PopClient.Authenticate("me", "me",
OpenPop.Pop3.AuthenticationMethod.UsernameAndPassword);
List<string> uids = PopClient.GetMessageUids();
int messageCount = PopClient.GetMessageCount() -1;
numberofallmessages = messageCount;
allMessages = new List<OpenPop.Mime.Message>(messageCount);
for (int i = messageCount; i > 0; i--)//for (int i = messageCount - 1; i > -1; i--)
{
if (backgroundWorker1.CancellationPending == true)
{
e.Cancel = true;
return;
}
string currentUidOnServer = uids[i];
if (!seenUids.Contains(currentUidOnServer))
{
if (i > 0)
allMessages.Add(PopClient.GetMessage(i));
SaveFullMessage(PopClient.GetMessage(i), i);
w = new StreamWriter(emailsIDSFile, true);
w.WriteLine(currentUidOnServer);
w.Close();
int nProgress = (messageCount - i + 1) * 100 / messageCount;
backgroundWorker1.ReportProgress(nProgress, PopClient.GetMessageCount().ToString() + "/" + i);
}
}
PopClient.Disconnect();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pbt.Value = e.ProgressPercentage;
pbt.Text = e.ProgressPercentage.ToString() + "%";
pbt.Invalidate();
label8.Text = e.UserState.ToString();
label8.Visible = true;
lvnf.Items.Add(new ListViewItem(new string[]
{
allMessages[countMsg].Headers.From.ToString(), //From Column
allMessages[countMsg].Headers.Subject, //Subject Column
allMessages[countMsg].Headers.DateSent.ToString() //Date Column
}));
countMsg += 1;
}
The problem is in the progresschanged event i think. Where i add the items to each column.
When it's adding the emails to the ListView i see it like this:
The problem is on the date column the date is fine but the time in not my time. Not sure of what place the time is but in my place it's now 1:52 AM
How can i get/set the time of my place ?
I couldn't find in the line:
allMessages[countMsg].Headers.DateSent.ToString()
How to change it to my time.
Try this:
allMessages[countMsg].Headers.DateSent.ToLocalTime().ToString();
You want to utilize the DateTime.ToLocalTime() method. It does the heavy lifting for you.
Hope this helps
Edit: Removed incorrect version as the documentation for OpenPop.Net states that the MessageHeader.DateSent property is in fact a DateTime object.

how to get dynamic created text box value from panel in c# on button click

I Create dynamic text box on button click inside panel and store number
and want to retrieve its text and make total of that number how can i do that?
Following is my code for text box generation
private void btnMaterialAdd_Click(object sender, EventArgs e)
{
TextBox[] txtTeamNames = new TextBox[100];
txtTeamNames[i] = new TextBox();
string name = "TeamNumber" + i.ToString();
txtTeamNames[i].Location = new Point(1, i * 30 );
txtTeamNames[i].Width = 30;
txtTeamNames[i].Name = "ID" + i;
txtTeamNames[i].Visible = true;
int num = i + 1 ;
txtTeamNames[i].Text = num.ToString();
panel1.Controls.Add(txtTeamNames[i]);
}
How to count total value of each text box and display?
Get rid of the Array and use a List at Class Level (not a local variable in your method):
private List<TextBox> TextBoxes = new List<TextBox>();
private void btnMaterialAdd_Click(object sender, EventArgs e)
{
TextBox tb = new TextBox();
int i = TextBoxes.Count + 1;
tb.Location = new Point(1, i * 30);
tb.Width = 30;
tb.Name = "ID" + i;
tb.Text = i.ToString();
TextBoxes.Add(tb);
panel1.Controls.Add(tb);
}
Now you can iterate over that List when to get a total:
private void btnTotal_Click(object sender, EventArgs e)
{
int value;
int total = 0;
foreach (TextBox tb in TextBoxes)
{
if (int.TryParse(tb.Text, out value))
{
total = total + value;
}
else
{
MessageBox.Show(tb.Name + " = " + tb.Text, "Invalid Value");
}
}
MessageBox.Show("total = " + total.ToString());
}

WPF loading animation on a separate UI thread? (C#)

Okay, I have a loading animation that runs while a large DataTable is populated to let the user know that the program has not frozen. I have the animation working fine, but it freezes while the DataTable is updatingv as well. Is there some way to have multiple UI threads, so that the animation will continue to run while the DataTable is loading information?
EDIT: Current code is below.
private void CreateFileTable()
{
file_data = new DataSet();
data_table = new DataTable();
file_data.Tables.Add(data_table);
DataColumn tempCol = new DataColumn("File Name", typeof(string));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Ext", typeof(string));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Size", typeof(string));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Created", typeof(Label));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Modified", typeof(Label));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Accessed", typeof(Label));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Location", typeof(string));
data_table.Columns.Add(tempCol);
File_List.ItemsSource = file_data.Tables[0].DefaultView;
}
private void PopulateDirectories(string[] directories)
{
for (int i = 0; i < directories.Length; i++)
{
DirectoryInfo tempDirInfo = new DirectoryInfo(directories[i]);
bool isSystem = ((tempDirInfo.Attributes & FileAttributes.System) == FileAttributes.System);
if (!isSystem)
{
DataRow tempRow = data_table.NewRow();
tempRow["File Name"] = tempDirInfo.Name;
tempRow["Ext"] = "";
tempRow["Size"] = "";
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempDirInfo.CreationTime.ToLongDateString() + ", " + tempDirInfo.CreationTime.ToLongTimeString();
tempRow["Created"] = tempLabel;
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempDirInfo.LastWriteTime.ToLongDateString() + ", " + tempDirInfo.LastWriteTime.ToLongTimeString();
tempRow["Modified"] = tempLabel;
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempDirInfo.LastAccessTime.ToLongDateString() + ", " + tempDirInfo.LastAccessTime.ToLongTimeString();
tempRow["Accessed"] = tempLabel;
tempRow["Location"] = tempDirInfo.FullName;
data_table.Rows.Add(tempRow);
}
}
}
private void PopulateFiles(string[] files)
{
for (int i = 0; i < files.Length; i++)
{
FileInfo tempFileInfo = new FileInfo(files[i]);
bool isSystem = ((File.GetAttributes(files[i]) & FileAttributes.System) == FileAttributes.System);
if (!isSystem)
{
DataRow tempRow = data_table.NewRow();
tempRow["File Name"] = tempFileInfo.Name;
tempRow["Ext"] = tempFileInfo.Extension;
int fileSize = (int)tempFileInfo.Length;
if (fileSize > 1048576)
{
tempRow["Size"] = "" + fileSize / 1048576 + " MB";
}
else if (fileSize > 1024)
{
tempRow["Size"] = "" + fileSize / 1024 + " KB";
}
else
{
tempRow["Size"] = "" + fileSize + " B";
}
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempFileInfo.CreationTime.ToLongDateString() + ", " + tempFileInfo.CreationTime.ToLongTimeString();
tempRow["Created"] = tempLabel;
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempFileInfo.LastWriteTime.ToLongDateString() + ", " + tempFileInfo.LastWriteTime.ToLongTimeString();
tempRow["Modified"] = tempLabel;
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempFileInfo.LastAccessTime.ToLongDateString() + ", " + tempFileInfo.LastAccessTime.ToLongTimeString();
tempRow["Accessed"] = tempLabel;
tempRow["Location"] = tempFileInfo.DirectoryName;
data_table.Rows.Add(tempRow);
}
}
}
private string GetSelectedPath(TreeViewItem selectedNode)
{
return selectedNode.Tag as string;
}
private void PopulateFileList()
{
PopulateDirectories(Directory.GetDirectories(GetSelectedPath((TreeViewItem)Dir_Tree.SelectedItem)));
PopulateFiles(Directory.GetFiles(GetSelectedPath((TreeViewItem)Dir_Tree.SelectedItem)));
}
private void UpdateFileList()
{
LoadingWheel.Visibility = System.Windows.Visibility.Visible;
CreateFileTable();
PopulateFileList();
TxtFoundCount.Text = "Files/Folders Found: " + File_List.Items.Count;
LoadingWheel.Visibility = System.Windows.Visibility.Hidden;
}
I have tried using the BackgroundWorker and calling the UpdateFileList() method inside it, but I'm not having any luck.
EDIT: Below is my code for the BackgroundWorker.
private BackgroundWorker bgWorker1;
private void InitializeBackgroundWorker()
{
bgWorker1 = new BackgroundWorker();
bgWorker1.DoWork += new DoWorkEventHandler(bgWorker1_DoWork);
bgWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker1_RunWorkerCompleted);
}
private void bgWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (Dispatcher.CheckAccess())
{
PopulateFileList();
}
else
{
Dispatcher.Invoke(new Action(() => PopulateFileList()));
}
}
private void bgWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
TxtFoundCount.Text = "Files/Folders Found: " + File_List.Items.Count;
LoadingWheel.Visibility = System.Windows.Visibility.Hidden;
}
private void UpdateFileList()
{
LoadingWheel.Visibility = System.Windows.Visibility.Visible;
CreateFileTable();
bgWorker1.RunWorkerAsync();
}
I have a SelectionChanged event on a TreeView that calls InitializeBackgroundWorker() and UpdateFileList(). The list loads still, and I get no errors, but the loading animation never becomes visible.
Any help would be greatly appreciated.
Thanks.
There is only one UI thread. What you need to do is to load the data in the DataTable on a different thread.
If you want to show progress to the DataTable loading along the way (either directly, or through a ProgressBar or some other mechanism), the BackgroundWorker is a fairly straight-forward way to do that.
UPDATE: Very Simple Background Worker example
Here is a fairly simple example. It adds 100 random numbers to a collection, pausing the thread for a short time between each to simulate a long loading process. You can simply cut and paste this into a test project of your own to see it work.
The thing to notice is that the heavy lifting (the stuff that takes a while) is done in the DoWork, while all UI updates are done in ProgressChanged and RunWorkerCompleted. In fact, a separate list (numbers) is created in the DoWork handler because the global mNumbers collection is on the UI thread, and can't interact in the DoWork handler.
XAML
<Button x:Name="btnGenerateNumbers"
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="Generate Numbers" />
C# Code-Behind
BackgroundWorker bgWorker = new BackgroundWorker();
ObservableCollection<int> mNumbers = new ObservableCollection<int>();
public Window1()
{
InitializeComponent();
bgWorker.DoWork +=
new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.ProgressChanged +=
new ProgressChangedEventHandler(bgWorker_ProgressChanged);
bgWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
bgWorker.WorkerReportsProgress = true;
btnGenerateNumbers.Click += (s, e) => UpdateNumbers();
this.DataContext = this;
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progress.Visibility = Visibility.Collapsed;
lstItems.Opacity = 1d;
btnGenerateNumbers.IsEnabled = true;
}
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
List<int> numbers = (List<int>)e.UserState;
foreach (int number in numbers)
{
mNumbers.Add(number);
}
progress.Value = e.ProgressPercentage;
}
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
Random rnd = new Random();
List<int> numbers = new List<int>(10);
for (int i = 1; i <= 100; i++)
{
// Add a random number
numbers.Add(rnd.Next());
// Sleep from 1/8 of a second to 1 second
Thread.Sleep(rnd.Next(125, 1000));
// Every 10 iterations, report progress
if ((i % 10) == 0)
{
bgWorker.ReportProgress(i, numbers.ToList<int>());
numbers.Clear();
}
}
}
public ObservableCollection<int> NumberItems
{
get { return mNumbers; }
}
private void UpdateNumbers()
{
btnGenerateNumbers.IsEnabled = false;
mNumbers.Clear();
progress.Value = 0;
progress.Visibility = Visibility.Visible;
lstItems.Opacity = 0.5;
bgWorker.RunWorkerAsync();
}
I wrote a little test program which shows the use of the Dispatcher class. It just requires a WPF-Window and a ListBox with Name "listBox". Should be easy to apply this solution to your problem.
public void Populate() {
// for comparison, freezing the ui thread
for (int i = 0; i < 1000000; i++) {
listBox.Items.Add(i);
}
}
private delegate void AddItemDelegate(int item);
public void PopulateAsync() {
// create a new thread which is iterating the elements
new System.Threading.Thread(new System.Threading.ThreadStart(delegate() {
// inside the new thread: iterate the elements
for (int i = 0; i < 1000000; i++) {
// use the dispatcher to "queue" the insertion of elements into the UI-Thread
// DispatcherPriority.Background ensures Animations have a higher Priority and the UI does not freeze
// possible enhancement: group the "jobs" to small units to enhance the performance
listBox.Dispatcher.Invoke(new AddItemDelegate(delegate(int item) {
listBox.Items.Add(item);
}), System.Windows.Threading.DispatcherPriority.Background, i);
}
})).Start();
}

Categories

Resources