In theory how would I do this.
short winded: store data like a datatable using custom collections, having variable amount of fields and columns...so long as the rows are consistent.
Long winded:
2 or 3 classes: field, row, optionally: table
Normally I would do something like List<Person> myList = new List<Person>;
Then that list could be bound to a datagridview and the columns would be based off the properties of the Person class.
Code to look at:
List<row> table = new List<row>;
List<field> row0 = new List<field>;
row0.Add(new field(col1,"value1"));
row0.Add(new field(col2,"value2"));
row0.add(new field(col3,"value3"));
table.Add(row0);
dataGridView1.DataSource = table;
theoretical Output:
| |col 1 | col 2| col 3|
___________________________
|row0|value1|value2|value3|
public class cField
{
public string Name { get; set; }
public string Content { get; set; }
public cField()
{
}
public cField(string name, string content)
{
Name = name;
Content = content;
}
}
public class cRow:BindingList<cField>
{
public cRow()
{
}
}
public class tables:BindingList<cRow>
{
public tables()
{
fillTestData();
}
private void fillTestData()
{
for (Int32 i = 0; i < 10; i++)
{
cRow tRow = new cRow();
for (Int32 x=0; x < 3; x++)
{
cField f1 = new cField("ColumnName" + x.ToString(), "content" + x.ToString());
tRow.Add(f1);
}
base.Items.Add(tRow);
}
}
}
//example class which shows the output of what I'd like.
public class eField
{
public string ColumnName0 { get; set; }
public string ColumnName1 { get; set; }
public string ColumnName2 { get; set; }
public eField(string colName0, string colName1, string colName2)
{
ColumnName0 = colName0;
ColumnName1 = colName1;
ColumnName2 = colName2;
}
}
public class eTable : BindingList<eField>
{
public eTable()
{
base.Add (new eField ("content","content", "content"));
base.Add(new eField("content", "content", "content"));
base.Add(new eField("content", "content", "content"));
}
}
Now Here is code for the form.
public partial class Form1 : Form
{
tables t;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
t = new tables ();
dataGridView1.DataSource = t;
dataGridView2.DataSource = t[0];
eTable table3 = new eTable ();
dataGridView3.DataSource = table3;
}
}
If you make that code into a project...you will see the first binding....pulls some built in stuff from the bindinglist into grid1. Grid2 lists my fields vertically when I want them horizontal.
Grid 3 shows exactly how I want my output to be.....yet I can't achieve it with the collection structure I have going to mimic a dataTable....(provided in code)
Disclaimer:
I am short on keywords I would need to research this problem. I didn't find much. The closest thing I found was related to linq and pivots. But non of their outputs seemed to be as I described.
I use custom collections all over the place, so I would like to keep my code very similar instead of using a datatable. This is the first time I have needed my collections to behave in this manner.
It sounds like you are looking for a collection of objects to use in memory once you have loaded the data from a database. You can do calculations and the like on the built-in System.Data objects, but it is cumbersome, and it does not perform well with a large amount of data.
We use System.Data objects heavily to present data. We try to do calculations in the database later and present the results as a DataSet, so the client doesn't have to do any data manipulation.
A few of our modules need more sophisticated data processing. In one case, we used an array of objects that represented a large amount of data to be massaged on the fly. The columns were fixed, so they were easy to implement as properties on each object. When the app presented this data, it generated a small summary DataSet to be displayed in a grid.
We have another module in which there are fields that can have values, or they can also have calculations based on other fields. For this model, we opted to use objects that have dependencies on other objects that made a sort of web of calculations. Change one value, and the ValueChanged event notifies any dependent fields that they need to be calculated, which changes those values, etc. (This is a gross simplification.)
If I had to present a variable number of columns, I'd seriously consider sticking with a System.Data.DataSet. If that really doesn't work for you, you might consider a hashtable that maps a column name to a collection of row values for that column. I believe that is how the System.Data.DataTable is implemented; it stores values by column, not by row. Then a row object would know its row index and how to grab the values out of the column collections.
Related
When I use Linq-to-Entities to bind the data in my database to a DataGridView in a C# application I am making the DataGridView become Read-Only and can't be edited.
Is it possible to edit the data in the DataGridView and the changes saved in the database ?
This is the code where I bind the data to the DGV after applying some filtering to the first query:
private void ViewResults(IQueryable<Hero> viewResult)
{
dgdResult.DataSource = (from r in viewResult
select new
{
Name = r.Name,
Rarity = r.Rarity,
Speed = r.Speed,
Attack = r.Attack,
Target = r.Target
}).ToList();
}
That's interesting, I didn't realize it did that with anonymous types. Pretty easy to reproduce though.
The easiest thing is probably to create a private class with just the fields you need, inside the Form since you won't be using it anywhere else.
public partial class Form1 : Form
{
...
...
private void ViewResults(IQueryable<Hero> viewResult)
{
dgdResult.DataSource = (from r in viewResult
select new LittleHero
{
Name = r.Name,
Rarity = r.Rarity,
Speed = r.Speed,
Attack = r.Attack,
Target = r.Target
}).ToList();
}
private class LittleHero
{
public string Name { get; set; }
public string Rarity { get; set; }
public string Speed { get; set; }
public string Attack { get; set; }
public string Target { get; set; }
}
}
As for saving it, that's kinda broad and depends on what technologies you're using.
You can easily get the collection back from the DataGridView by casting the DataSource though.
var heroes = (List<LittleHero>)dgdResult.DataSource;
According to C# Documentation:
Anonymous types provide a convenient way to encapsulate a set of read-only properties into a single object without having to explicitly define a type first.
I need to implement this approach - two kinds of data sent as one set of parameters in a single Type - so that one type will hold those two parameters. That way I will be able to pass that Type to be processed by some method.
the first data item is:
Columns to be displayed , named: displayed
the second data item:
A copy (or only a portion) of that Columns displayed, as it has the same source, only these columns will not be displayed... in other words, Columns to omit, so I've named it: omitted
both are of a type Columns that I named - SelectedColumns
public class SelectedcColoumns
{
public enum renederingMode
{
Displayed,
omitted
}
public class omitted
{
}
public class displayed
{
}
}
As the request for that SetOfColumns to be displayed is done by choosing table-name. So the Column class as data to be displayed varies based on the user choice the available source For SelectedColumns to choose from, is as shown below:
public class tableNames
{
public static readonly string tblCustomers = "tblCustomers";
public static readonly string tblProducts = "tblProducts";
}
public class TblColumns
{
public class tblCustomers
{
public const string custID = "custID",
Name = "Name",
Phone = "Phone";
Email = "Email";
}
public class tblProducts
{
public const string PrudctID = "PrudctID ",
PrudctName = "PrudctID",
PrudctCategory = "PrudctCategory";
}
...etc'
}
When the user selects a set of tables Columns ... from any table user could, in this example.. choose either Customers or Products columns (e.g. SelectedColumns - is tblCustomers Columns), I then need to have another list, of those that the user selected to omit (not to display) from all of available table Columns.
Say the user chose to have Table Customers as a table. He chose to omit tblCustomers.custID + tblCustomer.Email because he only needs the name and phone to be displayed.
The problem I've encountered is while having these parameters in my reach (table name + columns to omit), How could I send it to process (passing it as One Parameter)? So that is why I've created a dedicated class, to hold this Type as sent parameter: all columns + omitted Columns in one piece.
This is where I am currently stuck; I need to know how to use it to build / construct the parameter out of user selection.
public class SelectedColoumns
{
public enum renederingMode
{
Displayed,
omitted
}
public class omitted
{
List<string> omitCols_ListStr = new List<string>();
}
public class displayed
{
List<string> dispCols_ListStr = new List<string>();
}
}
In this part, I retrieve list of Columns through reflection as the supplier of data, via the following method:
Get any Nested Class-Fields, As List<string>, By a nested class-name and it's parent - Type.
public static List<string> anyNestedClassFiledsAsListByType<ClassToReturnOneOfitsNested_Fields>(string NetedClassName)
{
var RetNestedClassFildsListValues = typeof(ClassToReturnOneOFitsNested).GetNestedTypes()
.First(t => String.Compare(t.Name, NetedClassName, true) == 0).GetFields(BindingFlags.Public | BindingFlags.Static)
.Where(f => f.FieldType == typeof(string)).Select(f => (string)f.GetValue(null)).ToList();
return RetNestedClassFildsListValues;
}
so to produce this I could use the method above Like that
var TableColumns_ALL =
anyNestedClassFldsAsListByType<TblColumns>(tableNames.tblCustomers);
My question is related to the class that needs to send TableColumns_ALL + the selected Columns to omit to be then processed by renderSelectedTable() below.
So it's even more basic than the complexity of reflection, but still some how i do not know the popper way to construct, the SelectedColumns class, so it will accommodate and format the structure of this new data type that will be sent as a parameter the method is something like this.
public void renderSelectedTable(SelectedColoumns CurrentUserSelectedCols)
{
StringBuilder NwTRLoopSB = new StringBuilder();
string curRowStyle= string.Empty,
nwLine = Environment.NewLine + "\t\t\t",
BaseTemplateTD = string.Empty;
NwTRLoopSB.Append(
string.Format(
"<table id='tbl_Settings' cellspacing='0' border='1'><tr id='TR_headers'{0}>{1}",
curRowStyle,
nwLine
)._Dhtml_DoubleQoutes()
);
foreach (var Item in SelectedListStr.Select((Val, counter) => new { Value = Val, Index = counter }))
{
curRowStyle = Lsts.DynamicStyle_Generator(Item.Index);
if(Lsts.ExcludeColumns(Item.Value, OmittedCols))
{
BaseTemplateTD = string.Format("<td>{0}</td>{1}", Item.Value, nwLine)._Dhtml_DoubleQoutes();
NwTRLoopSB.Append(BaseTemplateTD);
}
}///ENd TR cells generator Section
NwTRLoopSB.Append("</tr></table>");
return NwTRLoopSB.ToString();
}
I would approach it this way:
public class Column{
public string Name { get; set; }
public bool Visible { get; set; }
}
public class Grid{
public List<Column> Columns { get; set; }
}
So the I could easily define my full table with either visible or ommited columns.
In the OP's example:
public class SelectedColumns
{
//instead of the enum you would have boolean in the column type "Visible" (whether is shown or not)
public enum renederingMode
{
Displayed,
omitted
}
// instead of both these you would have a List o Column types that have a name AND a boolean, so you have your List<string> and a boolean to indicate whether it is visible or ommited. Well at least that's how I understood it.
public class ommited
{
}
public class displayed
{
}
}
so.. first.. a design note: Given a list of columns.. you will either display a column, or not display a column.. there are no other options where visibility is concerned. So you really only need to pass in a single list of EITHER the columns to display, OR the columns to omit - but NOT both.
If you choose to make that modification then you can simply pass in a single List<string>
If you choose to keep your current design then you will need a class with two properties:
public class SelectedColumns {
public List<string> displayed { get; set; }
public List<string> omitted { get; set; }
}
I have looked at a lot of places and I'm struggling to do this supposedly simple thing. I have a Windows form on which I've to show a simple DataGridView in this form:
| (CheckBoxColumn) | FilePath | FileState |
Now, there are a couple of problems. The data I need to bind to them is in a List of objects like this:
class FileObject
{
string filePath;
string fileState;
}
//Now here's the list of these objects which I populate somehow.
List<FileObject> listFiles;
Is there any efficient way to bind this directly to the DataGridView
so that different members of Object in the list are bound to
different columns, and for each there's checkbox?
I saw the examples of adding checkbox column to a datagrid, but none of them showed how
to add it to the 'header' row as well, so that it can behave as a 'check all'/'uncheck all' box.
Any help in how to achieve this would be great! I do write simple applications in C# but never had to work with datagrids etc. :(
Thanks!
The DataGridView control has a feature to automatically generate columns that can be set by the AutoGenerateColumns property. This property defaults to true - that is columns are by default auto generated.
However, columns are only automatically generated for public properties of the object you bind to the grid - fields do not show up.
Auto generation also works for check box columns when there is a public boolean property on the bound object.
So the simplest way to achieve your first two requirements is to change your FileObject class to this:
public class FileObject
{
public string FilePath { get; set; }
public string FileState { get; set; }
public bool Selected { get; set; }
}
If you cannot modify that class then next best would be the create a wrapper object that holds a file object:
public class FileObjectWrapper
{
private FileObject fileObject_;
FileObjectWrapper()
{
fileObject_ = new FileObject();
}
FileObjectWrapper(FileObject fo)
{
fileObject_ = fo;
}
public string FilePath
{
get { return fileObject_.filePath; }
set { fileObject_.filePath = value; }
}
public string FileState
{
get { return fileObject_.fileState; }
set { fileObject_.fileState= value; }
}
public bool Selected { get; set; }
}
You can then create your list to bind to (a BindingList is usually best) doing something like:
var fowList = new BindingList<FileObjectWrapper>();
foreach (FileObject fo in // here you have your list of file objects! )
{
fowList.Add(new FileObjectWrapper(fo));
}
dataGridView1.DataSource = fowList;
There are many ways to do the above but that is a general idea.
You can also add an unbound DataGridViewCheckBoxColumn to the grid, though I find it easier to have in the the bound list. Here is how if you do need to:
DataGridViewCheckBoxColumn c = new DataGridViewCheckBoxColumn();
c.Name = "Selected";
dataGridView1.Columns.Add(c);
Finally, for having a "SelectedAll" option in the header you will need to use custom code.
The article on CodeProject that Umesh linked to (CheckBox Header Column for DataGridView) looks quite easy to implement - they create a custom DataGridViewHeaderCell overriding the Paint and OnMouseClick methods.
Please refer the the below example, showing exactly what you are looking for
http://www.codeproject.com/Articles/20165/CheckBox-Header-Column-For-DataGridView
I have a database to which i have to connect through odbc.
The data fetch takes app. 2 minutes. and the resulting DataTable has 350000 records.
I am trying to transform the data table into this object graph. The resultset has no primary key, the primary key is specified through the view from which i fetch data.
public class PriceCurve
{
public PriceCurve(DataTable dt)
{
this.Id = int.Parse(dt.AsEnumerable().First()["ID"].ToString());
this.Prices = new List<Price>();
GetPrices(dt);
}
public int Id { get; private set; }
public IList<Price> Prices { get; set; }
private void GetPrices(DataTable dt)
{
foreach (DataColumn column in dt.Columns)
{
switch (this.GetPriceProviderType(column)) // parses ColumnName to Enum
{
case Price.PriceProvider.A:
{
this.Prices.Add(new Price(Price.PriceProvider.A, dt.AsEnumerable()));
}
break;
case Price.PriceProvider.B:
{
this.Prices.Add(new Price(Price.PriceProvider.B, dt.AsEnumerable()));
}
break;
}
}
public class Price
{
public enum PriceProvider
{
A, B
}
public Price(PriceProvider type, IEnumerable<DataRow> dt)
{
this.Type = type;
this.TradingDates = new List<TradingDate>();
this.GetTradingDates(type, dt);
}
public IList<TradingDate> TradingDates { get; set; }
public PriceProvider Type { get; set; }
private void GetTradingDates(PriceProvider type, IEnumerable<DataRow> dt)
{
var data = dt.Select(column => column["TRADING_DATE"]).Distinct();
foreach (var date in data)
{
this.TradingDates.Add(new TradingDate(date.ToString(), type, dt));
}
}
public class TradingDate
{
public TradingDate(string id, PriceProvider type, IEnumerable<DataRow> dt)
{
this.Id = id;
this.DeliveryPeriodValues = new Dictionary<int, double?>();
this.GetDeliveryPeriodValues(type, dt);
}
public string Id { get; set; }
public IDictionary<int, double?> DeliveryPeriodValues { get; set; }
private void GetDeliveryPeriodValues(PriceProvider type, IEnumerable<DataRow> dt)
{
foreach (var row in dt.Where(column => column["TRADING_DATE"].ToString() == this.Name))
{
try
{
this.DeliveryPeriodValues.Add(
int.Parse(row["DELIVERY_PERIOD"].ToString()),
double.Parse(row[Enum.GetName(typeof(Price.PriceProvider), type)].ToString()));
}
catch (FormatException e)
{
this.DeliveryPeriodValues.Add(
int.Parse(row["DELIVERY_PERIOD"].ToString()),
null);
}
}
}
}
}
i create one object, which contains a list with two objects. Each of these two objects contains a list with 1000 objects. Each of these 1000 objects contains a dictionary with 350 pairs.
It either crashes visual studio 2010 during debug, fails because of OutOfMemory or takes minutes (unacceptable) to execute.
What is the best approach to this problem. i am new to c# and do not know how to optimize the looping through this huge data or my object graph. Any help is appreciated.
It either crashes visual studio 2010 during debug, fails because of OutOfMemory or takes minutes
(unacceptable) to execute.
YOu made me laugh. Really.
350.000 nodes is challenging on a 32 bit machine with .NET. Add some overhead and you are dead. Use objects, not adata table which is VERY memory destroying.
takes minutes is pretty much your decision / programming. Use a list of objects, not a data table. Use a profiler. DOnt make beginner mistakesl ike:
var data = dt.Select(column => column["TRADING_DATE"]).Distinct();
No need for that, deal with doubles later inthe code. Distinct is expensive. Profile it.
foreach (var row in dt.Where(column => column["TRADING_DATE"].ToString() == this.Name))
That is 350.000 row lookups by name to get the index of the column, compared by a lot of tostring.
Get a profiler and find out where you exactly spend your time. Get please rid of the table and use objects - DataTable is a memory hog and SLOW compared to a list of objects. And yes, it will take minutes. Main reasons:
Your programming. Not a shame. Just learn, Go objets / structs NOW.
ODBC. Takes time to just load the data, especially as you dont process swhile loading (DataReader) but wait for allto ahve loaded, and ODBC is NOT fast. 350.000 rows, good network, direct SQL Server is maybe 30 seconds - same machine less.
I'm not sure where to look for this one... I've got a viewmodel that has an underlying DataRow providing part of the model. I want to display this information as a single record, in a vertical layout. I planned to use the DataGrid because I want the user to be able to add/delete/rename rows right across the DataTable despite only looking at one record. I'm not quite sure how to achieve this though. Example of what I'm expecting is below:
Source Data Table
ID, Name, Value
1, One, 1
2, Two, 2
Expected in my UI would be a table looking like the following
ID | 1
Name | One
Value | 1
You can expose the DataRow as a list of fields :
public class DataRowField
{
public int Index { get; set; }
public string Name { get; set; }
public object Value { get; set; }
}
public IEnumerable<DataRowField> Fields
{
get
{
return _dataRow.Table.Columns.Cast<DataColumn>()
.Select((column, i) => new DataRowField
{
Index = i,
Name = column.ColumnName,
Value = _dataRow[column]
});
}
}
Then you just need to bind your DataGrid to the Fields property