SubItems of ListView are not displayed in winforms - c#

I am creating a winforms application in visual studio 2017, I am populating a ListView using a
List<KeyValuePair<string, string>>
Examples of the data are:
List<KeyValuePair<ABC, 123>>
List<KeyValuePair<ABC, 456>>
List<KeyValuePair<ABC, 789>>
List<KeyValuePair<DEF, 123>>
List<KeyValuePair<DEF, 233>>
I try to diplay this in a ListView, where I would like to have sometihng like this:
ABC
123
456
789
DEF
123
233
Where the ABC and the DEF are selectable only. I try to write a code to do this, but unfortunately it only displays the ABC and DEF without the subitems.
The code I wrote is:
workOrderClusters = GetItac.FilterWorkOrderClusters();
// GetItac.FilterWorkOrderClusters() is a
List<KeyValuePair<string,string>>
string current; string previous,
foreach (var workOrderCluster in workOrderClusters)
{
current = workOrderCluster.Key;
if (current != previous)
{
var listViewItem = new ListViewItem(workOrderCluster.Key);
foreach (var cluster in workOrderClusters)
{
if (cluster.Key == current)
{
listViewItem.SubItems.Add(cluster.Value);
}
}
}
previous = current;
listView1.Items.Add(listViewItem);
My question is, is there anyway to make the ListView display as expected ?

ListView shows sub items if it's in Details view and it has some columns.
Let's say you have the following data:
var list = new List<KeyValuePair<string, int>>(){
new KeyValuePair<string, int>("ABC", 123),
new KeyValuePair<string, int>("ABC", 456),
new KeyValuePair<string, int>("ABC", 789),
new KeyValuePair<string, int>("DEF", 123),
new KeyValuePair<string, int>("DEF", 233),
};
To convert your data structure to ListView items you can first group data based on the key:
var data = list.GroupBy(x => x.Key).Select(x => new
{
Key = x.Key,
Values = x.Select(a => a.Value)
});
Then add items and sub items to the control:
foreach(var d in data)
{
var item = listView1.Items.Add(d.Key);
foreach (var v in d.Values)
item.SubItems.Add(v.ToString());
}
And then setup ListView to show them:
listView1.View = View.Details;
var count = data.Max(x => x.Values.Count());
for (int i = 0; i <= count; i++)
listView1.Columns.Add($"Column {i+1}");
Note
As also mentioned in the comments, probably TreeView is more suitable to show such data. In case you want to add that data to TreeView, after grouping data you can use the following code:
foreach (var d in data)
{
var node = treeView1.Nodes.Add(d.Key);
foreach (var v in d.Values)
node.Nodes.Add(v.ToString());
}

Related

Entity Framework: How to insert data while looping through multiple lists

I am attempting to insert data from two List.
I am able to successfully insert from one List but adding the second list using a foreach loop does not work as expected.
How do I loop through each of these list so that I can insert their values?
Code:
private void InsertList()
{
var listA = new List<string>();
var listB = new List<string>();
//Populate both list by splitting items in listbox
foreach (ListItem item in ListBox1.Items)
{
var components = item.Value.Split('/');
listA.Add(components.First());
listB.Add(components.Last());
}
using (DataContext dataContext = new DataContext())
{
foreach (var itemA in listA)
{
foreach (var itemB in listB)
{
LIST_OBJECTS listObject = new LIST_OBJECTS
{
LIST_ITEM_A = itemA,
LIST_ITEM_B = itemB
};
dataContext.LIST_OBJECTS.Add(listObject);
}
}
dataContext.SaveChanges();
}
}
What about a for loop?
for (var i = 0; i < listA.Count; i++)
{
LIST_OBJECTS listObject = new LIST_OBJECTS
{
LIST_ITEM_A = listA[i],
LIST_ITEM_B = listB[i]
};
dataContext.LIST_OBJECTS.Add(listObject);
}
Since you know from the creation of the Lists that they'll have the same number of elements, this is fine.
You can do it with LINQ
LIST_OBJECTS listObject = listA.Join(listB,
a=>listA.Indexof(a),
b=>listB.Indexof(b),
(a,b)=> new LIST_OBJECTS()
{
LIST_ITEM_A =a,
LIST_ITEM_B =b
}).ToList();

Loop through sections of a collection

Here is a loop I'm working on to create a JSON object collection:
var sb = new StringBuilder();
var list = DataAccess.Read.EventsByID(id);
foreach (var spevent in list)
{
sb.AppendFormat("'{0}':'{2}',",spevent.StartTimeDt.ToString("MM-dd-yyyy"), spevent.Link, spevent.Title);
}
The flaw is, if I have multiple events with the same date, I only get the last one because
window.eventscollection = {
'07-23-2013' : 'Item One',
'07-23-2013' : 'Item Two',
}
has two items with the same object name, and won't take duplicates.
The correct output would be to merge all items with the same date into one item as follows:
window.eventscollection = {
'07-23-2013' : 'Item OneItem Two',
}
It seems I need some kind of while or recursive loop to see if the next item has the same date as the current item and change the output, but I can't seem to get it right.
Thoughts?
You could group the items from the same date as
var sb = new StringBuilder();
var items = DataAccess.Read.EventsByID(id).GroupBy(p=>StartTimeDt.ToString("MM-dd-yyyy"));
then iterate through the grouping:
foreach (var item in items)
{
StringBuilder sb2 = new StringBuilder();
foreach(var subitem in item)
sb2.Append(String.Format("{2}",subitem.Link,subitem.Title));
sb.AppendFormat("'{0}':'{1}',",item.Key, sb2.ToString());
}
You first need to GroupBy on your list:
var sb = new StringBuilder();
var list = DataAccess.Read.EventsByID(id).GroupBy(e => e.StartTimeDt.Date);
And then you can loop within the loop
foreach (var dt in list)
{
sb.AppendFormat("'{0}'",dt.Key.ToString("yyyy-MM-dd"))
foreach (var spevent in dt)
{
sb.AppendFormat("'{1}',",, spevent.Link, spevent.Title);
}
}

adding items to columns/rows in listview using foreach

I am into day 5 of learning c#, and am trying to figure out how to fill/re-fill a ListView control, containing 10 rows and 12 columns, using a foreach loop. I have coded the functionality I'm after in C.
void listPopulate(int *listValues[], int numberOfColumns, int numberOfRows)
{
char table[100][50];
for (int columnNumber = 0; columnNumber < numberOfColumns; ++columnNumber)
{
for (int rowNumber = 0; rowNumber < numberOfRows; ++rowNumber)
{
sprintf(&table[columnNumber][rowNumber], "%d", listValues[columnNumber][rowNumber]);
// ...
}
}
}
Here is what I have figured out so far:
public void listView1_Populate()
{
ListViewItem item1 = new ListViewItem("value1");
item1.SubItems.Add("value1a");
item1.SubItems.Add("value1b");
ListViewItem item2 = new ListViewItem("value2");
item2.SubItems.Add("value2a");
item2.SubItems.Add("value2b");
ListViewItem item3 = new ListViewItem("value3");
item3.SubItems.Add("value3a");
item3.SubItems.Add("value3b");
....
listView1.Items.AddRange(new ListViewItem[] { item1, item2, item3 });
}
I'm assuming that I would have to do the creation of the list items in a separate step. So my question is: there must be a way to do this in C# with a for or foreach loop, no?
I am not sure if I understood you correctly, but here's i think what you need...
Actually it depends on your DataSource which you are using to fill up the ListView.
Something like this (I am using Dictioanry as a DataSource here) -
// Dictinary DataSource containing data to be filled in the ListView
Dictionary<string, List<string>> Values = new Dictionary<string, List<string>>()
{
{ "val1", new List<string>(){ "val1a", "val1b" } },
{ "val2", new List<string>(){ "val2a", "val2b" } },
{ "val3", new List<string>(){ "val3a", "val3b" } }
};
// ListView to be filled with the Data
ListView listView = new ListView();
// Iterate through Dictionary and fill up the ListView
foreach (string key in Values.Keys)
{
// Fill item
ListViewItem item = new ListViewItem(key);
// Fill Sub Items
List<string> list = Values[key];
item.SubItems.AddRange(list.ToArray<string>());
// Add to the ListView
listView.Items.Add(item);
}
I have simplified the code for your understanding, since there several ways to iterate through a Dictionary...
Hope it helps!!
You do this almost exactly the same as in C. Just loop through the collection...
int i = 0;
foreach (var column in listValues)
{
var item = new ListViewItem("column " + i++);
foreach (var row in column)
{
item.SubItems.Add(row);
}
listView1.Items.Add(item);
}
It's hard to provide a real example without seeing what your collection looks like, but for an array of arrays this will work.

how to add the selected item from a listbox to list <string> in c#

how to add the selected item from a listbox to list to get the username that are selected
my code:
List<String> lstitems = new List<String>();
foreach (string str in lstUserName.SelectedItem.Text)
{
lstitems.Add(str);
}
it show me error saying cannot convert char to string.... how to add the items to list or array
You need to use the SelectedItems property instead of SelectedItem:
foreach (string str in lstUserName.SelectedItems)
{
lstitems.Add(str);
}
EDIT: I just noticed this is tagged asp.net - I haven't used webforms much but looking at the documentation it seems this should work:
List<string> listItems = listBox.GetSelectedIndices()
.Select(idx => listBox.Items[idx])
.Cast<string>()
.ToList();
I note that you're using ASP.
For standard C# the following would work:
List<string> stringList = new List<string>();
foreach (string str in listBox1.SelectedItems)
{
stringList.Add(str);
}
If there's only one selected item:
List<String> lstitems = new List<String>();
lstitems.Add(lstUsername.SelectedItem.Value);
Here's a method for getting multiple selections since System.Web.UI.WebControls.ListBox doesn't support SelectedItems:
// Retrieve the value, since that's usually what's important
var lstitems = lstUsername.GetSelectedIndices()
.Select(i => lstUsername.Items[i].Value)
.ToList();
Or without LINQ (if you're still on 2.0):
List<string> lstitems = new List<string():
foreach(int i in lstUsername.GetSelectedIndices())
{
lstitems.Add(lstUsername[i].Value);
}
You can also do this
List<String> lstitems = new List<String>();
for (int i = 0; i < ListBox1.Items.Count; i++)
{
if (ListBox1.Items[i].Selected)
lstitems.Add(ListBox1.Items[i].Value);
}
If you are using a button to add selected 'item' in string list, just do this.
private void button1_Click(object sender, EventArgs e)
{
List<string> selectedItems = new List<string>();
string item = listBox1.SelectedItem.ToString();
if (!selectedItems.Contains(item))
{
selectedItems.Add(item);
}
}
You can do this in one operation:
IEnumerable<string> groupList = groupsListBox.SelectedItems.Cast<string>();
It will always work for custom objects too:
IEnumerable<CustomObject> groupList = groupListBox.SelectedItems.Cast<CustomObject>();

Merge and Update Two Lists in C#

I have two List<T> objects:
For example:
List 1:
ID, Value where Id is populated and value is blank and it contains say IDs from 1 to 10.
1,""
2,""
...
10,""
List 2:
ID, Value and other attributes all filled with values but this list is a subset of List 1 in terms of IDs. (e.g only 3 items)
2,67
4,90
5,98
What I want is a merged list 1, but with updated values. Does anyone have any good extension method which will do this or any elegent code to perform this operation. The final list should be:
ID, Value
1,""
2,67 //value from list 2
3,""
4,90
5,98
6,""
...
10,""
use linq: list1=list2.Union(list1);
I would probably use a dictionary rather than a list:
// sample data
var original = new Dictionary<int, int?>();
for (int i = 1; i <= 10; i++)
{
original.Add(i, null);
}
var updated = new Dictionary<int, int>();
updated.Add(2, 67);
updated.Add(4, 90);
updated.Add(5, 98);
updated.Add(11, 20); // add
// merge
foreach (var pair in updated)
{
original[pair.Key] = pair.Value;
}
// show results
foreach (var pair in original.OrderBy(x => x.Key))
{
Console.WriteLine(pair.Key + ": " + pair.Value);
}
If you are talking about properties of an object, it will be trickier, but still doable.
This is O(m*n) but should do the job for arbitrary lists
foreach (var record in List1)
{
var other = List2.FirstOrDefault(x => x.Key == record.Key);
if(other != null) record.Value = other.Value;
}
If the lists are guaranteed ordered, then it could be brought down to O(n) at the cost of more code. The algortihm would be
Current items start as head of each list
While items remain in both lists
If the current item of list1 has lower key than list2 advance to next in list1
else if the current item of list2 has lower key than list1 advance to next in list2
else copy value from current list2 item into list1 item and advance both lists.
If you have both lists sorted by ID, you can use a variation of the classical merge algorithm:
int pos = 0;
foreach (var e in list2) {
pos = list1.FindIndex(pos, x => x.Id==e.Id);
list1[pos].Value = e.Value;
}
Note that this also requires list2 to be a strict subset of list1 in terms of ID (i.e. list1 really contains all ids of list2)
Of course you can also wrap this in an extension method
public static void UpdateWith<T>(this List<T> list1, List<T> list2)
where T:SomeIdValueSupertype {
int pos = 0;
foreach (var e in list2) {
pos = list1.FindIndex(pos, x => x.Id==e.Id);
list1[pos].Value = e.Value;
}
}
private void btnSearch_Click(object sender, EventArgs e)
{
String searchBy = cmbSearchBy.Text.ToString();
String searchFor = txtSearchFor.Text.Trim();
var List3 = (from row in JobTitleDB.jobList
where (row.JID.ToString()+row.JobTitleName.ToString().ToLower()).Contains(searchFor.ToLower())
select row).ToList();
if (searchBy == "All")
{
dgJobTitles.DataSource = null;
//dgJobTitles.DataSource = List1;
//dgJobTitles.DataSource = List2;
//dgJobTitles.DataSource = List1.Concat(List2);
//dgJobTitles.DataSource = List1.Union(List2);
dgJobTitles.DataSource = List3;
//dgJobTitles.DataSource=List1.AddRange(List2);
}
}
Dictionary<int, string> List1 = new Dictionary<int, string>();
List1.Add(1,"");
List1.Add(2,"");
List1.Add(3,"");
List1.Add(4,"");
List1.Add(5,"");
List1.Add(6,"");
Dictionary<int, string> List2 = new Dictionary<int, string>();
List2.Add(2, "two");
List2.Add(4, "four");
List2.Add(6, "six");
var Result = List1.Select(x => new KeyValuePair<int, string>(x.Key, List2.ContainsKey(x.Key) ? List2[x.Key] : x.Value)).ToList();

Categories