I have the below code
public void panel_item_collections_Click(object sender, EventArgs e)
{
TextBox[] textbox_item_array = new TextBox[5];
item_textbox.Add(textbox_item_array);
textbox_item_array[0] = new TextBox();
textbox_item_array[0].Width = label_item_code.Width;
textbox_item_array[0].Height = 26;
textbox_item_array[0].Font = print_font_default;
textbox_item_array[0].Location = new Point(label_item_code.Location.X, 45 + (20 * row_count));
textbox_item_array[0].Name = string.Concat("item_code", row_count.ToString());
panel_item_collections.Controls.Add(textbox_item_array[0]);
textbox_item_array[0].Leave += new EventHandler(dynamic_text_item_code_Leave);
textbox_item_array[1] = new TextBox();
textbox_item_array[1].Width = label_item_descrition.Width;
textbox_item_array[1].Font = textbox_item_array[0].Font;
textbox_item_array[1].Location = new Point(label_item_descrition.Location.X, textbox_item_array[0].Location.Y);
textbox_item_array[1].Name = string.Concat("item_description", row_count.ToString());
panel_item_collections.Controls.Add(textbox_item_array[1]);
textbox_item_array[2] = new TextBox();
textbox_item_array[2].Width = label_item_price.Width;
textbox_item_array[2].Font = textbox_item_array[0].Font;
textbox_item_array[2].Location = new Point(label_item_price.Location.X, textbox_item_array[0].Location.Y);
textbox_item_array[2].Name = string.Concat("item_price", row_count.ToString());
panel_item_collections.Controls.Add(textbox_item_array[2]);
textbox_item_array[3] = new TextBox();
textbox_item_array[3].Width = label_item_quantity.Width;
textbox_item_array[3].Font = textbox_item_array[0].Font;
textbox_item_array[3].Location = new Point(label_item_quantity.Location.X, textbox_item_array[0].Location.Y);
textbox_item_array[3].Name = string.Concat("item_quantity", row_count.ToString());
panel_item_collections.Controls.Add(textbox_item_array[3]);
textbox_item_array[4] = new TextBox();
textbox_item_array[4].Width = label_item_total.Width;
textbox_item_array[4].Font = textbox_item_array[0].Font;
textbox_item_array[4].Location = new Point(label_item_total.Location.X, textbox_item_array[0].Location.Y);
textbox_item_array[4].Name = string.Concat("item_total", row_count.ToString());
panel_item_collections.Controls.Add(textbox_item_array[4]);
row_count++;
}
Now, here is the leave event handler:
void dynamic_text_item_code_Leave(object sender, EventArgs e)
{
//MessageBox.Show(((Control)sender).Name.Substring(((Control)sender).Name.Length - 1, 1));
int i;
string name_textbox = ((Control)sender).Name;
i = System.Convert.ToInt32(name_textbox.Substring(name_textbox.Length - 1, 1));
//MessageBox.Show(i.ToString());
//i--;
TextBox[] textbox_item_array = new TextBox[5];
textbox_item_array = (TextBox[])(item_textbox[i]);
double item_total;
Item item = new Item();
if (long.TryParse(textbox_item_array[0].Text, out item.item_code) == true)
{
if (item.get_item() == 0)
{
textbox_item_array[1].Text = item.item_details;
textbox_item_array[2].Text = item.sell_price.ToString();
textbox_item_array[3].Text = "1";
item_total = System.Convert.ToInt32(textbox_item_array[3].Text) * item.sell_price;
textbox_item_array[4].Text = item_total.ToString();
}
}
else
{
//TextBox[] textbox_item_array = new TextBox[5];
textbox_item_array = (TextBox[])(item_textbox[item_textbox.Count - 1]);
panel_item_collections.Controls.Remove(textbox_item_array[0]);
panel_item_collections.Controls.Remove(textbox_item_array[1]);
panel_item_collections.Controls.Remove(textbox_item_array[2]);
panel_item_collections.Controls.Remove(textbox_item_array[3]);
panel_item_collections.Controls.Remove(textbox_item_array[4]);
item_textbox.RemoveAt((item_textbox.Count - 1));
row_count--;
}
}
Now, the problem is like this:
If the user leave the textbox blank, the row will be deleted. The strange problem is:
If press tab, it will execute the leave event handler twice. It means it will try to delete the same textbox twice and this will create problem. Can any one help me how to avoid this double calling?
Thanks
I want to add more: here is exactly what is happening:
Now, I will give exactly how it is executed:
void dynamic_text_item_code_Leave(object sender, EventArgs e)
{
//MessageBox.Show(((Control)sender).Name.Substring(((Control)sender).Name.Length - 1, 1));
int i;
string name_textbox = ((Control)sender).Name;
i = System.Convert.ToInt32(name_textbox.Substring(name_textbox.Length - 1, 1));
//MessageBox.Show(i.ToString());
//i--;
TextBox[] textbox_item_array = new TextBox[5];
textbox_item_array = (TextBox[])(item_textbox[i]);
double item_total;
Item item = new Item();
if (long.TryParse(textbox_item_array[0].Text, out item.item_code) == true)
{
if (item.get_item() == 0)
{
textbox_item_array[1].Text = item.item_details;
textbox_item_array[2].Text = item.sell_price.ToString();
textbox_item_array[3].Text = "1";
item_total = System.Convert.ToInt32(textbox_item_array[3].Text) * item.sell_price;
textbox_item_array[4].Text = item_total.ToString();
}
}
else
{
//TextBox[] textbox_item_array = new TextBox[5];
textbox_item_array = (TextBox[])(item_textbox[item_textbox.Count - 1]);
panel_item_collections.Controls.Remove(textbox_item_array[0]);
After that, it calls the same function
void dynamic_text_item_code_Leave(object sender, EventArgs e)
{
//MessageBox.Show(((Control)sender).Name.Substring(((Control)sender).Name.Length - 1, 1));
int i;
string name_textbox = ((Control)sender).Name;
i = System.Convert.ToInt32(name_textbox.Substring(name_textbox.Length - 1, 1));
//MessageBox.Show(i.ToString());
//i--;
TextBox[] textbox_item_array = new TextBox[5];
textbox_item_array = (TextBox[])(item_textbox[i]);
double item_total;
Item item = new Item();
if (long.TryParse(textbox_item_array[0].Text, out item.item_code) == true)
{
if (item.get_item() == 0)
{
textbox_item_array[1].Text = item.item_details;
textbox_item_array[2].Text = item.sell_price.ToString();
textbox_item_array[3].Text = "1";
item_total = System.Convert.ToInt32(textbox_item_array[3].Text) * item.sell_price;
textbox_item_array[4].Text = item_total.ToString();
}
}
else
{
//TextBox[] textbox_item_array = new TextBox[5];
textbox_item_array = (TextBox[])(item_textbox[item_textbox.Count - 1]);
panel_item_collections.Controls.Remove(textbox_item_array[0]);
panel_item_collections.Controls.Remove(textbox_item_array[1]);
panel_item_collections.Controls.Remove(textbox_item_array[2]);
panel_item_collections.Controls.Remove(textbox_item_array[3]);
panel_item_collections.Controls.Remove(textbox_item_array[4]);
item_textbox.RemoveAt((item_textbox.Count - 1));
row_count--;
}
}
Then it continues here
panel_item_collections.Controls.Remove(textbox_item_array[1]);
panel_item_collections.Controls.Remove(textbox_item_array[2]);
panel_item_collections.Controls.Remove(textbox_item_array[3]);
panel_item_collections.Controls.Remove(textbox_item_array[4]);
item_textbox.RemoveAt((item_textbox.Count - 1));
row_count--;
}
}
So, why it executes once it get to the remove
Are you sure, you don't use this line twice in another part of code?
textbox_item_array[0].Leave += new EventHandler(dynamic_text_item_code_Leave);
If not I suppose you hooks dynamic_text_item_code_Leave to one another control.
EDIT
You can also to protect by adding this code:
if (textbox_item_array[0].Leave != null)
textbox_item_array[0].Leave += new EventHandler(dynamic_text_item_code_Leave);
Related
I want to create link label dynamically on each listview row.. So i wrote following code to do it..
int l = 0;
private void Timer1_Tick(object sender, EventArgs e)
{
string[] idlist = textBox1.Text.Split('\n');
//string[] title = textBox2.Text.Split('\n');
//string[] shippingcost = textBox3.Text.Split('\n');
//string[] newuserbonus = textBox4.Text.Split('\n');
//string[] itemprice = textBox5.Text.Split('\n');
string[] linkbuilder = textBox6.Text.Split('\n');
string[] title = title1.Split('\n');
string[] shippingcost = SC.Split('\n');
string[] newuserbonus = NUB.Split('\n');
string[] itemprice = IP.Split('\n');
//Button btn = new Button();
//btn.Text = "View";
//btn.BackColor = SystemColors.ButtonFace;
// Point p = this.listView1.Items[2].Position;
//p.X -= 21;
//btn.Location = p;
// btn.Size = this.listView1.Items[2].Bounds.Size;
// btn.Click += new EventHandler(btn_Click);
LinkLabel ln = new LinkLabel();
string A = null;
if (title[l] != null && shippingcost[l] != null && idlist[l] != null && itemprice[l] != null)
{
ListViewItem items = new ListViewItem((l + 1).ToString());
items.SubItems.Add(title[l]);
items.SubItems.Add(shippingcost[l]);
items.SubItems.Add(idlist[l]);
items.SubItems.Add(itemprice[l]);
// items.SubItems.Add("views");
// this.listView1.Controls.Add(btn);
ListView.Items.Add(items);
ln.Parent = ListView;
ln.Text = "View"+(l+1);
//btn.BackColor = SystemColors.ButtonFace;
Point p = this.ListView.Items[l].Position;
p.X += 1000;
p.Y += ListView.Items[l].Index;
ln.Location = p;
//ln.Size = new Size(100, 50);
this.ListView.Controls.Add(ln);
label2.Text = l.ToString();
int Ab = l;
ln.Click += (object senderw, EventArgs ew) =>
{
// MessageBox.Show(i.ToString()); // i= 3
string strUrl = linkbuilder[Ab];
Process proc = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo(strUrl);
proc.StartInfo = startInfo;
proc.Start();
};
//if (listView1.Items.Count > 0)
// listView1.Items[listView1.Items.Count - 1].Remove();
l++;
}
if (l == 60)
{
timer1.Stop();
}
}
So i got following output, S
after scrolling the listview. I saw like following..
Then i saw, when i scroll the listview. View Link label are not moving. But my expectation is not this.. I want to move these link lables when scrolling and Number of View link label and Item Number should be same line and same number while scrolling. Like 1st photo... I have no idea how to do it...
Could somebody help me to get fixed this..?
I have little issue with checkbox added programmatically. I don't know how to check which checkbox are selected, when I hit "Send Button".
layout.RemoveAllViewsInLayout();
CheckBox _Options = new CheckBox(Activity);
ScrollView _Scroll = new ScrollView(Activity);
_Scroll.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
LinearLayout _LScroll = new LinearLayout(Activity);
_LScroll.LayoutParameters = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
_LScroll.Orientation = Orientation.Vertical;
_LScroll.SetGravity(GravityFlags.CenterHorizontal);
//_Scroll.AddView(_LScroll);
Button _Send = new Button(Activity);
_Send.Text = "WyĆlij";
_Send.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
for (int i = 0; i < _Item.options.Length; i++)
{
_Options.Text = _Item.options[i];
_Options.Id = i;
_Options.LayoutParameters = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
_LScroll.AddView(_Options);
}
_Send.Click += delegate
{
_MultiAnswer._QuestionId = _Item.id;
for(int i = 0; i < _Item.options.Length; i++)
{
if (_Options.Selected == true)
{
_MultiAnswer._AnwserOptionIds.SetValue(i + 1, i);
}
}
output = JsonConvert.SerializeObject(_MultiAnswer);
SendJson(_Url, DataCache._Login, output);
SetLayout(layout, btn);
};
_Scroll.AddView(_LScroll);
layout.AddView(_Scroll);
layout.AddView(_Send);
I'll try to work on ID of checkbox, but I really don't know how to do it. I was thinking on method, which give me code which create checkbox, but still don't know how to check if checkbox is selected.
I understand that you've many checkbox controls. So add them to a list as follows:
List<Checkbox> checkboxes = new List<Checkbox>
{
chk1, chk2, chk3
};
When you want to know which ones are checked, you'll do this:
IEnumerable<Checkbox> checkedCheckboxes = checkboxes.Where(chk => chk.Checked);
This is a quick and dirty sample on how to generate dynamic cheboxes and retreive their state :
public class MainActivity : Activity
{
public class MyItem
{
public string[] options { get; set; }
public int id { get; set; }
}
public class MyMultiAnswer
{
public int _QuestionId { get; set; }
}
private List<CheckBox> _chkList = new List<CheckBox>();
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
var _Item = new MyItem() { options =new string [] { "aaa", "bbb", "ccc" }, id=0 };
var _MultiAnswer = new MyMultiAnswer() { _QuestionId = 0 };
ScrollView _Scroll = new ScrollView(this);
_Scroll.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
LinearLayout _LScroll = new LinearLayout(this);
_LScroll.LayoutParameters = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
_LScroll.Orientation = Orientation.Vertical;
_LScroll.SetGravity(GravityFlags.CenterHorizontal);
TextView txView = new TextView(this);
//_Scroll.AddView(_LScroll);
Button _Send = new Button(this);
_Send.Text = "test";
_Send.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
for (int i = 0; i < _Item.options.Length; i++)
{
CheckBox _Options = new CheckBox(this);
_chkList.Add(_Options);
_Options.Text = _Item.options[i];
_Options.Id = i;
_Options.LayoutParameters = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
_LScroll.AddView(_Options);
}
_Send.Click += delegate
{
_MultiAnswer._QuestionId = _Item.id;
string strChkIds = "";
foreach (var chk in _chkList.Where(c => c.Checked))
{
//_MultiAnswer._AnwserOptionIds.SetValue(_Options.Id + 1, _Options.Id);
//do something
strChkIds += " - " + chk.Id;
}
// or
for (int i = 0; i < _Item.options.Length; i++)
{
if (_chkList[i].Checked == true)
{
//_MultiAnswer._AnwserOptionIds.SetValue(i + 1, i);
//do something
}
}
//output = JsonConvert.SerializeObject(_MultiAnswer);
//SendJson(_Url, DataCache._Login, output);
//SetLayout(layout, btn);
txView.Text = "selected ids " + strChkIds;
};
_Scroll.AddView(_LScroll);
_LScroll.AddView(_Send);
_LScroll.AddView(txView);
// Set our view from the "main" layout resource
SetContentView(_Scroll);
}
}
This is a sample about how you can achieve this in a minimum effort.
Hello I have a code where I inside of a datagridview generate buttons with data from sql server database. But now I want to scroll them through buttons. I tried lots of things all gives me error already saw a post about this but nothing worked for me can someone help.
<-----------------------------------My code------------------------------------->
Methods to fill the datagridview:
public void TabelaFuncionario()
{
try
{
BDfuncionarios = new DataTable();
string cmd = "My select string";
var adpt = fConexao.GetDataAdapter(cmd);
BDfuncionarios.Clear();
adpt.Fill(BDfuncionarios);
}
catch (Exception r)
{
MessageBox.Show(r.Message);
}
}
public void BotaoFuncionario()
{
try
{
TabelaFuncionario();
PosXartigo = 1;
PosYartigo = 1;
//Apagar o painel todo
dataGridView1.Controls.Clear();
foreach (DataRow row in BDfuncionarios.Rows)
{
int posicaoX = ((PosXartigo - 1) * Gap_Xartigo) + xInicial + (Largura_BotaoArtigo * (PosXartigo - 1));
if (posicaoX > maximoxArtigo)
{
PosYartigo++; PosXartigo = 1;
}
else
{
PosXartigo = PosXartigo != 1 ? PosXartigo++ : 1;
}
int PontoX = ((PosXartigo - 1) * Gap_Xartigo) + xInicial + (Largura_BotaoArtigo * (PosXartigo - 1));
int PontoY = ((PosYartigo - 1) * Gap_Yartigo) + yInicial + (Altura_BotaoArtigo * (PosYartigo - 1));
Button bt1 = new Button();
bt1.Location = new Point(PontoX, PontoY);
Mo mo = new Mo();
mo.codmo = (int)row["Something"];
mo.nome_func = (string)row["Something"];
bt1.Name = "Botao" + NBotoes.ToString();
bt1.Height = Altura_BotaoArtigo;
bt1.Width = Largura_BotaoArtigo;
bt1.BackColor = Color.Tan;
bt1.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Bold);
bt1.ForeColor = Color.Black;
bt1.Text = mo.nome_func;
bt1.Tag = mo;
bt1.FlatStyle = FlatStyle.Popup;
bt1.Click += btArtigo_click;
dataGridView1.Controls.Add(bt1);
NBotoes++;
PosXartigo++;
}
}
catch (Exception r)
{
MessageBox.Show(r.Message);
}
}
Image of my form (don't know if it helps):
http://imgur.com/f5G25nX
<--------------------------EDITED--------------------------------->
i have tried things like this :https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.rowcount(v=vs.110).aspx
Gives me out of range or something like that
And tried this just now
int row = dataGridView1.RowCount;
MessageBox.Show(row+"");
And it displays me 0; how can i have buttons inside my grid but have 0 rows?
I solved the problem using this panel instead of datagrid for what u wanted it was much better the code will be below:
Methods:
public void TabelaFuncionario()
{
try
{
BDfuncionarios = new DataTable();
string cmd = "your select";
var adpt = fConexao.GetDataAdapter(cmd);
BDfuncionarios.Clear();
adpt.Fill(BDfuncionarios);
}
catch (Exception r)
{
MessageBox.Show(r.Message);
}
}
public void BotaoFuncionario()
{
try
{
TabelaFuncionario();
PosXartigo = 1;
PosYartigo = 1;
//Apagar o painel todo
panel2.Controls.Clear();
foreach (DataRow row in BDfuncionarios.Rows)
{
int posicaoX = ((PosXartigo - 1) * Gap_Xartigo) + xInicial + (Largura_BotaoArtigo * (PosXartigo - 1));
if (posicaoX > maximoxArtigo)
{
PosYartigo++; PosXartigo = 1;
}
else
{
PosXartigo = PosXartigo != 1 ? PosXartigo++ : 1;
}
int PontoX = ((PosXartigo - 1) * Gap_Xartigo) + xInicial + (Largura_BotaoArtigo * (PosXartigo - 1));
int PontoY = ((PosYartigo - 1) * Gap_Yartigo) + yInicial + (Altura_BotaoArtigo * (PosYartigo - 1));
Button bt1 = new Button();
bt1.Location = new Point(PontoX, PontoY);
Mo mo = new Mo();
mo.codmo = (int)row["Var1"];
mo.nome_func = (string)row["Var2"];
bt1.Name = "Botao" + NBotoes.ToString();
bt1.Height = Altura_BotaoArtigo;
bt1.Width = Largura_BotaoArtigo;
bt1.BackColor = Color.Tan;
bt1.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Bold);
bt1.ForeColor = Color.Black;
bt1.Text = mo.nome_func;
bt1.Tag = mo;
bt1.FlatStyle = FlatStyle.Popup;
bt1.Click += btFuncionario_click;
panel2.Controls.Add(bt1);
NBotoes++;
PosXartigo++;
}
}
catch (Exception r)
{
MessageBox.Show(r.Message);
}
}
Now the PainelExtension class:
public static class PanelExtension
{
public static void ScrollDown(this Panel p, int pos)
{
//pos passed in should be positive
using (Control c = new Control() { Parent = p, Height = 1, Top = p.ClientSize.Height + pos })
{
p.ScrollControlIntoView(c);
}
}
public static void ScrollUp(this Panel p, int pos)
{
//pos passed in should be negative
using (Control c = new Control() { Parent = p, Height = 1, Top = pos })
{
p.ScrollControlIntoView(c);
}
}
}
The up and down button click:
private void upbt_Click(object sender, EventArgs e)
{
if (i >= 0) i = -1;
panel2.ScrollUp(i=i-30);
}
private void downbt_Click(object sender, EventArgs e)
{
if (i < 0) i = 0;
panel2.ScrollDown(i=i+20);
}
I got it to work like this maybe there were other ways to do it i choose this one.
In Quize method I am passing qestions which contains set of all my Questions to be displayed using DisplayQuestion(),Question is my Class, Problem is that I am getting only First Question displayed, how can I get them displayed when i click on listviewItem if suppose questionscontains 10 Questions,than in listviewItem I have displayed numbers(1 2 3 4 5 ....10),when i click on each number how do i display that particular Questiondisplyed on click and if not clicked how all Questions displayed one by one using timer
public partial class GroupExmStart : Form
{
string[] randomQsn = new string[totQsn + 1]; //totQsn is the total number of question for e.g.10
public GroupExmStart(string GroupName, string DurationID)
{
InitializeComponent();
this.GrpID=GroupName;
TopiID=db.GetTopicIDForGroup(GrpID);
string[] conf = db.GetConfiguration(Convert.ToInt16(DurationID)).Split('|');
Question qsn = new Question();
var questions = qsn.Foo(TopiID, conf);
int z = Quiz(questions);
totQsn = Convert.ToInt16(conf[0]);
for (int kk = 1; kk <= totQsn; kk++)
{
ListViewItem lvi = new ListViewItem();
lvi.Text = kk.ToString();
listView1.Items.Add(lvi);
}
randomQsn = new string[totQsn + 1];
timer1.Interval = 1000; //1000ms = 1sec
timer1.Tick += new EventHandler(timer1_Tick);
timer1.Start();
}
int Quiz(List<Question> questions)
{
foreach (Question question in questions)
{
DisplayQuestion(question);
}
return 0;
}
private void DisplayQuestion(Question question)
{
string Q = question.Text;
label5.Text = Q;
string OP1 = question.Option1;
string OP2 = question.Option2;
string OP3 = question.Option3;
string OP4 = question.Option4;
radioButton12.Text = OP1;
radioButton11.Text = OP2;
radioButton10.Text = OP3;
radioButton9.Text = OP4;
}
private void listView1_MouseClick(object sender, MouseEventArgs e)
{
if (randomQsn.GetLength(0) >= 0)
{
if (listView1.SelectedItems.Count > 0)
{
//here how should i get That particular Question so that i can display it
//something like this ? //Convert.ToInt16(listView1.SelectedItems[0].SubItems[0].Text)
DisplayQuestion(question);
}
}
}
private void timer1_Tick(object sender, EventArgs e)
{
tik++;
if (tik == 60)
{
label1.Text = (Convert.ToInt16(label1.Text) - 1).ToString();
tik = 0;
}
}
}
Thanks for any help in advance
The following is what you are looking for. You must grab the text of the list view item and use that as the index of the question.
if (listView1.SelectedItems.Count > 0)
{
var q = Convert.ToInt16(listView1.SelectedItems[0].Text);
var selectedQuestion = questions[q - 1];
DisplayQuestion(selectedQuestion);
}
In order for this to work, modify your constructor to the following:
private List<Question> questions;
public partial class GroupExmStart : Form
{
string[] randomQsn = new string[totQsn + 1]; //totQsn is the total number of question for e.g.10
public GroupExmStart(string GroupName, string DurationID)
{
InitializeComponent();
this.GrpID=GroupName;
TopiID=db.GetTopicIDForGroup(GrpID);
string[] conf = db.GetConfiguration(Convert.ToInt16(DurationID)).Split('|');
Question qsn = new Question();
/// THIS IS MODIFIED //
questions = qsn.Foo(TopiID, conf);
int z = Quiz(questions);
totQsn = Convert.ToInt16(conf[0]);
for (int kk = 1; kk <= totQsn; kk++)
{
ListViewItem lvi = new ListViewItem();
lvi.Text = kk.ToString();
listView1.Items.Add(lvi);
}
randomQsn = new string[totQsn + 1];
timer1.Interval = 1000; //1000ms = 1sec
timer1.Tick += new EventHandler(timer1_Tick);
timer1.Start();
}
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();
}