I want to filter a DataTable or DaatView that contains complex objects.
Let's say I have this object Person
public class Person{
public int Id{get; set;}
public int Age{get; set;}
public strng Name{get; set;}
public Address BillAddress{get; set;}
}
public class Address{
public string
Town{get; set}
public string Street{get; set}
public int Number{get; set}
}
Now i fill a DataView with a list of Person objects:
public static DataView ToObjectDataView<T>(this IList<T> list, int countOfColumns)
{
if (list == null || countOfColumns < 1)
{
return null;
}
int columns = countOfColumns;
DataTable dataTable = new DataTable();
for (int currentCol = 0; currentCol != columns; currentCol++)
{
DataColumn column = new DataColumn(currentCol.ToString(), typeof(T));
dataTable.Columns.Add(column);
}
DataRow row = null;
int currentColumn = 0;
for (int index = 0; index < list.Count; index++)
{
if (list[index] == null)
{
continue;
}
if (Equals(null, row))
row = dataTable.NewRow();
row[currentColumn] = list[index];
currentColumn++;
if (currentColumn == columns)
{
dataTable.Rows.Add(row);
row = null;
currentColumn = 0;
}
}
//Verarbeitung der letzten Zeile
if (!Equals(null, row))
{
dataTable.Rows.Add(row);
}
return new DataView(dataTable);
}
So that i get a DataView with 10 columns of Person objects, evrey column has the name of its index:
IList<Person> personList = new List<Person>();
// Fill the list...
DataView dataSource = personList.ToObjectDataSource(10);
Now I would like to filter this DataView based on child values with an expression, for example, get all persons that live in the 'Fakestreet'.
I tried "0.BillAddress.Street = 'Fakestreet'"(and or expression with the rest columns) but that doesn't work..
This is a partial solution, because I didn't find a direct way.
Use the DataTable AsEnumerable extension and filter with dynamic linq (System.Linq.Dynamic (also available for .Net 3.5))
// Filter directly the List
public static List<T> FilterByLinqExpression<T>(this IList<T> list, string linqFilterExpression)
{
var result = list.AsQueryable().Where(linqFilterExpression);
return result.ToList<T>();
}
So you can call it like this for all persons that live in a street that has 'Avenue' in it's name:
IList<Person> personList = new List<Person>();
// Fill the list...
var filteredValues = personList.FilterByLinqExpression("((BillAddress.Street).Contains(\"Avenue\"))");
DataView dataSource = filteredValues .ToObjectDataSource(10);
I use this to filter for example complex objects that are used to be displayed in a DevExpress ASPxGridView. By the way, they have an automatic converter from their filter expressions to different filter expression, in my case the 'CriteriaToWhereClauseHelper.GetDynamicLinqWhere()' that converts the given filter expression to a dynamic linq expression.
Related
I have a strange data model and I'm trying to generate dynamic columns in a datagrid and bind the items correctly.
I have a list of Row objects that I want to bind to a DataGrid and display using simple DataGridTextColumn.
<controls:DataGrid
Grid.Row="1"
x:Name="dataGrid"
ItemsSource="{x:Bind ViewModel.CurrentRows}"
My objective is to grab a list of columns from the first row and build my grid columns while setting up the bindings. I'm having trouble figuring out the correct way to bind the data at RowValue.value.
public TablePage()
{
InitializeComponent();
dataGrid.ItemsSource = ViewModel.CurrentRows;
foreach (Column column in ViewModel.CurrentRows.FirstOrDefault().Values.Select(x => x.key))
{
dataGrid.Columns.Add(new DataGridTextColumn()
{
Header = column.ColumnValidation.column_label,
Binding = new Binding() { Path = new PropertyPath("Values.value") }
});
}
}
And in my viewmodel I have:
public ObservableCollection<Row> CurrentRows
And a Row object looks like this:
public class Row: INotifyPropertyChanged
{
public List<RowValue> Values { get; set; } = new List<RowValue>();
public event PropertyChangedEventHandler PropertyChanged;
}
And finally a RowValue object looks like this:
public class RowValue: INotifyPropertyChanged
{
public Column key { get; set; }
public string value { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
Column looks like this:
public class Column
{
public string name;
public ColumnValidation ColumnValidation;
}
ColumnValidation looks like this:
public class ColumnValidation
{
public string column_label;
public DataTypeEnum data_type;
public int width;
public int decimal_places;
public int display_order;
public string calculation_formula;
public string edit_style;
public string default_value;
public decimal? minimum_value;
public decimal? maximum_value;
public bool is_column_nullable = false;
public bool inherit_values = false;
public bool display_column = false;
public bool is_editable = false;
public int column_style;
public string code_table_name;
public string code_display_name;
public string code_data_column_name;
}
My solution ended up being to build a DataTable using my list of column definitions and row data. It's a pretty dirty solution but it suits my purposes. I'm not doing any editing or saving from the grid, it is simply for displaying data. The editing occurs in a dialog elsewhere.
public TablePage()
{
InitializeComponent();
// get list of columns
List<Column> columns = ViewModel.CurrentTableDefinition.Columns.OrderBy(x => x.ColumnValidation.display_order).ToList();
// create DataTable
DataTable dataTable = new DataTable();
dataTable.Columns.Add(new DataColumn() { ColumnName = "RowObject" }); // add column to store original row object
// add columns to datagrid and datatable
for (int i = 0; i < columns.Count; i++)
{
// add column to datagrid using the correct header label. bind using index of array.
dataGrid.Columns.Add(new DataGridTextColumn()
{
Header = columns[i].ColumnValidation.column_label,
Tag = columns[i].name,
Binding = new Binding() { Path = new PropertyPath("[" + i.ToString() + "]") }
});
// add corresponding column to datatable
dataTable.Columns.Add(new DataColumn() { ColumnName = columns[i].name });
}
// iterate through rows of data
foreach (Row row in ViewModel.CurrentRows)
{
// create new datatable row
DataRow dataRow = dataTable.NewRow();
// set the original row object
dataRow["RowObject"] = row;
// add data from each column in the row to the datatable row
for (int i = 0; i < columns.Count; i++)
{
// add column value to row
try
{
dataRow[i] = row.Values.Where(x => x.key.name == columns[i].name).FirstOrDefault().value;
}
catch (Exception ex)
{
dataRow[i] = null; // insert null if the table has columns defined for which there is no data in the dataset
}
}
// add datable row to datatable
dataTable.Rows.Add(dataRow);
}
// convert datatable to collection of 'object'
ObservableCollection<object> collection = new ObservableCollection<object>();
foreach(DataRow row in dataTable.Rows)
collection.Add(row.ItemArray);
// bind to datagrid
dataGrid.ItemsSource = collection;
}
I'm extracting data from a MySQL database file and load three data items from there into a method called index that extracts Name, surname & Tel Number fields.....main goal is to load all the data in alphabetical order by surname Letter and if two Surnames are matching it should sort them by alphabetical name order.
using Data_Layer;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace Business_Logic_Layer
{
public class BusinessLogic
{
public List<PhoneIndex> phoneIndex = new List<PhoneIndex>();
public void createPhoneIndex()
{
string tableName = "customers";
DataConnection dc = new DataConnection();
DataSet customers = dc.selectData(tableName);
foreach (DataRow customer_row in customers.Tables[0].Rows)
{
string surname, name, telephone;
surname = name = telephone = "";
for (int i = 0; i < customers.Tables[0].Columns.Count; i++)
{
if (customers.Tables[0].Columns[i].ColumnName.StartsWith("contactFirstName"))
{
name = customer_row.ItemArray[i].ToString();
}
else if (customers.Tables[0].Columns[i].ColumnName.StartsWith("contactLastName"))
{
surname = customer_row.ItemArray[i].ToString();
}
else if (customers.Tables[0].Columns[i].ColumnName.StartsWith("phone"))
{
telephone = customer_row.ItemArray[i].ToString();
}
}
// Add the information to the main phoneIndex
bool added = false;
PhoneIndexEntry temp_index_entry = new PhoneIndexEntry() { name = name, surname = surname, telephone = telephone };
for (int i = 0; i < phoneIndex.Count; i++)
{
if (surname.StartsWith(phoneIndex[i].letter))
{
phoneIndex[i].entries.Add(temp_index_entry);
added = true;
}
}
if (!added)
{
PhoneIndex temp_index = new PhoneIndex() { letter = surname.Substring(0, 1), entries = new List<PhoneIndexEntry>() { temp_index_entry } };
phoneIndex.Add(temp_index);
added = true;
}
}
} // End of method createPhoneIndex()
public void sortPhoneIndex()
{
// Your code goes here...
string surname, name;
surname = name = "";
List<PhoneIndex> phoneIndex = new List<PhoneIndex>() { };
PhoneIndexEntry temp_index_entry = new PhoneIndexEntry() { name = name, surname = surname };
foreach (PhoneIndex index in phoneIndex)//letter, entries sort through letters
{
string[] sortedLetter = new string[1];
for (int i = 0; i < phoneIndex.Count - 1; i++)
{
for (int j = i + 1; j < phoneIndex.Count; j++)
{
if (phoneIndex[i].letter.CompareTo(phoneIndex[j].letter) > 0)
{
sortedLetter[0] = phoneIndex[i].letter;
phoneIndex[i].letter = phoneIndex[j].letter;
phoneIndex[j].letter = sortedLetter[0];
}
}
}
//if name==name compare surname for sorting
return;
}
}//end of foreach
} // End of method sortPhoneIndex()
} // End of Class BusinessLogic
public class PhoneIndex
{
public string letter { get; set; }
public List<PhoneIndexEntry> entries { get; set; }
} // End of Class PhoneIndex
public class PhoneIndexEntry
{
public string name { get; set; }
public string surname { get; set; }
public string telephone { get; set; }
} // End of Class PhoneIndexEntry
// End of Namespace Business_Logic_Layer
A =
anthony, adams, 0670510248
B =
bill, adams, 0724677987
bill, crucible, 0687944987
Assuming that you cannot change the way in which you retrieve your data from the database then this is a job where Linq could help a lot to simplify your code.
// Example data .....
DataTable dt = new DataTable();
dt.Columns.Add("name", typeof(string));
dt.Columns.Add("surname", typeof(string));
dt.Columns.Add("phone", typeof(string));
dt.Rows.Add("charles", "dickens", "11232");
dt.Rows.Add("mark", "twain", "453446");
dt.Rows.Add("howard", "lovecraft", "875564");
dt.Rows.Add("ernst", "hemingway", "1647567");
dt.Rows.Add("thomas", "mann", "56434");
dt.Rows.Add("isaac", "asimov", "9700");
dt.Rows.Add("aldous", "huxley", "2654");
List<PhoneIndex> phoneIndex = new List<PhoneIndex>();
// Let's enumerate the datatable grouping by first letter in the surname column
var names = dt.AsEnumerable().GroupBy(d => d.Field<string>("surname").Substring(0, 1))
// foreach group build a sublist with the info from the datarow
// transformed in a PhoneIndexEntry.
.Select(g => g.Select(p => new PhoneIndexEntry
{
name = p.Field<string>("name"),
surname = p.Field<string>("surname"),
telephone = p.Field<string>("phone")
}));
// Now we have an IEnumerable<IEnumerable> where the first enumerable is a list
// of all distinct first letter from the surname column, while the second
// is an enumerable of PhoneIndexEntry.
// We can loop over the first Enumerable ordering it
// by the first surname's letter and creating a PhoneIndex with all the PhoneIndexEntry for that specific letter
foreach (var entry in names.OrderBy(n => n.First().surname[0].ToString()))
phoneIndex.Add(new PhoneIndex { letter = entry.First().surname[0].ToString(), entries = entry.OrderBy(s => s.surname).ToList()});
Side note: It seems that you have a very bad do_it_all method (SelectData) where you pass a table name and you get back all the data from that table.
Surely this is not a good way to query a database and, unless you retrieve very few records, I suggest you to write specific methods for every data extraction or dedicate a bit of time to learn how to use an ORM library.
this the problem; I have a list and I would like to covert to a multidimencion array, this is the class:
public class Student{
public int Id {get; set;}
public string name {get; set;}
public string name {get; set;}
}
the list is, (I am using EF);
var listStudent = db.Students.ToList();
I would to converte this list to something like this
string[,] new Value= db.Students.ToList();
In the controller, you do something like:
Student student1 = new Student();
Student student2 = new Student();
string[,] temp = {
{ student1.FirstName, student1.LastName },
{ student2.FirstName, student2.LastName }
};
By the way, you should provide more details.
I resolved,
My idea was, how to convert a list of any object to a multidimensional array, something like this:
1.- First my class:
public class Myclass{
public string propr1 { get; set;}
public string propr2 { get; set;}
public string propr3 { get; set;}
}
2.- Make query using EF:
var listMyclass = db.Myclass.ToList();
3.- Use this method to convert this list to DataTable:
public DataTable ToDataTable<T>(List<T> items)
{
DataTable dataTable = new DataTable(typeof(T).Name);
//Get all the properties by using reflection
PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in Props)
{
//Setting column names as Property names
dataTable.Columns.Add(prop.Name);
}
foreach (T item in items)
{
var values = new object[Props.Length];
for (int i = 0; i < Props.Length; i++)
{
values[i] = Props[i].GetValue(item, null);
}
dataTable.Rows.Add(values);
}
return dataTable;
}
4.- get DataTable:
DataTable dt = ToDataTable<T>(listMyclass);
5.- And finally, pass all values from DataTable to multidimensional Array:
string[,] multidimensionalArray = new string[dt.Rows.Count, dt.Columns.Count];
for (int i = 0; i < dt.Rows.Count; i++)
{
for (int j = 0; j < dt.Columns.Count; j++)
{
multidimensionalArray[i, j] = dt.Rows[i][j].ToString();
}
}
Thank you all..!!
I need help adding rows to datagridwiev using list. I have a class
public class GridObject
{
public string Worker
{
get;
set;
}
[DisplayName("Item name")]
public string ItemName
{ get; set; }
public int Price { get; set; }
public int Quantity { get; set; }
}
And i am making win form app that sould display slod items in datagridview. By now i made it do it like this
private void btnCash_Click(object sender, EventArgs e)
{
dt = new System.Data.DataTable();
using (SqlConnection con = Helper.ConnectionToDatabase)
{
SqlDataAdapter da = new SqlDataAdapter(string.Format(Resources.ViewCommand, tbUser.Text, ItemList.SelectedValue), con);
da.Fill(dt);
//dataDaily.Rows.Clear();
foreach (DataRow item in dt.Rows)
{
int n = dataDaily.Rows.Add();
dataDaily.Rows[n].Cells[0].Value = item[0].ToString();
dataDaily.Rows[n].Cells[1].Value = item[1].ToString();
dataDaily.Rows[n].Cells[2].Value = item[2].ToString();
}
}
}
It works but I wanted to add a new column(quantity) that is not in database and that counts same itmes sold by one user(worker in my case). I tought doing it with list buy i don't know how to fill the list and than display it in datagridview
Win Forms add Datagridview Row to list and Add Row to datagridview from List
// ADD ROW TO LIST
List<DataGridViewRow> DeletedRowsList = new List<DataGridViewRow>(); // Row List
// Add the row for deletion to the list
private void AddRowToList()
{
if (Kunde_dataGridView.CurrentRow.Cells.Count == Kunde_dataGridView.ColumnCount) // If all cels are selected on the current row
{
// New Row
DataGridViewRow row = (DataGridViewRow)Kunde_dataGridView.SelectedRows[0].Clone(); // Clone the row and assign it to the row that will be added to the list
for (int i = 0; i < Kunde_dataGridView.SelectedCells.Count; i++)
{
row.Cells[i].Value = Kunde_dataGridView.SelectedCells[i].Value;
}
DeletedRowsList.Add(row); // Add the Cloned Row to the list
}
}
// Insert row from list to datagridview
// Undo Button
private void undo_button_Click(object sender, EventArgs e)
{
if(DeletedRowsList.Count > 0)
{
int lastindex = DeletedRowsList.Count - 1;
// DataSet of the datagridview can be found in the Form Load
advokathusetDataSet.Kunde.Rows.Add(DeletedRowsList[lastindex].Cells[0].Value, DeletedRowsList[lastindex].Cells[1].Value, DeletedRowsList[lastindex].Cells[2].Value, DeletedRowsList[lastindex].Cells[3].Value, DeletedRowsList[lastindex].Cells[4].Value, DeletedRowsList[lastindex].Cells[5].Value, DeletedRowsList[lastindex].Cells[6].Value);
SaveDatagridview(); // Save to DB
DeletedRowsList.RemoveAt(lastindex); // Remove Last index
}
}
// Only ADD Datagridview Row TO LIST
List<DataGridViewRow> DeletedRowsList = new List<DataGridViewRow>(); // Row List
DataGridViewRow row = (DataGridViewRow)Kunde_dataGridView.SelectedRows[0].Clone(); // Clone the row and assign it to the row that will be added to the list
for (int i = 0; i < Kunde_dataGridView.SelectedCells.Count; i++)
{
row.Cells[i].Value = Kunde_dataGridView.SelectedCells[i].Value;
}
DeletedRowsList.Add(row); // Add the Cloned Row to the list
// Only Adding ROW from List to Datagridview
// DataSet of the datagridview can be found in the Form Load
//Here you can do also "yourDataset.table[0].Rows.Add(value,value.....); advokathusetDataSet.Kunde.Rows.Add(DeletedRowsList[lastindex].Cells[0].Value, DeletedRowsList[lastindex].Cells[1].Value, DeletedRowsList[lastindex].Cells[2].Value, DeletedRowsList[lastindex].Cells[3].Value, DeletedRowsList[lastindex].Cells[4].Value, DeletedRowsList[lastindex].Cells[5].Value, DeletedRowsList[lastindex].Cells[6].Value);
you could use DataGridView.Columns.Add , this method accepts an object of DataGridViewColumn and add it to your grid view .... then you can use a loop to fill the value of this new column with whatever you want similar to what you are doing with rows
DataTable table = new DataTable();
table.Columns.Add("First Name", typeof(string));
table.Columns.Add("Last Name", typeof(string));
table.Columns.Add("Age", typeof(int));
if (File.Exists("file.txt"))
{
List<string[]> rows = File.ReadAllLines("file.txt").Select(x => x.Split(';')).ToList();
foreach (var row in rows)
{
string fName = row[0];
string lName = row[1];
int age = -1;
int.TryParse(row[2], out age);
table.Rows.Add(fName,lName,age);
}
}
dataGridView1.DataSource = table;
You could do it like this:
public class GridObject
{
public string Worker { get; set; }
public string ItemName { get; set; }
public int Price { get; set; }
public int Quantity { get; set; }
public override string ToString()
{
return ItemName;
}
}
and just bind it like other have said.
More info, try to read this
I'm getting values from another data table as input to list. Now i need to save those list values into another DataTable.
List:
List<DataRow> list = slectedFieldsTable.AsEnumerable().ToList();
foreach (DataRow dr in slectedFieldsTable.Rows)
{
list.Add(dr);
}
New Data table :
DataRow newRow = tempTable.NewRow();
newRow["Field Name"] = fieldLabel;
newRow["Field Type"] = fieldType;
for(int gg =0 ; gg<list.Count; gg++)
{
tempTable.Rows.Add(????);
}
I'm stuck here in adding up rows in to new data table.
public static DataTable ToDataTable<T>(List<T> items)
{
DataTable dataTable = new DataTable(typeof(T).Name);
//Get all the properties
PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in Props)
{
//Setting column names as Property names
dataTable.Columns.Add(prop.Name);
}
foreach (T item in items)
{
var values = new object[Props.Length];
for (int i = 0; i < Props.Length; i++)
{
//inserting property values to datatable rows
values[i] = Props[i].GetValue(item, null);
}
dataTable.Rows.Add(values);
}
//put a breakpoint here and check datatable
return dataTable;
}
Variable declare:
DataTable tempTable = new DataTable();
DataTable slectedFieldsTable = new DataTable();
DataRow newRow;
List<object> list = new List<object>();
Add Column in DataTable:
slectedFieldsTable = new DataTable();
slectedFieldsTable.Columns.Add("Field Name");
slectedFieldsTable.Columns.Add("Field Type");
Add Value in DataTable:
slectedFieldsTable.Rows.Add("1", "AAA");
slectedFieldsTable.Rows.Add("2", "BBB");
slectedFieldsTable.Rows.Add("3", "CCC");
Convert DataTable to List:
foreach (DataRow dr in slectedFieldsTable.Rows)
{
list.Add(dr);
}
Add Column in another DataTable:
tempTable.Columns.Add("Field Name", typeof(string));
tempTable.Columns.Add("Field Type", typeof(string));
Convert List to dataTable:
foreach(DataRow dr in list)
{
newRow = tempTable.NewRow();
newRow["Field Name"] = dr.ItemArray[0].ToString();
newRow["Field Type"] = dr.ItemArray[1].ToString();
tempTable.Rows.Add(newRow);
tempTable.AcceptChanges();
}
use CopyToDataTable() method. CopyToDataTable
IEnumerable<DataRow> query = TempselectedFieldsTable.AsEnumerable().ToList();
// Create a table from the query.
DataTable boundTable = query.CopyToDataTable<DataRow>();
The answer providing the ToDataTable is a very nice start but it is missing some key elements. Namely, it ignores that List item properties may:
...be marked ReadOnly
...use the DisplayName attribute
...have a DefaultValue which the DataColumn should know about
...be Nullable
...be marked BrowsableAttribute(false)
The following is an extension method to return a DataTable and either accounts for the above or provides the means for your code to apply them. It also uses an Interface to get the values from the class object rather than Reflection.
public static DataTable ToDataTable<T>(this IList<T> lst, bool includeAll = true)
{
DataTable dt = new DataTable();
DataColumn dc;
PropertyDescriptor pd;
bool Browsable;
PropertyDescriptorCollection propCol = TypeDescriptor.GetProperties(typeof(T));
for (int n = 0; n < propCol.Count; n++)
{
pd = propCol[n];
Type propT = pd.PropertyType;
dc = new DataColumn(pd.Name);
// if Nullable, get underlying type
// the first test may not be needed
if (propT.IsGenericType && Nullable.GetUnderlyingType(propT) != null )
{
propT = Nullable.GetUnderlyingType(propT);
dc.DataType = propT;
dc.AllowDBNull = true;
}
else
{
dc.DataType = propT;
dc.AllowDBNull = false;
}
// is it readonly?
if (pd.Attributes[typeof(ReadOnlyAttribute)] != null)
{
dc.ReadOnly = ((ReadOnlyAttribute)pd.
Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly;
}
// DefaultValue ...
if (pd.Attributes[typeof(DefaultValueAttribute)] != null)
{
dc.DefaultValue = ((DefaultValueAttribute)pd.
Attributes[typeof(DefaultValueAttribute)]).Value;
}
// caption / display name
dc.ExtendedProperties.Add("DisplayName", dc.Caption);
if (pd.Attributes[typeof(DisplayNameAttribute)] != null)
{
// these are usually present but blank
string theName = ((DisplayNameAttribute)pd.
Attributes[typeof(DisplayNameAttribute)]).DisplayName;
dc.Caption = string.IsNullOrEmpty(theName) ? dc.Caption : theName;
// DGV doesnt use Caption...save for later
dc.ExtendedProperties["DisplayName"] = dc.Caption;
}
Browsable = true;
dc.ExtendedProperties.Add("Browsable", Browsable);
var foo = pd.Attributes[typeof(BrowsableAttribute)];
if (pd.Attributes[typeof(BrowsableAttribute)] != null)
{
Browsable = ((BrowsableAttribute)pd.Attributes[typeof(BrowsableAttribute)]).Browsable;
// no such thing as a NonBrowsable DataColumn
dc.ExtendedProperties["Browsable"] = Browsable;
}
// ToDo: add support for custom attributes
if (includeAll || Browsable)
{
dt.Columns.Add(dc);
}
}
// the lst could be empty such as creating a typed table
if (lst.Count == 0) return dt;
if (lst[0] is IDataValuesProvider)
{
IDataValuesProvider dvp;
// copy the data - let the class do the work
foreach (T item in lst)
{
dvp = (IDataValuesProvider)item;
dt.Rows.Add(dvp.GetDataValues(includeAll).ToArray());
}
}
else
{
List<object> values;
foreach (T item in lst)
{
values = new List<object>();
// only Browsable columns added
for (int n = 0; n < dt.Columns.Count; n++)
{
values.Add(propCol[dt.Columns[n].ColumnName].GetValue(item));
}
dt.Rows.Add(values.ToArray());
}
}
return dt;
}
The method allows you to specify whether columns for non Browsable properties should be added to the DataTable. Rather than hiding the columns later, you can omit them entirely if you want.
An interface proves the means to get the data values from collection members in order (as an alternative to a reflection loop):
public interface IDataValuesProvider
{
IEnumerable<object> GetDataValues(bool includeAll);
}
... on the class:
public class StockItem : IDataValuesProvider
{
public int Id { get; set; }
public string ItemName {get; set;}
[Browsable(false), DisplayName("Ignore")]
public string propA {get; set;}
[ReadOnly(true)]
public string Zone { get; set; }
public string Size {get; set;}
[DisplayName("Nullable")]
public int? Foo { get; set; }
public int OnHand {get; set;}
public string ProdCode {get; set;}
[Browsable(false)]
public string propB { get; set; }
public DateTime ItemDate {get; set;}
// IDataValuesProvider implementation
public IEnumerable<object> GetDataValues(bool IncludeAll)
{
List<object> values = new List<object>();
values.AddRange(new object[] {Id, ItemName });
if (IncludeAll) values.Add(propA);
values.AddRange(new object[] { Zone, Size, Foo, OnHand, ProdCode });
if (IncludeAll) values.Add(propB);
values.Add(ItemDate);
return values;
}
}
Add the data values in the same order as they are listed in your class; be sure to update it when you add properties. The reflection version is still there so you can do it either way.
Finally, there are a few common Attributes which do not have a related DataColumn property. The method stores these for you as ExtendedProperties allowing you to easily apply them to the DGV:
var dtX = someData.ToDataTable();
dgvB.SuspendLayout();
dgvB.DataSource = dtX;
// process extended props
foreach (DataColumn dc in dtX.Columns)
{
// no need to test, the code adds them everytime
//if (dc.ExtendedProperties.ContainsKey("DisplayName"))
//{
dgvB.Columns[dc.ColumnName].HeaderText = dc.ExtendedProperties["DisplayName"].ToString();
//}
//if (dc.ExtendedProperties.ContainsKey("Browsable"))
//{
dgvB.Columns[dc.ColumnName].Visible = (bool)dc.ExtendedProperties["Browsable"];
//}
}
dgvB.ResumeLayout();
Results using a list of the class shown above:
Both OnHand and Foo show the DisplayName and both PropA and PropB are hidden. Most importantly, columns created for ReadOnly and Nullable properties act accordingly.
Try this:
foreach (DataRow dr in list)
{
tempTable.Rows.Add(dr);
}