Convert vertical rows to horizontal datatable - c#

I have a web service and in the code below I loop through a collection of items. These items are desplayed in an xml like fashion (see example below). However I would like to display them in table like fashion. I thought of using the datatable to insert the items in there and then redisplay them.
585344
585344 Title Issue 1
585344 Number 140024
585344 State In progress
585350
585350 Title Issue 2
585350 Number 140026
585350 State Classification
How can I display them like this:
ID | Title | Number | State
585344 issue 1 140024 In progress
static void Main(string[] args)
{
ABWebService webSvc = new ABWebService();
GetObjectListData gold = new GetObjectListData();
gold.folderPath = "01. ITSM - Service Operation";
gold.RequiredField = new RequiredField[3];
gold.RequiredField[0] = new RequiredField {Value = "Title"};
gold.RequiredField[1] = new RequiredField {Value = "Number"};
gold.RequiredField[2] = new RequiredField {Value = "State"};
try
{
GetObjectListResult golr = webSvc.GetObjectList(gold);
List<NewsTracker> list = new List<NewsTracker>();
if (golr.success)
{
ObjectData[] myObjects = golr.Object;
for (int i = 0; i < myObjects.Length; i++)
{
Console.WriteLine(myObjects[i].id);
foreach (object myItem in myObjects[i].Items)
{
string field1 = string.Empty;
string val1 = string.Empty;
int val2 = 0;
string field2 = string.Empty;
StringVal item = myItem as StringVal;
if (item != null)
{
field1 = item.name;
val1 = item.Value;
Console.WriteLine("\t" + myObjects[i].id + " " + field1 + " " + val1);
}
LongIntVal val = myItem as LongIntVal;
if (val != null)
{
field2 = val.name;
val2 = val.Value;
Console.WriteLine("\t" + myObjects[i].id + " " + field2 + " " + val2);
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
}
EDIT 1:
can you tell me how can I add them in a generic list like this: list.Add(new NewsTracker(title,number,state)); so that I can loop through and do other things with the list?

if you have only simple types then you can use reflection instead to build that table. here an example of a method i often use to send class info to a web service. Can be tweaked to receive a List and create 1 table with multiple rows once you understand the logic
public static DataTable ObjectToData(object o)
{
DataTable dt = new DataTable("OutputData");
DataRow dr = dt.NewRow();
dt.Rows.Add(dr);
o.GetType().GetProperties().ToList().ForEach(f =>
{
try
{
f.GetValue(o, null);
dt.Columns.Add(f.Name, f.PropertyType);
dt.Rows[0][f.Name] = f.GetValue(o, null);
}
catch { }
});
return dt;
}
If you pass the object ClassA per structure below
ClassA
{
string Value1 = "abc";
DateTime Value2 = DateTime.Now();
int Value3 = 12;
}
ObjectToData(MyClassA); datatable will look like the following :
|----------|-------------------------|----------|
| Value1 | Value2 | Value3 |
|==========|=========================|==========|
| "abc" | 2015/10/30 08:00:00 AM | 12 |
|----------|-------------------------|----------|

You could group by the key (ID in your example) and fill the other values accordingly. If you want a generic code, you could use some generic properties, correlating "name of column" and "value", like this:
Pseudo-code:
// Consider ID as the key (maybe int in your case) and GenericProperty as a class of just 2 properties: name of property and value.
List<YourClass> list;
Dictionary<ID, HashSet<GenericProperty>> dict = ConvertToDictionary(list); // Convert the list to a dictionary
// Aggregate values per ID (key)
// You can get the PropertyName with reflection
foreach (var pair in dict)
{
var row = DataTable.Rows.AddRow();
foreach (var values in pair.Value)
row[pair.Value.PropertyName] = pair.Value.Value;
}
Note that if you don't need a generic code, than the class GenericProperty could be an specific class with all values as properties. In your example, 3 properties: title, number, state. Then you can use them to fill the DataTable. Example as follow:
Aditionally, you can get the property name and value with reflection (example here), or as Franck said.
EDIT (based on questions edit):
Let's make an example for "myObjects" object that you've shown.
If I understood correctly, "myObjects" is already your grouped list. So "myObjects.Items" are the values of one grouped key.
So we have:
List<NewsTracker> list = new List<NewsTracker>(myObjects.Count);
foreach (var obj in myObjects)
{
// 'obj' is one key, so one value of NewsTracker
NewsTracker nt = new NewsTracker();
foreach (var item in myObjects.Items)
{
// Capture the name of the item and check what property is it from
// Code you've show in your question. --> I'll consider that you already know how to get "fieldName" as with "value" (object)
if (fieldName.Equals("Title"))
nt.Title = (string)value;
else if (fieldName.Equals("Number")
nt.Number = (int)value;
// ... and so on ...
}
// At the end of the inner loop, we must have a "NewsTracker" filled with the correct values, so now we only need to add them to the main list
list.Add(nt);
}
Note: I've wrote an specific code to your problem considerind the 'NewsTracker' class.

Related

Each row iterate through n columns

I have the code below. To explain there will always be values for the 'tl' variable.
At the moment its hard coded to always assume 4 columns in the row, but I want to make it work based on the count of the columns and make it build the levels based on how many columns there are, but there also needs to be a value in the column.
So at the moment if there is a value in column 2, it will build the 'ltwo' variable, and then if there is a value in column 3 it does the 'lthree'.
I want to make it build as many levels as it needs to so im not repeating code and having the same code over and over.
public static List<AdditionalPropertyType> SQLAddPropsStructured(DataTable dataTable, List<AdditionalPropertyType> currentadditionalproperties)
{
foreach (DataRow row in dataTable.Rows)
{
var tl = new AdditionalPropertyType
{
Name = row[0].ToString(),
Value = row[1].ToString()
};
if (!String.IsNullOrEmpty(row[2].ToString()))
{
var ltwo = new AdditionalPropertyType
{
Name = row[2].ToString()
};
var ltwolist = new List<AdditionalPropertyType>();
ltwolist.Add(tl);
ltwo.AdditionalProperties = ltwolist;
if (!String.IsNullOrEmpty(row[3].ToString()))
{
var lthree = new AdditionalPropertyType
{
Name = row[3].ToString()
};
var lthreelist = new List<AdditionalPropertyType>();
lthreelist.Add(ltwo);
lthree.AdditionalProperties = lthreelist;
currentadditionalproperties.Insert(0, lthree);
}
else
currentadditionalproperties.Insert(0, ltwo);
}
else
currentadditionalproperties.Insert(0, tl);
}
return currentadditionalproperties;
}
You can get the columns using the Columns property of the DataTable:
foreach (DataRow row in dataTable.Rows)
{
foreach(DataColumn column in dataTable.Columns)
{
Trace.WriteLine(column.ColumnName + " = " + row[column]);
}
}
You probably want to do something like this: (written on the websites, some minor typos can be present)
You need to iterate the additional columns and check if there is a value present. When there is a value, create a backup reference and renew your property.
public static List<AdditionalPropertyType> SQLAddPropsStructured(DataTable dataTable, List<AdditionalPropertyType> currentadditionalproperties)
{
// check if there are atleast 2 columns defined
if(dataTable.Columns.Count < 2)
throw new Exception("At least two columns are required");
// The result
var currentadditionalproperties = new List<AdditionalPropertyType>();
// iterate the rows
foreach (DataRow row in dataTable.Rows)
{
// create the base property
var tl = new AdditionalPropertyType
{
Name = row[0].ToString(),
Value = row[1].ToString()
};
// check the rest of the columns for additional names
foreach(int index=2;index<dataTable.Columns.Count;index++)
{
var columnValue = row[index].ToString();
// if the column is empty, discontinue the iteration
if(String.IsNullOrEmpty(columnValue))
break;
// create a backup reference.
var previous = tl;
// create a new AdditionalPropertyType
var tl = new AdditionalPropertyType { Name = columnValue };
// Create the list
tl.AdditionalProperties = new List<AdditionalPropertyType>();
// add the previous (backup reference)
tl.AdditionalProperties.Add(previous);
}
// insert the 'chain' of additional properties on the list at possition 0
currentadditionalproperties.Insert(0, tl);
}
// return the list
return currentadditionalproperties;
}
The first step is to reverse your condition and make use of the keyword continue
public static List<AdditionalPropertyType> SQLAddPropsStructured(DataTable dataTable, List<AdditionalPropertyType> currentadditionalproperties)
{
foreach (DataRow row in dataTable.Rows)
{
var tl = new AdditionalPropertyType
{
Name = row[0].ToString(),
Value = row[1].ToString()
};
if (String.IsNullOrEmpty(row[2].ToString())){
currentadditionalproperties.Insert(0, tl);
continue;
}
var ltwo = new AdditionalPropertyType
{
Name = row[2].ToString()
};
var ltwolist = new List<AdditionalPropertyType>();
ltwolist.Add(tl);
ltwo.AdditionalProperties = ltwolist;
if (String.IsNullOrEmpty(row[3].ToString())) {
currentadditionalproperties.Insert(0, ltwo);
continue;
}
var lthree = new AdditionalPropertyType
{
Name = row[3].ToString()
};
var lthreelist = new List<AdditionalPropertyType>();
lthreelist.Add(ltwo);
lthree.AdditionalProperties = lthreelist;
currentadditionalproperties.Insert(0, lthree);
}
return currentadditionalproperties;
}
Now, the code is clearer. The next step is to collect the repeating cases. Note the second case onward is repeating. Thus, do further simplification:
public static List<AdditionalPropertyType> SQLAddPropsStructured(DataTable dataTable, List<AdditionalPropertyType> currentadditionalproperties)
{
foreach (DataRow row in dataTable.Rows)
{
var tlprev = new AdditionalPropertyType
{
Name = row[0].ToString(),
Value = row[1].ToString()
};
bool isTlUpdated = true;
for (int i = 2; i <= 3; ++i) { //change this according to your need
if (String.IsNullOrEmpty(row[i].ToString()) && isTlUpdated){
currentadditionalproperties.Insert(0, tlprev);
isTlUpdated = false;
break; //note that this will now change to break to break from the current for-loop
}
var lnext = new AdditionalPropertyType
{
Name = row[i].ToString()
};
var lnextlist = new List<AdditionalPropertyType>();
lnextlist.Add(tlprev);
lnext.AdditionalProperties = lnextlist;
tlprev = lnext; //need to record this for the next loop or end of the case
isTlUpdated = true;
}
if (isTlUpdated) //correction by Jeroen
currentadditionalproperties.Insert(0, tlprev);
}
return currentadditionalproperties;
}
The key is to simplify the code step-by-step
You haven't posted all your code, so I had to guess in a couple of places (such as what the "currentAdditionalProperties" does).
I think that the below code illustrates what you want to do by making the logic extendable depending on how many columns the data table has.
The trick is to just store the "last thing" in a variable, so it can be used for the "current thing". At the end, whatever was the "last thing" is what you want to store in your "currentAdditionalProperties" object. I have commented so you can see the logic.
private List<AdditionalPropertyType> SQLAddPropsStructured(DataTable dataTable)
{
AdditionalPropertyType lastNewType; // to remember the previous new instance
// for all rows...
foreach (DataRow row in dataTable.Rows)
{
// the first type takes name and value from the first two fields
AdditionalPropertyType newType = new AdditionalPropertyType();
newType.Name = row[0].ToString();
newType.Value = row[1].ToString();
// remember this type: it is used as the AdditionalProperties for the NEXT type
lastNewType = newType;
// additional types start from field 2
int field = 2;
// iterate until we find a NULL field.
// If you want to check for the end of the fields rather than a NULL value, then instead use:
// while(field < dataTable.Columns.Count)
while(!String.IsNullOrEmpty(row[field].ToString()))
{
// create new type
var newSubType = new AdditionalPropertyType();
// get name
Name = row[field].ToString();
// new type takes the PREVIOUS type as its additional parameters
List<AdditionalPropertyType> propertyData = new List<AdditionalPropertyType>();
propertyData.Add(lastNewType);
newSubType.AdditionalProperties = propertyData;
// remember THIS type for the NEXT type
lastNewType = newSubType;
// process next field (if valid)
field++;
}
// put the last set of properties found into the current properties
currentAdditionalProperties.Insert(0, lastNewType);
return currentAdditionalProperties;
}
}

Compare two DataRows excluding some columns

I have two DataTables, e.g. OriginalEntity and Entity.
Interface application modifies Entity dataTable's rows. While saving I want to check DataRows that is modified or different from OrigianlEntity.
But, also I need to exclude few fields while comparing e.g. modified date and other audit fields.
Currently I am looping through each rows of datatable, like this:
List<string> auditFields = new List<string>(){"createdon","modifiedon"};
string IdentityKeyName = "id";
object ent,orgEnt;
foreach(string columnName in columnList) // ColumnList is List of columns available in datatable
{
foreach(DataRow dr in Entity.Rows)
{
ent = dr[columnName];
orgEnt = OriginalEntity.Select(IdentityKeyName + " = " + dr[IdentityKeyName].ToString())[0][columnName];
if(!ent.Equals(orgEnt) && !auditFields.Contains(columnName))
{
isModified = true;
break;
}
}
}
I just want an efficient way to achieve above. Please suggest.
Thanks everyone for your suggestion, and this is my (as I don't have primary key defined)
Solution:
public bool isModified(DataTable dt1, DataTable dt2, string IdentityKeyName)
{
bool isModified = false;
List<string> auditFields = new List<string>() { "createdon", "modifiedon" };
isModified = isModified || (dt1.Rows.Count != dt2.Rows.Count);
if(!isModified)
{
//Approach takes 150 ms to compare two datatable of 10000 rows and 24 columns each
DataTable copyOriginalEntity = dt1.Copy();
DataTable copyEntity = dt2.Copy();
//Exclude field you don't want in your comparison -- It was my main task
foreach(string column in auditFields)
{
copyOriginalEntity.Columns.Remove(column);
copyEntity.Columns.Remove(column);
}
for(int i=0;i<copyOriginalEntity.Rows.Count;i++)
{
var origItems = copyOriginalEntity.Rows[i].ItemArray;
var entityItem = copyEntity.Select(IdentityKeyName + " = " + copyOriginalEntity.Rows[i][dentityKeyName].ToString())[0].ItemArray;
if(string.Concat(origItems) != string.Concat(entityItem)){ isModified = true; break; }
}
}
return isModified;
}
You are going to have to loop though the columns to compare. This compare ent.Equals(orgEnt) in your code is comparing if the object references are the same. This doesn't seem like what you want and you want to compare values.
public bool IsChanged(DataTable original, DataTable source, string idKeyName, params string[] ignoreColumns)
{
// make sure "key" column exist in both
if (!original.Columns.Contains(idKeyName) || !source.Columns.Contains(idKeyName))
{
throw new MissingPrimaryKeyException("Primary key column not found.");
}
// if source rows are not the same as original then something was deleted or added
if (source.Rows.Count != original.Rows.Count)
{
return false;
}
// Get a list of columns ignoring passed in and key (key will have to be equal to find)
var originalColumns =
original.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName)
.Where(n => !ignoreColumns.Contains(n) && n != idKeyName)
.ToArray();
// check to make sure same column count otherwise just fail no need to check
var sourceColumnsCount =
source.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName).Count(originalColumns.Contains);
if (originalColumns.Length != sourceColumnsCount)
{
return false;
}
//Switch to linq
var sourceRows = source.AsEnumerable();
return sourceRows.All(sourceRow =>
{
// use select since not real key
var originalCheck = original.Select(idKeyName + " = " + sourceRow[idKeyName]);
if (originalCheck.Length != 1)
{
// Couldn't find key or multiple matches
return false;
}
var originalRow = originalCheck.First();
//Since using same array we can use linq's SequenceEqual to compare for us
return
originalColumns.Select(oc => sourceRow[oc])
.SequenceEqual(originalColumns.Select(oc => originalRow[oc]));
});
}
There might be some micro optimizations but I think no matter what you will have to check each column.

filtered Query for objectContext Using Linq to SQL

i have tried to search some examples about my approach but all questions was not close enough to what i was trying to achieve .
for the TLDR sake , Question is : how do i make it work as in plain sql query?
using c# - Winforms with SqlCompact4 and Linq to SQL
my scenario involves a form with all the relevant Db table columns as availble filters to query
and then on text change event of each filtertextbox as a filter, the datasource of the gridview updates accordingly
and because i allow filtered search via many of them columns i was trying to avoid use of some extra
lines of code.
so lets say we only concentrate on 4 columns
custID, name, email, cellPhone
each has its corresponding TextBox.
i am trying to make a query as follows :
first i systematically collect all Textbox into a List
var AllFormsSearchFiltersTBXLst = new List<TextBox>();
code that collects all tbx on current form
var AllFormsSearchFiltersTBXLst = [currentFormHere].Controls.OfType<TextBox>();
so now i have all of textboxes as filters regardless if they have any value
then check who has some value in it
forech textbox in this filters textboxes if text length is greater than zero
it means that current filter is active
then.. a second list AllFormsACTIVESearchfiltersTBXLst will contain only active filters
what i was trying to achieve was in same way i didn't have to specify each of textbox objects
i just looped through each of them all as a collection and didn't have to specify each via it's id
now i want to make a filter on a dbContext using only those active filters
so i will not have to ask if current tbxName is email
like
query = db.Where(db=>db.email.Contains(TbxEmail.Text));
and again and again for each of 10 to 15 columns
what i have got so far is nothing that implements what i was heading to.
using (SqlCeConnection ClientsConn = new SqlCeConnection(ConfigurationManager.ConnectionStrings["Conn_DB_RCL_CRM2014"].ConnectionString))
{
System.Data.Linq.Table<ContactsClients> db = null;
// get all column names from context
var x =(System.Reflection.MemberInfo[]) typeof(ContactsClients).GetProperties();
using (DB_RCL_CRM2014Context Context = new DB_RCL_CRM2014Context(ClientsConn))
{
if (!Filtered)
db = Context.ContactsClients;//.Where(client => client.Name.Contains("fler"));
else
{
db = Context.ContactsClients;
// filters Dictionary contains the name of textBox and its value
// I've named TBX as Columns names specially so i could equalize it to the columns names when needed to automate
foreach (KeyValuePair<string,string> CurFltrKVP in FiltersDict)
{
foreach (var memberInfo in x)
{
// couldn't find out how to build the query
}
}
}
BindingSource BS_Clients = new BindingSource();
BS_Clients.DataSource = db;
GV_ClientInfo_Search.DataSource = BS_Clients;
what i normally do when working with plain sql is
foreach textbox take its value and add it into a string as filter
var q = "where " ;
foreach(tbx CurTBX in ALLFILTERTBX)
{
q +=CurTBX.Name +" LIKE '%" + CurTBX.Text + "%'";
// and some checking of last element in list off cores
}
then pass this string as a filter to the main select query ... that simple
how do i make it work as in plain sql query?
I think that you're trying to get the property of db dynamically, like: db.email according to the looped name of your textbox (here 'email'). However, I recommend you to do it some other way. I'd make a switch for each type of the property, like: email, name etc. Something like this:
// Create a list for the results
var results = new List<YourDBResultTypeHere>();
foreach(tbx CurTBX in ALLFILTERTBX)
{
switch(CurTBX.Name) {
case "email":
results.AddRange(db.Where(db => db.email.Contains(tbx.Text)).ToList());
break;
case "name":
results.AddRange(db.Where(db => db.name.Contains(tbx.Text)).ToList());
break;
}
}
try this
void UpdateGridViewData(bool Filtered=false, Dictionary<string,string> FiltersDict = null)
{
using (SqlCeConnection ClientsConn = new SqlCeConnection(ConfigurationManager.ConnectionStrings["Conn_DB_RCL_CRM2014"].ConnectionString))
{
System.Data.Linq.Table<ContactsClients> db = null;
IEnumerable<ContactsClients> IDB = null;
BindingSource BS_Clients = new BindingSource();
System.Reflection.MemberInfo[] AllDbTblClientsColumns = (System.Reflection.MemberInfo[])typeof(ContactsClients).GetProperties();
using (DB_RCL_CRM2014Context Context = new DB_RCL_CRM2014Context(ClientsConn))
{
if (!Filtered)
{
db = Context.ContactsClients;
BS_Clients.DataSource = db;
}
else
{
string fltr = "";
var and = "";
if (FiltersDict.Count > 1) and = "AND";
for (int i = 0; i < FiltersDict.Count; i++)
{
KeyValuePair<string, string> CurFltrKVP = FiltersDict.ElementAt(i);
if (i >= FiltersDict.Count-1) and = "";
for (int j = 0; j < AllDbTblClientsColumns.Length; j++)
{
if (AllDbTblClientsColumns[j].Name.Equals(CurFltrKVP.Key))
{
fltr += string.Format("{0} Like '%{1}%' {2} ", AllDbTblClientsColumns[j].Name, CurFltrKVP.Value, and);
}
}
}
try
{
IDB = Context.ExecuteQuery<ContactsClients>(
"SELECT * " +
"FROM ContactsCosmeticsClients " +
"WHERE " + fltr
);
BS_Clients.DataSource = IDB;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
GV_ClientInfo_Search.DataSource = BS_Clients;
}
}
}

How to convert datatable to dictionary in ASP.NET/C#

The DataTable below:
ClassID ClassName StudentID StudentName
1 A 1000 student666
2 B 1100 student111
5 C 1500 student777
1 A 1200 student222
2 B 1080 student999
The dictionary key is composed of "ClassID ,ClassName " and value is composed of "StudentID,StudentName" .
Dictionary<string, string> d = new Dictionary<string, string>();
foreach (DataRow dr in table.Rows)
{
string key=dr["ClassID"].ToString() + dr["ClassName"].ToString();
if (!d.ContainsKey(key))
{
//Do something();......
}
else
{
//Do something();......
}
}
foreach (var s in d.Keys)
{
Response.Write(s+"|+"+d[s]+"<br>");
}
Is there a faster way?
assume that key is '1,A' ,Value should be ' 1000,student666' and '1200,student222'
Here goes then. Using Linq, you can group them then perform string concatenation if you want.
// Start by grouping
var groups = table.AsEnumerable()
.Select(r => new {
ClassID = r.Field<int>("ClassID"),
ClassName = r.Field<string>("ClassName"),
StudentID = r.Field<int>("StudentID"),
StudentName = r.Field<string>("StudentName")
}).GroupBy(e => new { e.ClassID, e.ClassName });
// Then create the strings. The groups will be an IGrouping<TGroup, T> of anonymous objects but
// intellisense will help you with that.
foreach(var line in groups.Select(g => String.Format("{0},{1}|+{2}<br/>",
g.Key.ClassID,
g.Key.ClassName,
String.Join(" and ", g.Select(e => String.Format("{0},{1}", e.StudentID, e.StudentName))))))
{
Response.Write(line);
}
Try this:
Dictionary<string, string> d = new Dictionary<string, string>();
foreach (DataRow dr in table.Rows)
{
string key=dr["ClassID"].ToString() + "-" + dr["ClassName"].ToString();
string value=dr["StudentID"].ToString() + "-" + dr["StudentName"].ToString();
if (!d.ContainsKey(key))
{
d.Add(key, value);
}
}
Reference
Dictionary.Add Method
OR ELSE Try Onkelborg's Answer
How to use compound key for dictionary?
The tricky thing here is the composite key (ClassID, ClassName). Once you identify that, it's easy to search this site for the solution.
I'd recommend using tuples as pointed out here: Composite Key Dictionary
The easiest way is to use a string value of ClassID|ClassName as key. For example, use string value "1|A" for key for the first row, and string value "2|B" for key for the second row, etc.
Here is something that can give you an idea:
using System;
using System.Data;
using System.Collections.Generic;
namespace SO17416111
{
class Class
{
public int Id { get; set; }
public string Name { get; set; }
}
// Note that definition of Class and Student only differ by name
// I'm assuming that Student can/will be expanded latter.
// Otherwise it's possible to use a single class definition
class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main()
{
DataTable table = GetData();
Dictionary<Class, List<Student>> d = new Dictionary<Class, List<Student>>();
foreach (DataRow dr in table.Rows)
{
// If it's possible to get null data from the DB the appropriate null checks
// should also be performed here
// Also depending on actual data types in your DB the code should be adjusted as appropriate
Class key = new Class {Id = (int) dr["ClassID"], Name = (string) dr["ClassName"]};
Student value = new Student { Id = (int)dr["StudentID"], Name = (string)dr["StudentName"] };
if (!d.ContainsKey(key))
{
d.Add(key, new List<Student>());
}
d[key].Add(value);
}
foreach (var s in d.Keys)
{
foreach (var l in d[s])
{
Console.Write(s.Id + "-" + s.Name + "-" + l.Id + "-" + l.Name + "\n");
}
}
}
// You don't need this just use your datatable whereever you obtain it from
private static DataTable GetData()
{
DataTable table = new DataTable();
table.Columns.Add("ClassID", typeof (int));
table.Columns.Add("ClassName", typeof (string));
table.Columns.Add("StudentID", typeof (int));
table.Columns.Add("StudentName", typeof (string));
table.Rows.Add(1, "A", 1000, "student666");
table.Rows.Add(2, "B", 1100, "student111");
table.Rows.Add(5, "C", 1500, "student777");
table.Rows.Add(1, "A", 1200, "student222");
table.Rows.Add(2, "B", 1080, "student999");
return table;
}
}
}
Note, that this can be compiled and tested as a console application - I substituted your Response.Write with Console.Write. I'm also generating a test DataTable, you should be able to use one that is already present in your application. As far as Class/Student classes go, you have several options here: you can have two separate classes as I show, you can use the same class or you can even use a Tuple class. I suggest you use two separate classes, as it improves readability and maintainability.
Note if you just need to output them, you don't need a dictionary or anything to that effect:
// Add null checks and type conversions as appropriate
foreach (DataRow dr in table.Rows)
{
Response.Write(dr["ClassID"] + "-" + dr["ClassName"] + "-" + dr["StudentID"] + "-" + dr["StudentName"] + "<br>");
}

How to Set default combobox

So I've been looking to set a default value for my combobox. I found a few things but none of them seem to work.
Actually, it works if I create a simple combobox and use comboBox1.SelectedIndex = comboBox1.Items.IndexOf("something") but once I dynamically generate the contents of the comboboxes, I can't get it to work anymore.
This is how I fill my combo box (located in the class's constructor);
string command = "SELECT category_id, name FROM CATEGORY ORDER BY name";
List<string[]> list = database.Select(command, false);
cbxCategory.Items.Clear();
foreach (string[] result in list)
{
cbxCategory.Items.Add(new ComboBoxItem(result[1], result[0]));
}
I can't seem to get it to work to set a default value, like if I place cbxCategory.SelectedIndex = cbxCategory.Items.IndexOf("New") below the above code, it won't work.
WinForms, by the way.
Thank you in advance.
cbxCategory.SelectedIndex should be set to an integer from 0 to Items.Count-1 like
cbxCategory.SelectedIndex = 2;
your
cbxCategory.SelectedIndex = cbxCategory.Items.IndexOf("New")
should return -1 as long as no ComboboxItem mutches the string ("New");
another solution though i don't like it much would be
foreach(object obj in cbxCategory.Items){
String[2] objArray = (String[])obj ;
if(objArray[1] == "New"){
cbxCategory.SelectedItem = obj;
break;
}
}
perhaps this also requires the following transformation to your code
foreach (string[] result in list)
{
cbxCategory.Items.Add(result);
}
I haven't tested the code and i am not sure about the casting to String[2] but something similar should work
It looks like you're searching the cbxCategory.Items collection for a string, but it contains items of type ComboBoxItem. Therefore the search will return -1.
You can use LINQ.
//string command = "SELECT category_id, name FROM CATEGORY ORDER BY name";
//List<string[]> list = database.Select(command, false);
// sample data...
List<string[]> list = new List<string[]> { new string[] { "aaa", "bbb" }, new string[] { "ccc", "ddd" } };
cbxCategory.Items.Clear();
foreach (string[] result in list)
{
cbxCategory.Items.Add(new ComboBoxItem(result[1], result[0]));
}
ComboBoxItem tmp = cbxCategory.Items.OfType<ComboBoxItem>().Where(x => x.ResultFirst == "bbb").FirstOrDefault();
if (tmp != null)
cbxCategory.SelectedIndex = cbxCategory.Items.IndexOf(tmp);
ComboBoxItem class:
class ComboBoxItem
{
public string ResultFirst { get; set; }
public string ResultSecond { get; set; }
public ComboBoxItem(string first, string second)
{
ResultFirst = first;
ResultSecond = second;
}
}
Here's my simple solution
var list = comboBox1.Items.Cast<string>().ToList();
cbxCategory.SelectedIndex = list.FindIndex(c => c.StartsWith("test"));
My solution:
int? defaultID = null;
foreach (DataRow dr in dataSource.Tables["DataTableName"].Rows)
{
if ((dr["Name"] != DBNull.Value) && ((string)dr["Name"] == "Default Name"))
{
defaultID = (int)dr["ID"];
}
}
if (defaultID != null) comboBox.SelectedValue = defaultID;

Categories

Resources