int curCol; //Column about which the user will be prompted #endregion Fields #region Constructors //The class constructor takes as arguments the two only relevant variables for this popup: list of all the columns and index of the current columns public popUp(AllInputs allInputs_temp, int curCol_temp) { InitializeComponent(); allInputs = allInputs_temp; curCol = curCol_temp; }
//Method called from mainForm.inputsToUpdate (in charge of determining whether some columns of values should be changed) to perform accessory actions. //It returns a list of the non-numerical columns (if any); that is: columns which have to be modified (e.g., categorical columns) or ones which have to be //ignored (e.g., blank/constant ones) public List<Input> inputsToUpdate2(AllInputs inputCols) { string blankMessage = ""; string constantMessage = ""; List<Input> nonNumList = new List<Input>(); //Loop iterating through all the columns ("Input") to determine whether there is any suitable one (i.e, non-numerical) for (int i = inputCols.inputs.Count - 1; i >= 0; i--) { if (inputCols.inputs[i].type.mainType == MainTypes.Blank) { blankMessage = inputCols.inputs[i].displayedName + ", " + blankMessage; inputCols.inputs.RemoveAt(i); } else if (inputCols.inputs[i].type.mainType == MainTypes.Constant) { constantMessage = inputCols.inputs[i].displayedName + ", " + constantMessage; inputCols.inputs.RemoveAt(i); } else if (inputCols.inputs[i].type.mainType == MainTypes.Categorical || inputCols.inputs[i].type.mainType == MainTypes.DateTime) { nonNumList.Add(inputCols.inputs[i]); } } //Showing a message in case of having ignored columns (e.g., blank or constant) showNonNumericalMessage(blankMessage, constantMessage); return nonNumList; }
//Function called after the column updating process has finished to perform some actions in the definitive column public AllInputs finalColumnActions(AllInputs allInputs) { for (int i = 0; i < allInputs.inputs.Count; i++ ) { if (allInputs.inputs[i].type.mainType != MainTypes.Blank) { allInputs.inputs[i] = preAnalyseColumn(allInputs.inputs[i]); allInputs.inputs[i].min = allInputs.inputs[i].vals.Min(); allInputs.inputs[i].max = allInputs.inputs[i].vals.Max(); allInputs.inputs[i].totDiffVals = allInputs.inputs[i].vals.Distinct().Count(); } } return allInputs; }
//Method called after the popup has been closed, taking as argument the new type input by the user public void updateNonNumerical(AllInputs allInputs, int curCol, InputType newType) { if (allInputs.inputs[curCol].vals2.Count >= allInputs.inputs[curCol].vals.Count) { if (newType.mainType == MainTypes.Blank) { allInputs.inputs[curCol].vals = new List<double>(); allInputs.inputs[curCol].vals2 = new List<string>(); allInputs.inputs[curCol].type.mainType = MainTypes.Blank; } else { if (newType.mainType == MainTypes.DateTime) { //The current column has to be converted to DateTime (if possible) allInputs.inputs[curCol] = updateTime(allInputs.inputs[curCol], newType.secType); } else if (newType.mainType == MainTypes.Categorical) { //The current column has to be converted to categorical (i.e., made-up scale starting from 0 assigning unique IDs to every values) allInputs.inputs[curCol] = updateCategorical(allInputs.inputs[curCol]); } allInputs = lastActionsNewNonNumerical(allInputs, curCol, newType); } } if (curCol < allInputs.inputs.Count) { //There are still some non-numerical columns requiring an input from the user checkNextColumn(allInputs, curCol); } else { allInputs0 = allInputs; allInputs0.updateCompleted = true; completed = true; //All the columns have been updated by the user and thus the inputs can be shown in the corresponding controls; this flag tells the BGW to stop waiting } }
//Method in charge or reading the CSV file with the input values public AllInputs readInputs() { AllInputs allInputs = new AllInputs(); bool errorPresent = false; try { using (StreamReader sr = new StreamReader(Environment.CurrentDirectory + @"\inputs.csv")) { string curLine = null; int totCols = 0; while ((curLine = sr.ReadLine()) != null) { curLine = curLine.Trim(); if (curLine.Length == 0 && totCols > 0) break; string[] temp = colsInRow(curLine); if ((totCols == 0 && temp.Length >= 2) || (totCols > 0 && temp.Length == totCols)) { if (totCols == 0) { //Heading allInputs = readHeading(temp, allInputs); totCols = temp.Length; } else { //Input values line allInputs = readLine(temp, allInputs); if (allInputs.inputs.Count >= 0.75 * Int32.MaxValue) { allInputs.maxRowConsidered = allInputs.inputs.Count; //Too many rows. Some of them will be ignored break; } } } else { errorPresent = true; break; } } } } catch { errorPresent = true; } if (errorPresent) { //Just one unaccounted error is enough to not perform the calculations at all allInputs = new AllInputs(); MessageBox.Show("There was an error while reading \"inputs.csv\"."); } return allInputs; }
//Method called from mainForm to start the process of updating the current non-numerical columns public void updateColumns(AllInputs allInputs, int curCol) { completed = false; checkNextColumn(allInputs, -1); }
Form mainForm; //To store the current instance of mainForm #endregion Fields #region Constructors //Class constructor performing initial actions public Modifications(Form mainForm_temp) { allInputs0 = new AllInputs(); curPopUp = null; mainForm = mainForm_temp; }
//Displaying the popup allowing the user to input the type for the given non-numerical column private void showPopupNonNumerical(AllInputs allInputs, int curCol) { curPopUp = new popUp(allInputs, curCol); curPopUp.Tag = this; curPopUp.Show(); int newX = mainForm.Location.X + mainForm.Width / 2 - curPopUp.Width / 2; int newY = mainForm.Location.Y + mainForm.Height / 2 - curPopUp.Height / 2; curPopUp.Location = new Point(newX, newY); }
//Method called once the input-file reading is over to update the required variables/GUI-controls accordingly private void bgwCompleteUpdate(AllInputs curInputs) { if (curInputs.inputs.Count > 0) { bool updateNonNum = true; if (curInputs.updateCompleted || curInputs.inputs.FirstOrDefault(x => x.type.mainType == MainTypes.NonNumerical) == null) { updateNonNum = false; } Modifications curModif = new Modifications(this); if (updateNonNum) { //Non-numerical columns are present and thus some additional actions have to be carried out curModif.updateColumns(curInputs, curInputs.inputs.Count); bgwMain.RunWorkerAsync(curModif); //To wait while the user performs the corresponding actions } else { //There is no non-numerical columns or the ones present have already been corrected curInputs.updateCompleted = true; curInputs = curModif.finalColumnActions(curInputs); //To analyse all the valid columns updateControlsWithInputs(curInputs); //To populate the corresponding controls and to eventually display some warnings } } else { //No valid inputs enableControls(false, false); btnUpdate.Enabled = true; this.Cursor = Cursors.Default; } }
//Method called to start/continue the non-numerical column update. This process consists in: displaying a set of popups (one per non-numerical column) and storing the corresponding answers private void checkNextColumn(AllInputs allInputs, int curCol) { bool found = false; int col = curCol; while (col < allInputs.inputs.Count - 1 && !found) { col = col + 1; if (allInputs.inputs[col].type.mainType != MainTypes.Blank && allInputs.inputs[col].type.mainType != MainTypes.Numerical) { showPopupNonNumerical(allInputs, col); found = true; //The new type for this column has to be determined by the user } } if (!found) { allInputs0 = allInputs; allInputs0.updateCompleted = true; completed = true; //All the columns have been updated by the user and thus the inputs can be shown in the corresponding controls } }
//Method called to populate the cmbBxIndependent (where the user selects the independent variable to be considered) with all the input columns private void populateVarsCombo(AllInputs allInputs) { if (cmbBxIndependent.Items.Count > 0) cmbBxIndependent.Items.Clear(); foreach (Input col in allInputs.inputs) { cmbBxIndependent.Items.Add(Common.getDisplayedName(col.displayedIndex, col.name, false, true)); } cmbBxIndependent.Items.Add(allIndItem); cmbBxIndependent.SelectedIndex = 0; }
//Method populating the main DGV with all the (eventually-corrected) inputs private void populateInputDGV(AllInputs allInputs) { dgvInputs.Columns.Clear(); dgvInputs.Tag = allInputs.inputs; //Adding to the DGV as many columns as input variables foreach (Input col in allInputs.inputs) { DataGridViewColumn curDGVCol = new DataGridViewTextBoxColumn(); curDGVCol.HeaderText = col.displayedIndex.ToString() + ". " + col.name; curDGVCol.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; curDGVCol.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter; dgvInputs.Columns.Add(curDGVCol); } //Adding all the values for (int row = 0; row < allInputs.inputs[0].vals.Count; row++) { dgvInputs.Rows.Add(); for (int col = 0; col < allInputs.inputs.Count; col++) { dgvInputs[col, row].Value = allInputs.inputs[col].vals[row]; } } }
//Method called in case that some (valid) non-numerical columns are present. It populates the controls in charge of displaying the performed conversions private void inputsToUpdate(AllInputs inputCols) { cmbBxNonNumerical.Items.Clear(); cmbBxNonNumerical.Tag = new List<Input>(); List<Input> nonNumList = curGUI.inputsToUpdate2(inputCols); bool visible = false; if (nonNumList.Count > 0) { for (int i = nonNumList.Count - 1; i >= 0; i--) { cmbBxNonNumerical.Items.Add(nonNumList[i].displayedName); ((List<Input>)cmbBxNonNumerical.Tag).Add(nonNumList[i]); } visible = true; cmbBxNonNumerical.SelectedIndex = 0; } cmbBxNonNumerical.Visible = visible; lstVwNonNumerical.Visible = visible; lblNonNumerical1.Visible = visible; lblNonNumerical2.Visible = visible; }
//Method extracting the values for all the columns in the first row (heading). The resulting number of columns will define all the subsequent rows. In case of finding a single row not having //this number of columns, the input file would be considered faulty and no calculations would be performed private AllInputs readHeading(string[] line, AllInputs colsSoFar) { for (int i = 0; i < line.Length; i++) { colsSoFar.inputs.Add(new Input { displayedIndex = i + 1, name = line[i].Trim(), displayedName = Common.getDisplayedName(i + 1, line[i].Trim(), false, false), displayedShortName = Common.getDisplayedName(i + 1, line[i].Trim(), true, false), vals = new List<double>(), type = new InputType() { mainType = MainTypes.Numerical} }); } return colsSoFar; }
//Method called after the corresponding conversion (to categorical/DateTime) has been completed to perform some final actions private AllInputs lastActionsNewNonNumerical(AllInputs allInputs, int curCol, InputType newType) { if (allInputs.inputs[curCol].wrongRowsCount > 0) { allInputs.inputs[curCol].vals = new List<double>(); allInputs.inputs[curCol].vals2 = new List<string>(); allInputs.inputs[curCol].type.mainType = MainTypes.Blank; string nameToShow = "\"" + allInputs.inputs[curCol].displayedName + "\""; string typeToShow = newType.mainType.ToString(); if (newType.mainType == MainTypes.DateTime) { if (newType.secType == DateTimeTypes.Time) { typeToShow = typeToShow + " (expected format: hh:mm:ss or equivalent)"; } else { typeToShow = typeToShow + " (expected format: dd-mm-yyyy or equivalent)"; } } MessageBox.Show("Some values in " + nameToShow + " cannot be converted into " + typeToShow + "." + Environment.NewLine + "This column will not be considered during the calculations."); } else { allInputs.inputs[curCol].type = newType; } return allInputs; }
//Method extracting the values for all the columns in any row after the first one (i.e., rows including input values) private AllInputs readLine(string[] line, AllInputs colsSoFar) { try { for (int i = 0; i < line.Length; i++) { if (colsSoFar.inputs[i].type.mainType == MainTypes.Numerical) { double curVal; if (double.TryParse(line[i], out curVal)) { colsSoFar.inputs[i].vals.Add(curVal); } else { if (line[i] == null || line[i].Trim().Length == 0) { colsSoFar.inputs[i].type.mainType = MainTypes.Blank; } else { colsSoFar.inputs[i].type.mainType = MainTypes.NonNumerical; } } } colsSoFar.inputs[i].vals2.Add(line[i]); } } catch { colsSoFar = null; } return colsSoFar; }
//Updating the controls with the information from the inputs public void updateControlsWithInputs(AllInputs curInputs) { bool calculate = true; if (curInputs.maxRowConsidered > 0) MessageBox.Show("Only the first " + curInputs.maxRowConsidered.ToString() + " rows will be considered during the calculations."); inputsToUpdate(curInputs); //Managing valid non-numerical columns if (curInputs.inputs.Count <= 1) { MessageBox.Show("No valid inputs were found."); } else { populateInputDGV(curInputs); //DGV population populateVarsCombo(curInputs); //Independent variable comboBox population calculate = false; } enableControls(true, calculate); }