I am having trouble populating a datagridview with items from a string array. Here is the code I used to call the function:
ThreadPool.QueueUserWorkItem((o) =>
ReBuildObjectExplorer();
And the function itself:
try
{
List<ExplorerItem> list = new List<ExplorerItem>();
var item = new ExplorerItem();
for (int i = 0; i < lbl.Length; i++) // lbl = string array with items
{
item.title = lbl[i].Name;
list.Add(item);
}
BeginInvoke((MethodInvoker)delegate
{
explorerList = list;
dgvObjectExplorer.RowCount = explorerList.Count;
dgvObjectExplorer.Invalidate();
});
}
catch (Exception e) { MessageBox.Show(e.ToString(); }
The problem is: Suppose there are 76 items in the array. When I use this code, it ALWAYS adds the 75th item 76 times and nothing else. Why does this happen? I can't seem to figure out what is wrong with my code.
I think you want:
try
{
List<ExplorerItem> list = new List<ExplorerItem>();
for (int i = 0; i < lbl.Length; i++) // lbl = string array with items
{
var item = new ExplorerItem();
item.title = lbl[i].Name;
list.Add(item);
}
BeginInvoke((MethodInvoker)delegate
{
explorerList = list;
dgvObjectExplorer.RowCount = explorerList.Count;
dgvObjectExplorer.Invalidate();
});
}
catch (Exception e) { MessageBox.Show(e.ToString(); }
That is, move the creation of the new ExplorerItem inside the loop rather than outside it. That way a new item is created at each iteration of the loop. If you don't create a new item in each iteration, then you are adding the same item over and over again, changing its title in every iteration.
Related
I have done the following code and works well. I am wondering how I can do a for loop to clean this code from 38 lines into 2 lines.
s0s.Text = seg[0].start.ToString("X8");
s0e.Text = seg[0].end.ToString("X8");
s1s.Text = seg[1].start.ToString("X8");
s1e.Text = seg[1].end.ToString("X8");
// .. many more ..
s19s.Text = seg[19].start.ToString("X8");
s19e.Text = seg[19].end.ToString("X8");
I can obviously do the seg[i] substitution, but how do i do it with the text boxes?
I suppose you could use the Controls property and call OfType<T>() to get all the instances of TextBoxes in your Form instance
Filters the elements of an IEnumerable based on a specified type.
Then convert the results to a Dictionary based on the control Name
// this could potentially be done in the constructor
var dict = Controls.OfType<TextBox>().ToDictionary(x => x.Name);
for (int i = 0; i < 19; i++)
{
dict[$"s{i}s"].Text = $"{seg[i].Start:X8}";
dict[$"s{i}e"].Text = $"{seg[i].End:X8}";
}
Note : This code is untested and only a guide to a possible solution
I'd be tempted to do it this way. First create two lists of your controls, the starts and the ends:
var starts = new List<TextBox>
{
s0s,
s1s,
//...
s19s
};
var ends = new List<TextBox>
{
s0e,
s1e,
//...
s19e
};
Then loop over each list:
var i = 0;
foreach (var start in starts)
{
start.Text = seg[i].start.ToString("X8");
++i;
}
i = 0;
foreach (var end in ends)
{
start.Text = seg[i].end.ToString("X8");
++i;
}
Your indexes and control numbers would need to line up perfectly though.
Note: Like TheGeneral's code, this is untested (neither of us wants to create a form with 38 text boxes with specific names)
Based on the textboxes names I would suggest alternative approach to use control designed to display collection of things - DataGridView would be one of the options.
With data binding you can achieve little bit more maintainable code
public class MyItem
{
public int Start { get; set; }
public int End { get; set; }
}
In the form create a datagridview with two bounded columns, you can do this in winforms designer without manually writing the code below.
// constructor
public MyForm()
{
var startColumn = new DataGridViewTextBoxColumn();
startColumn.DataPropertyName = "Start"; // Name of the property in MyItem class
startColumn.DefaultCellStyle.Format = "X8";
var endColumn = new DataGridViewTextBoxColumn();
endColumn.DataPropertyName = "End"; // Name of the property in MyItem class
endColumn.DefaultCellStyle.Format = "X8";
myDataGridView.Columns.AddRange(startColumn, endColumn);
myDataGridView.AutoGenerateColumns = false;
}
private void Form1_Load(object sender, EventArgs e)
{
var items = new List<MyItem>
{
new MyItem { Start = 10, End = 20 },
new MyItem { Start = 11, End = 19 },
new MyItem { Start = 12, End = 18 }
};
myDataGridView.DataSource = items;
}
firstly when you create they variables insert them all to an array. then run a loop as following:
for (int i; int < 19(your list); i++)
{
your list[i].Text = seg[i].start.ToString("X8");
your list[i].Text = seg[i].end.ToString("X8");
}
I have a little problem, I'm trying update my listView(listClients)
private ListView initializeLstView()
{
var lstView = new ListView();
var lstViewItem = new ListViewItem();
for (uint i = 0; i < 18; ++i)
{
lstViewItem = lstView.Items.Add("Free");
lstViewItem.SubItems.Add("Free");
}
return (lstView);
}
private ListView setNamesToLstView()
{
var lstView = initializeLstView();
try
{
for (uint i = 0; i < 18; ++i)
{
lstView.Items[(int)i].Text = clients.GetName(i);
lstView.Items[(int)i].SubItems[1].Text = "{" + i.ToString() + "}";
}
lstView.Update();
}
catch (Exception a)
{
Interaction.MsgBox(a.ToString());
}
return (lstView);
}
private void btnRefreshClients_Click(object sender, EventArgs e)
{
listClients = setNamesToLstView(); // Here, no update
}
But here, my listView contains nothing, no update / no refresh, why ?
How can I resolve this ?
The problem is you are adding items to another list view.
listClients is a member variable which points to a ListView which you previously added to Controls collection of your form. When you want to add items to that list view, you should add items exactly to that object using listClients.Items.Add(...).
But currently you have created a ListView in the method and added items to it and at last returned it and assigned it to listClients. It doesn't make your new created list view be shown on your form. It makes listClients variable only points to new created control, Whilst you are seeing the previous created ListView.
In the top of form1:
public class ComboboxItem
{
public string Text { get; set; }
public object Value { get; set; }
public override string ToString()
{
return Text;
}
}
List<string> results = new List<string>();
Then:
ComboboxItem item = new ComboboxItem();
var result = videoCatagories.Execute();
for (int i = 0; i < result.Items.Count; i++)
{
item.Text = result.Items[i].Snippet.Title;
item.Value = result.Items[i].Id;
comboBox1.Items.Add(item);
}
And in the end:
private void comboBox1_SelectedValueChanged(object sender, EventArgs e)
{
MessageBox.Show((comboBox1.SelectedItem as ComboboxItem).Value.ToString());
}
What i wanted to do in general is to add to the combobox the titles and then when i select a title to get the title id.
For example i run the program and select the title Weather now i want to see in a messageBow.Show the id 1
There are 31 items.
When i use a breakpoint and look on result i see 31 items when i click on the first item in index 0 i see Id = "1" and then i click on snippet and see title "weather"
Then i do the same for item in index 1 and i see Id = "19" and in the snippet the title is "animals".
But for some reason it's adding each itertion the same item many times.
Create a new instance of ComboboxItem each time you want to add a new item to the combo box:
for (int i = 0; i < result.Items.Count; i++)
{
ComboboxItem item = new ComboboxItem();
item.Text = result.Items[i].Snippet.Title;
item.Value = result.Items[i].Id;
comboBox1.Items.Add(item);
}
Your code changes the properties of the same item instance for each entry in results, then adds it to the comboBox1.Items collection. Add inserts its argument to the Items collection, it doesn't copy its contents. As a result, when the combobox is rendered, all combobox items point to the same item. To avoid this, create a new item instance for each entry in results:
for (int i = 0; i < result.Items.Count; i++)
{
var item=new ComboboxItem
{
Text = result.Items[i].Snippet.Title,
Value = result.Items[i].Id
};
comboBox1.Items.Add(item);
}
or
var items=from item in result
select new ComboboxItem
{
Text = item.Snippet.Title,
Value = item.Id
};
comboBox1.Items.AddRange(items);
You could do a simple check to make sure that the combobox doesn't already contain it before doing the insert.
ComboboxItem item = new ComboboxItem();
var result = videoCatagories.Execute();
for (int i = 0; i < result.Items.Count - 1; i++)
{
if(!comboBox1.Items.Contains(item))
{
item.Text = result.Items[i].Snippet.Title;
item.Value = result.Items[i].Id;
comboBox1.Items.Add(item);
}
}
Or you can do like this article suggests and remove every item that is the same before adding the new item to remove conflicts, No Duplicate in a Listbox or using the same way to stop duplicates from a combobox too
I have a text file that is read on the program startup and it populates a simple array. The array is then used to populate a listBox. Now what I want to do is to let the user select a item in the listBox and delete it. When the item is deleted the array should update accordingly and the listBox should be populated with the new array again(I have a function for that).
Here's what I use to populate Array:
string[] cArray = new string[10];
int counter, count;
public void loadArray()
{
try
{
StreamReader inputfile = File.OpenText("Classes.txt");
classList.Items.Clear();
while (!inputfile.EndOfStream)
{
cArray[count] = inputfile.ReadLine();
count++;
}
inputfile.Close();
}
catch (Exception e)
{
MessageBox.Show(e.Message, "Loading Error!");
}
}
And this is custom function I made to display the data into the listBox whenever I make any updates.
public void displayClasses()
{
try
{
classList.Items.Clear();
for (counter = 0; counter < count; counter++)
{
classList.Items.Add(cArray[counter]);
}
}
catch (Exception e)
{
MessageBox.Show(e.Message, "Displaying Error!");
}
}
I tried this using Nirupam's reply:
private void deleteButton_Click(object sender, EventArgs e)
{
try
{
while (classList.SelectedItems.Count != 0)
{
classList.Items.RemoveAt(classList.SelectedIndex);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
DeleteClasses(classList.SelectedIndex);
}
public void DeleteClasses(int indexId)
{
cArray = cArray.Where((source, index) => index != indexId).ToArray();
displayClasses();
}
The class does get deleted but with the displayClasses() it comes back, meaning it isn't being deleted from the array.
In comments, I guessed that you were trying to ask us two separate questions:
How can I allow a user to delete an item from a ListBox control, and capture the deleted item's index?
I'm actually not going to answer this one just yet, because we're still working out how the deletion will be performed.
How can I remove an item from an array, given that index?
The best solution to this problem is to, instead of an array, use a List<T>. In your case, a List<string>.
I don't know whether you're familiar with lists, but if not, a list is like a "mutable array." You can add or remove items relatively freely. This will accomplish what you asked for in your comment, so that removing the item at index 2 would shift all subsequent items down one.
To remove an item, use the RemoveAt method:
List<string> strs = new List<string>();
strs.Add("AAA");
strs.Add("BBB");
strs.Add("CCC");
strs.RemoveAt(1); // remove "BBB"
Console.WriteLine(strs[0]); // yields AAA
Console.WriteLine(strs[1]); // yields CCC
If you're stuck with an array for other reasons, you'd have to write the "shifting" logic yourself. I can help you with that if it's the case, but I won't bother otherwise.
For what it's worth, a list will also clean up your reading method.
This is probably how I would perform the remove operation in the array.
string[] args = new string[3];
args[0] = "AAA";
args[1] = "BBB";
args[2] = "CCC";
int indexToRemove = 1;
for (int v = indexToRemove + 1; v < args.Length; v++)
{
if(v > 0)
{
args[v - 1] = args[v];
}
}
args[args.Length - 1] = null;
write a another function DeleteClasses(int indexId) for better modularity of code which will return Array Data.It will be called from inside your displayClasses() methode .Inside it write down code according to Algo given below:
Step 1 :
Remove the Item in cArray using parameter IndexId which we will send when we call DeleteClasses(int indexId) method.
Code will be something like this:
cArray = cArray .Where((source, index) => index != indexId).ToArray();
return that cArray from where it will be called.
Step 2:
After That you will have updated Array when user choose to delete an item.
than Load your ListBox by calling displayClasses();
Note: Inside displayClasses() you should have code like this below:
try{
classList.Items.Clear();
classList.Items = cArray.ToList(); //here cArray is updated data array
}
catch (Exception e)
{
MessageBox.Show(e.Message, "Displaying Error!");
}
Overall :
Overall code looks something like this below:
Public string[] DeleteClasses(int indexId)
{
//indexId will be depends upon user selection on demand for delete
// operation.
cArray = cArray .Where((source, index) => index != indexId).ToArray();
}
public void displayClasses()
{
try{
int indexId=classList.SelectedItemIndex;//
string[] cArray = DeleteClasses(int indexId);
classList.Items.Clear();
classList.Items = cArray.ToList(); //here cArray is updated data array
}
catch (Exception e)
{
MessageBox.Show(e.Message, "Displaying Error!");
}
}
Hi I neeed to do this one in a loop, but I don't have any idea how to do. I can't do this by just simply incrementing it.
CheckBox[] checkboxarray;
checkboxarray = new CheckBox[] {
txtChckBx0, txtChckBx1, txtChckBx2, txtChckBx3, txtChckBx4, txtChckBx5,
txtChckBx6, txtChckBx7, txtChckBx8, txtChckBx9, txtChckBx10, txtChckBx11,
txtChckBx12, txtChckBx13, txtChckBx14, txtChckBx15, txtChckBx16, txtChckBx17,
txtChckBx18, txtChckBx19, txtChckBx20, txtChckBx21, txtChckBx22, txtChckBx23,
txtChckBx24, txtChckBx25, txtChckBx26, txtChckBx27, txtChckBx28, txtChckBx29,
txtChckBx30, txtChckBx31, txtChckBx32, txtChckBx33, txtChckBx34, txtChckBx35,
txtChckBx36, txtChckBx37, txtChckBx38, txtChckBx39, txtChckBx40, txtChckBx41,
txtChckBx42, txtChckBx43, txtChckBx44, txtChckBx45, txtChckBx46, txtChckBx47,
txtChckBx48, txtChckBx49, txtChckBx50, txtChckBx51, txtChckBx52, txtChckBx53,
txtChckBx54, txtChckBx55, txtChckBx56, txtChckBx57, txtChckBx58, txtChckBx59,
txtChckBx60, txtChckBx61, txtChckBx62, txtChckBx63, txtChckBx64, txtChckBx65,
txtChckBx66, txtChckBx67, txtChckBx68, txtChckBx69, txtChckBx70, txtChckBx71,
txtChckBx72, txtChckBx73, txtChckBx74, txtChckBx75, txtChckBx76, txtChckBx77,
txtChckBx78, txtChckBx79, txtChckBx80
};
If you know that the checkboxes are all on a form:
var list = new List<CheckBox>();
foreach(var control in this.Controls)
{
var checkBox = control as CheckBox;
if(checkBox != null)
{
list.Add(checkBox);
}
}
var checkBoxArray = list.ToArray();
If you don't know where the controls are then you will have to search for them.
BTW: The code above uses WinForms. If you are using WPF, Silverlight, Metro,... the container will be named differently.
You can't do new and then
checkboxarray = new CheckBox[] { txtChckBx0, ....}
it's two different ways to define an array.
you need to do:
CheckBox[] checkboxarray = { txtChckBx0, ....};
If you want it to work.
Good luck.
In WinForm
List<CheckBox> checkBox = new List<CheckBox>();
// Adding checkboxes for testing...
for (int i = 0; i <= 80; i++)
{
var cbox = new CheckBox();
cbox.Name = "txtChckBx"+ i.ToString();
checkBox.Add(cbox);
Controls.Add(cbox);
}
List<CheckBox> checkBoxfound = new List<CheckBox>();
// loop though all the controls
foreach (var item in Controls)
{
// filter for checkboxes and name should start with "txtChckBx"
if (item is CheckBox && ((CheckBox)item).Name.StartsWith("txtChckBx", StringComparison.OrdinalIgnoreCase))
{
checkBoxfound.Add((CheckBox)item);
}
}