Preface
Not sure if it matters but I'll explain how my project is structured. It has three layers:
native C++: Purpose is to read multiple files and perform calculations. The outputs are doubles and vectors.
C++/CLI Wrapper: Communication layer between my native C++ program and the C# GUI. It also converts my native C++ vectors into Generic Lists.
C#: This layer is used to design the GUI and basically just to present the results in charts and tables.
My goal
My goal is to populate a DataGridView
with my parameters from class Evaluation
as column and the values of each parsed file as a row.
What I have done so far is:
Created the class Evaluation
with each parameter as property
public ref class Evaluation
{
/.../ constructor exists
System::Collections::Generic::List<double> ^GetTAFValues();
System::Collections::Generic::List<double> ^GetTAFTemps();
property double parameterA
{
double get() { return getParameterA(); }
}
property double parameterB
{
double get() { return getParameterB(); }
}
/.../
}
For each parsed file (using a foreach loop) I'd create a new Evaluation
object and add that object into a List called results
of type List<Evaluation>
After that I'd bind the List<Evaluation>
to the DataGridView by doing dataGridView1.DataSource = results;
This works pretty good so far but only for Parameter A and B. What I am missing is my parameter TAF.
TAF consists of a temperature and a value. For example:
List<double> TAFValues = new List<double> {1, 2, 3, 4};
List<double> TAFTemps = new List<double> {-30, -10, 25, 50,};
What I need is to add columns called TAF-30, TAF-10, TAF25, TAF50 with the values attached from TAFValue
. These four parameters can be used for all remaining files.
I can't add TAF as property though because I don't know how many temperature steps are being used during compile time and what the property name (read: which temperature) would be.
Questions/Ideas:
How do I add columns that include the temperature strings as HeaderText?
Can I somehow combine my List<Evaluation>
with my List<double>
and use it as a DataSource? I know you'd have to make a List but I'm not really able to declare any class variables.
Am I able to manually insert the TAF columns + values for each row/file AFTER the DataSource has been declared with dataGridView1.DataSource = results;
?
Are there any other data structures I could use as a Datasource instead of a List, that could possibly help me out?
I managed to solve my problem by using a Dictionary<string, object>
and DataTable
. I think in order to solve my problem using class properties I'd have to implement Reflections in order to define properties during runtime.
This route doesn't require any reflections. The temperature values for TAZ
is known once I run Calculate()
for the first time.
The parameters are applicable for all remaining files.
First step: Instead of storing my parameters as class properties and bind it to a DataGridView
, I decided to use a Dictionary<string, object>
to store my parameters instead.
Since Dictionary
stores a string
I was able to declare the dynamic column names by doing "TAZ" + temperature.ToString()
(temperature
is a vector<int>
).
// C++/CLI Wrapper file
using namespace System::Collections::Generic;
Dictionary<String^, Object^>^ EvaluationCLI::GetParameters()
{
Dictionary<String^, Object^>^ d = gcnew Dictionary<String^, Object^>();
List<int>^ temps = GetTAFTemps();
List<double>^ TAZ = GetTAZValues();
d->Add("Parameter A", GetParameterA());
d->Add("Parameter B", GetParameterB());
for (int i = 0; i < temps->Count; i++)
{
d->Add("TAZ_" + temps[i].ToString(), TAZ[i] );
}
return d;
}
Second Step: Instead of using properties from Evaluation
,
and add these to a List<Evaluation>
, I would use a Dictionary<string, object>
and populate a DataTable
as a DataSource
.
// C#
/.../
var dt = new System.Data.DataTable();
foreach (string path in listBox.Items) // file(paths) are stored in a ListBox
{
Evaluation eval = new Evaluation(path); // create Evaluation for that file
eval.Calculate(); // does calculations and stores results inside the native C++ vectors/doubles
Dictionary<string, object> dict = eval.GetParameters(); // retrieve the parameters from native C++ as a Dictionary
if (table.Columns.Count < 1) // only add columns when DataTable is empty
{
foreach (var c in dict.Keys)
table.Columns.Add(new DataColumn(c)); // add new column for each parameter string
}
DataRow row = table.NewRow(); // create new row for each file
int i = 0;
foreach (var value in dict.Values)
{
row[i] = value; // inserts values column by column
i++;
}
table.Rows.Add(row); // add the populated row to the DataTable
}
dataGridView1.DataSource = table; // bind DataTable as a DataSource
/.../
Results:
DataGridView
with runtime parameter names as columnsRow values match the parameter columns
|---------------------|------------------|------------------|------------------|
| Parameter A | Parameter B | TAZ_10 | TAZ_50 |
|---------------------|------------------|------------------|------------------|
| 12 | 26 | 96 | 89 |
|---------------------|------------------|------------------|------------------|
| 85 | 34 | 91 | 72 |
|---------------------|------------------|------------------|------------------|
| ... | ... | ... | ... |
|---------------------|------------------|------------------|------------------|