Working with ICalcData in Windows Forms Calculation Engine
13 Jul 20219 minutes to read
Essential Calculate provides calculation support to arbitrary business objects through ICalcData interface. To add calculation support to classes that represent data in a row/column format like a Data Grid, then you need to derive the classes inherited from ICalcData interface.
Methods and Events in ICalcData
ICalcData has three methods and one event. This interface allows the CalcEngine class in Essential Calculate to communicate with arbitrary data sources that implement this interface.
SetValueRowCol
SetValueRowCol method is used to set the value to mentioned row and column index. Essential Calculate expects any indexes (rows / column integer values) to be one-based.
An example of defining the SetValueRowCol method in custom class(CalcData) is explained below,
//Custom class,
public class CalcData : ICalcData
{
Dictionary<string, object> values = new Dictionary<string, object>();
//Defining SetValueRowCol method in Custom(user defined) Class,
public void SetValueRowCol(object value, int row, int col)
{
var key = RangeInfo.GetAlphaLabel(col) + row;
if (!values.ContainsKey(key))
values.Add(key, value);
else if (values.ContainsKey(key) && values[key] != value)
values[key] = value;
}
}
//Main class,
public void Main()
{
CalcData calcData = new CalcData();
//To set the data value of a specified row and column,
calcData.SetValueRowCol(90, 1, 1);
calcData.SetValueRowCol(50, 1, 2);;
}GetValueRowCol
GetValueRowCol method is used to get the value from mentioned row and column index. Essential Calculate expects any indexes (rows / column integer values) to be one-based.
An example of defining the GetValueRowCol method in custom class(CalcData) is explained below,
//Custom class,
public class CalcData : ICalcData
{
Dictionary<string, object> values = new Dictionary<string, object>();
//Defining GetValueRowCol method in Custom(user defined) Class,
public object GetValueRowCol(int row, int col)
{
object value = null;
var key = RangeInfo.GetAlphaLabel(col) + row;
this.values.TryGetValue(key, out value);
return value;
}
}
//Main class,
public void Main()
{
CalcData calcData = new CalcData();
//To get the data value of a specified row and column,
var value1 = calcData.GetValueRowCol(1, 1);
var value2 = calcData.GetValueRowCol(1, 2);
}WireParentObject
WireParentObject method that wires the ParentObject after the CalcEngine object is created or when a RegisterGridAsSheet call is made. The purpose is to give the data object
a chance to do any initialization steps it may need, such as subscribe the events to handle the changes in data notifications.
ValueChanged
ValueChanged event of ICalcData interface occurs whenever the value is changed. The CalcEngine listens to this event and accordingly reacts to data changes.
It is through this event that formulas are processed and dependencies are tracked by the CalcEngine.
//Custom class,
public class CalcData : ICalcData
{
public event ValueChangedEventHandler ValueChanged;
private void OnValueChanged(int row, int col, string value)
{
if (ValueChanged != null)
ValueChanged(this, new ValueChangedEventArgs(row, col, value));
}
}Computation using ICalcData
Below example shows the computation of formula using ICalcData interface.
Creating a Class from ICalcData
Create CalcData class derived from ICalcData interface,
public class CalcData : ICalcData
{
public event ValueChangedEventHandler ValueChanged;
Dictionary<string, object> values = new Dictionary<string, object>();
public object GetValueRowCol(int row, int col)
{
object value = null;
var key = RangeInfo.GetAlphaLabel(col) + row;
this.values.TryGetValue(key, out value);
return value;
}
public void SetValueRowCol(object value, int row, int col)
{
var key = RangeInfo.GetAlphaLabel(col) + row;
if (!values.ContainsKey(key))
values.Add(key, value);
else if (values.ContainsKey(key) && values[key] != value)
values[key] = value;
}
public void WireParentObject(){}
private void OnValueChanged(int row, int col, string value)
{
if (ValueChanged != null)
ValueChanged(this, new ValueChangedEventArgs(row, col, value));
}
}Setting Value into ICalcData
The SetValueRowCol method is used to set the value to ICalcData object.
CalcData calcData = new CalcData();
calcData.SetValueRowCol(10, 1, 1);
calcData.SetValueRowCol(20, 1, 2);Initialization of CalcEngine
The ICalcData object can be integrated into CalcEngine by passing it through constructor. Now, you can compute the expressions or equations using CalcEngine.
CalcData calcData = new CalcData();
CalcEngine engine = new CalcEngine(calcData);Evaluation of formula
The ParseAndComputeFormula method of CalcEngine is used to evaluate the formulas using the values from ICalcData object by cell references.
CalcData calcData = new CalcData();
calcData.SetValueRowCol(10, 1, 1);
calcData.SetValueRowCol(20, 1, 2);
CalcEngine engine = new CalcEngine(calcData);
string formula = “SUM (A1, B1)”;
string result = engine.ParseAndComputeFormula(formula);How to use custom control with CalcEngine
You can use any of Tools to in our CalcEngine. But it should be derived from ICalcData.
this.grid.ItemsSource = dt.DefaultView;
engine = new CalcEngine(this.grid);
public class CustomGrid : DataGrid,ICalcData
{
public CustomGrid()
{ }
public event ValueChangedEventHandler ValueChanged;
public object GetValueRowCol(int row, int col)
{
if (row < 0 || col < 0)
return "Invalid cell";
string s = (this.Items[row-1] as DataRowView).Row.ItemArray[col-1].ToString();
return s;
}
public void SetValueRowCol(object value, int row, int col)
{
//To set the value to specific cell.
}
public void WireParentObject()
{
//To trigger any of event for parent. This method is called when calcEngine assigned the parent object as CustomDataGrid.
}
}A sample that demonstrates using custom control with CalcEngine is available here
NOTE
To support cross-references among several
ICalcDataobjects, you must register the objects with a single instance of theCalcEngine.
For more reference, refer here.