I've tried to compute a column in my datagrid, i will be showing these in my code as per below. I Keep getting these error.
I've go through these links
1. How To Convert The DataTable Column type?
2. Error while taking SUM() of column in datatable
3. Invalid usage of aggregate function Sum() and Type: String
//This is the column i add into my datagrid
MITTRA.Columns.Add("Min_Tol");
MITTRA.Columns.Add("Max_Tol");
MITTRA.Columns.Add("Min_Weight");
MITTRA.Columns.Add("Max_Weight");
// The following codes is working finely
// if you notice MTTRQT column, it's a data queried from database
for (int i = 0; i <= MITTRA.Rows.Count - 1; i++)
{
string item = MITTRA.Rows[i]["MTITNO"].ToString();
Tolerancechecking = database_select4.LoadUser_Tolerance(item);
MITTRA.Rows[i]["Min_Tol"] = Tolerancechecking.Rows[0]["Min_Tol"].ToString();
MITTRA.Rows[i]["Max_Tol"] = Tolerancechecking.Rows[0]["Max_Tol"].ToString();
MITTRA.Rows[i]["Min_Weight"] = Convert.ToDecimal(MITTRA.Rows[i]["MTTRQT"]) - ((Convert.ToDecimal(MITTRA.Rows[i]["MTTRQT"]) * Convert.ToDecimal(MITTRA.Rows[i]["Min_Tol"]) / 10));
MITTRA.Rows[i]["Max_Weight"] = Convert.ToDecimal(MITTRA.Rows[i]["MTTRQT"]) + ((Convert.ToDecimal(MITTRA.Rows[i]["MTTRQT"]) * Convert.ToDecimal(MITTRA.Rows[i]["Max_Tol"]) / 10));
dataGrid2.Columns.Clear();
dataGrid2.ItemsSource = null;
dataGrid2.ItemsSource = Tolerancechecking.DefaultView;
}
//Working Sum computation
Decimal Sum = Convert.ToDecimal(MITTRA.Compute("SUM(MTTRQT)", string.Empty));
MaxTol.Text = Sum.ToString(); /*** This is working as i got my value on the text box ****/
Errors when trying sum computation for different column
Initial try
1. Decimal Sum = Convert.ToDecimal(MITTRA.Compute("SUM(Min_Weight)", string.Empty));
Errors occurred
Invalid usage of aggregate function Sum() and Type: String.
Second Attempts
2. Decimal Sum = Convert.ToDecimal(MITTRA.Compute("SUM(Convert(Min_Weight,'System.Decimal'))", string.Empty));
3. Decimal Sum = Convert.ToDecimal(MITTRA.Compute("SUM(Convert(Min_Weight,'System.Decimal'))", ""));
Errors occurred
Syntax error in aggregate argument: Expecting a single column argument with possible 'Child' qualifier.
How am i going to get the compute sum function to work?
Thank you #NoChance for the solutions. Hope this post can help others too.
/***** This is the part where i declare my datacolumn data type ****/
DataColumn Min_Weight = new DataColumn("Min_Weight");
Min_Weight.DataType = System.Type.GetType("System.Decimal");
MITTRA.Columns.Add(Min_Weight);
/**** This Works finally *****/
Decimal Sum = Convert.ToDecimal(MITTRA.Compute("SUM(Min_Weight)", string.Empty));
MaxTol.Text = Sum.ToString();
Related
I'm trying to build a module where customer can create custom formulas and calculate them on the data he has in SQL database.
The customer has access to basic arithmetic operators (+, -, *, /) and aggregate functions (MIN, MAX, AVG, SUM). In addition he is given a set of columns he can perform those operations on, for the sake of the example: (UnitsProduced, ProductionTime, DownTime etc.). With these tools he can construct formulas such as: SUM(UnitsProduced)/SUM(ProductionTime).
After checking that the formula is mathematically correct and contains only valid columns I fetch the data using Stored Procedure from SQL Server into DataTable in C# and then use DataTable.Compute() method on that formula.
The problem is when the aggregate function yields 0. I tried padding the provided formula with IIF condition so that SUM(UnitsProduced)/SUM(ProductionTime) becomes SUM(UnitsProduced)/IIF(SUM(ProductionTime)<>0,SUM(ProductionTime),NULL) and i would expect to get null as the result but it gives me the error:
Cannot evaluate. Expression 'System.Data.FunctionNode' is not an aggregate
After doing some research I found that I cannot use aggregate function in a conditional statement. I haven't tried LINQ yet and I don't know if it's going to work.
How can I solve this problem?
After some research, me and my colleagues came to an interesting solution.
Basically you need to find all the divisors of the given formula and create a filtering string that will filter out all the data where the divisor is 0. This code also handles aggregation on multiple columns because Comute can't do that.
The code that takes care of zero divisions and aggregation:
DataTable dt = db.GetDataFromStoredProcedure("GetCustomFormulasData", param);
//Avoid zero division
List<string> divisors = GetFormulaDivisors(formula);
string formulaFilter = "";
foreach (string divisor in divisors)
formulaFilter += $" and {divisor}<>0 ";
if(formulaFilter != "")
formulaFilter = formulaFilter.Remove(0, 5); //remove the 1st " and "
//Compute cant handle multiple columns in one aggregate, so we handle it here
List<string> aggregations = GetFormulaAggregations(formula);
for (int i = 0; i < aggregations.Count; i++)
{
string expr = aggregations[i].Substring(4, aggregations[i].Length - 5);//remove the agg func and last paren
DataColumn dc = new DataColumn
{
DataType = typeof(float),
ColumnName = $"Column{i}",
Expression = expr
};
dt.Columns.Add(dc);
Regex rgx = new Regex(Regex.Escape(expr));
string temp = rgx.Replace(aggregations[i], dc.ColumnName);
rgx = new Regex(Regex.Escape(aggregations[i]));
formula = rgx.Replace(formula, temp);
if (formulaFilter != "")
formulaFilter = rgx.Replace(formulaFilter, temp);
}
return float.Parse(dt.Compute(formula, formulaFilter).ToString());
Where GetDataFromStoredProcedure, GetFormulaDivisors, GetFormulaAggregations are inner methods that do as their name suggests.
So for the example of formula SUM(GoodUnitsProduced+RejectedUnits)/SUM(ProductionTime), the DataTable dt will get the Columns: (GoodUnitsProduced, RejectedUnits, ProductionTime) from stored procedure, List divisors will have SUM(ProductionTime) and List aggregations will have: SUM(GoodUnitsProduced+RejectedUnits) and SUM(ProductionTime).
Right before calling the Compute method the DataTable will have two additional columns, one for each aggregate in the formula and filter will have the divisors in it. So formula = SUM(Column0)/SUM(Column1) and formulaFilter = SUM(Column1)<>0 and the problem of zero division is solved.
I want to update the value of cells in a datagridview using c#, the value gets updated but during the code execution it gives this error message.
Index was out of range, must be non negative and less than the size of the collection.parameter name: index
I have tried the column names as well as column index and also verified the row index, it actually updates the values but at each assignment statement it give the above error message and moves to next line. I am using this code,
foreach (DataGridViewRow row in SelectedItems.Rows)
{
if (Convert.ToInt32(row.Cells["SKU"].Value) == orderItem.ItemId)
{
decimal currentQuantity = Convert.ToDecimal(row.Cells["Quantity"].Value);
orderItem.Quantity = orderItem.Quantity + currentQuantity;
orderItem.Amount = orderItem.Quantity * orderItem.UnitPrice;
orderItem.Tax = orderItem.Tax * orderItem.Quantity;
orderItem.Discount = orderItem.Discount * orderItem.Quantity;
row.Cells["Quantity"].Value = orderItem.Quantity;
row.Cells["Amount"].Value = orderItem.Amount;
row.Cells["Tax"].Value = orderItem.Tax;
row.Cells["Discount"].Value = orderItem.Discount;
counter = 1;
CalculateTotal();
}
}
I am trying to use Datatable Select but I am getting incorrect data
double marks = 5;
DataRow[] result = dsGrades.Tables[0].Select("Convert(MarksFrom, 'System.Decimal') >=" + marks + " And " + marks + "<= Convert(MarksTo, 'System.Decimal') ");
dsGrades contains below data,
when 'marks' contain '5.0', I am expecting row where MarksFrom = 5.0 and MarksTo = 5.9, as 5.0 falls in this range, but here it is returning 5 rows.
Whats wrong with datatable select? Any help is appreciated.
If would make sense to change your DataColumn types to double, however even with decimal you don't need a conversion inside the expression.
Note in your provided example, your constraint appears to be backwards. You're specifying that you want MarksFrom greater or equal to the passed in amount, which won't return a single row in the range you want.
This should return a single row for any mark passed in:
double marks = 5.0;
DataRow[] result = dsGrades.Tables[0].Select($"{marks} >= MarksFrom AND {marks} <= MarksTo");
Also since you're always only expecting a single match, you could change this to:
DataRow match = table.Select($"{marks} >= MarksFrom AND {marks} <= MarksTo").SingleOrDefault();
SingleOrDefault will throw an InvalidOperationException if more than one result is returned, which may be the desired outcome in this case.
You can do it like this:
double marks = 5.0;
decimal newMarks = Convert.ToDecimal(marks);
var result =
dsGrades.Tables[0]
.AsEnumerable()
.Where( dr => dr.Field<decimal>( "MarksFrom" ) >= newMarks
&& dr.Field<decimal>( "MarksTo" ) < newMarks + 1);
This could be the solution:
var result = dsGrades.Tables[0].Select("Convert(MarksFrom, 'System.Decimal') >=" + newMarks + " And Convert(MarksTo, 'System.Decimal') < " newMarks + 1);
From my comment on question explaining problem:
Getting all the rows where MarksFrom is above 5 will return the first 5 visible rows in table, checking the second condition for these 5 rows and 5.0 is less than or equal to MarksTo in each of the rows so this would evaluate true for these rows. Therefore grabbing 5 rows
Needs to do casting from datatable as below:
DataRow[] drGrdPercntl = dt_GrdPercntil.Select($"{SubjVal} >= Convert(MarksFrom, 'System.Decimal') AND {SubjVal} <= Convert(MarksTo, 'System.Decimal')");
I have a datagrid where a user will input heights in inches then feet and I want to capture the tallest people (based off feet only). I have tried the below syntax, but I get a compile error of:
Non invocable member 'DataGridViewRow.Cells can not be used like a method
This is my syntax - what would be the appropriate way to get the max value of column 1 in my datagrid?
double MaxVal = 0;
foreach (DataGridViewRow row in HeightGrid.Rows)
{
if (row.Cells(1).Value > MaxVal)
{
MaxVal = row.Cells(1).Value;
}
}
To get the max:
MaxVal = dataGridView.Rows.Cast<DataGridViewRow>()
.Max(r => Convert.ToDouble(r.Cells[1].Value));
where Cells[1] denotes the second column.
To your second query:
var biggestRow = dataGridView.Rows.Cast<DataGridViewRow>()
.Aggregate((r1,r2) => Convert.ToDouble(r1.Cells[1].Value) > Convert.ToDouble(r2.Cells[1].Value) ? r1 : r2);
This should give you the row with the biggest value in your second column. From there, you can extract the first and second cells.
It means that Cells is a property, not a method, and as such should be accessed as DataGridViewRow.Cells, not DataGridViewRow.Cells().
I am trying to add horizontally across rows and vertically down a column in my DataGridView in C#, but I get this error message each time I try to enter data:
"An unhandled exception of type 'System.InvalidCastException'
occured in mscorlib.dll. Additional information: object cannot be
cast from DBNull to other types".
These are my codes:
private void tbl_valuesDataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
//totalling horizontally across rows
int val1 = Convert.ToInt32(tbl_valuesDataGridView.Rows[e.RowIndex].Cells[1].Value);
int val2 = Convert.ToInt32(tbl_valuesDataGridView.Rows[e.RowIndex].Cells[2].Value);
tbl_valuesDataGridView.Rows[e.RowIndex].Cells[3].Value = val1 + val2;
//adding vertically down the total column to get Grand Total
double sum = 0;
for (int i = 0; i < tbl_valuesDataGridView.Rows.Count; ++i)
{
sum += Convert.ToDouble(tbl_valuesDataGridView.Rows[i].Cells[2].Value);
}
textBox1.Text = sum.ToString();
}
POINTS TO NOTE:
The DataGridView has the following columns; id which auto
increments, value1, value2 and total. In addition, there is a
textbox in my form that shows the Grand Total (i.e. totalling all
the values in the total column).
In the sql server database table, id is the primary key column with type int, values 1,2 & total are of type 'money' and allow null.
If I change the row indexes in my code to 0,1 & 2, I don't get any error but the system adds the ID (index 0) to Value1(index 1). However, when I change it to 1,2 & 3, that is when I get the error.
I am new to programming, so I don't know whether the error is as a result of the way I setup the database table, or whether it is with my C# code.
Can someone please help me?
For your horizontal addition code, you set Cells[3].Value equal to Cells[1].Value + Cells[2].Value.
This is fine, but you appear to be doing it for one row only.
Then in your vertical addition you're totalling Cells[2].Value from all rows.
If I understand the code correctly, you should be totalling Cells[3].Value here (as this is where the total for each row is stored).
In any case, you need to go to the line where the exception is thrown, and examine each variable in the debugger Watch window. This should tell you which object is in an incorrect state.
With Anik's help and some debugging, I was able to get the answer to my question. For those of us who are new to programming, you will find this code very helpful if you intend working with DataGridView. Always ensure you use the CellEndEdit event handler for this code.
private void tbl_valuesDataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
//totalling horizontally across rows
int val1 = 0, val2 = 0, demo1;
if (int.TryParse(tbl_valuesDataGridView.Rows[e.RowIndex].Cells[1].Value.ToString().Trim(), out demo1))
val1 = Convert.ToInt32(tbl_valuesDataGridView.Rows[e.RowIndex].Cells[1].Value);
if (int.TryParse(tbl_valuesDataGridView.Rows[e.RowIndex].Cells[2].Value.ToString().Trim(), out demo1))
val2 = Convert.ToInt32(tbl_valuesDataGridView.Rows[e.RowIndex].Cells[2].Value);
tbl_valuesDataGridView.Rows[e.RowIndex].Cells[3].Value = val1 + val2;
//Adding vertically down the total column
double sum = 0;
for (int i = 0; i < tbl_valuesDataGridView.Rows.Count; ++i)
{
sum += Convert.ToDouble(tbl_valuesDataGridView.Rows[i].Cells[3].Value); //where 1 represents the index of the column you are adding
}
textBox1.Text = sum.ToString();
}