What I need to do is calculated the value of one field in the grid, based on the values of other fields in the grid. I need to run this calculation After the value in one of the dependent cells is changed, but only if the value was a Valid entry. The EditValueChanged, Validating, and Validated events of the editor/repository all occur before the data is posted back into the datasource. I am wondering if there is any event I can hook into that will allow me to fire this calculation after the data has been post back into the datasource, but before control is returned to the user.
Sample Code
//calculation functions
private void SetCalcROP(MyObjectt Row)
{
//rop = m/hr
TimeSpan ts = Row.ToTime - Row.FromTime;
double diffDepth = Row.EndDepth - Row.StartDepth;
if (ts.TotalHours > 0)//donot divide by 0
Row.ROP = diffDepth / ts.TotalHours;
else
Row.ROP = 0;
}
private void SetCalcDeltaP(MyObject Row)
{
Row.DeltaPress = Row.SPPOnBtm - Row.SPPOffBtm;
}
//events
private void repNumberInput_Validated(object sender, EventArgs e) //is actaully ActiveEditor_Validated
{
if (vwDDJournal.FocusedColumn.Equals(colSPPOff) || vwDDJournal.FocusedColumn.Equals(colSPPOn))
SetCalcDeltaP(vwDDJournal.GetFocusedRow() as MyObject);
}
private void repNumberInput_NoNulls_Validated(object sender, EventArgs e) //is actaully ActiveEditor_Validated
{
if (vwDDJournal.FocusedColumn.Equals(colStartDepth) || vwDDJournal.FocusedColumn.Equals(colEndDepth))
SetCalcROP(vwDDJournal.GetFocusedRow() as MyObject);
}
private void repTimeEdit_Validated(object sender, EventArgs e) //is actaully ActiveEditor_Validated
{
SetCalcROP(vwDDJournal.GetFocusedRow() as MyObject);
}
private void repNumberInput_NoNulls_Validating(object sender, System.ComponentModel.CancelEventArgs e)
{
TextEdit TE = sender as TextEdit;
//null is not valid for this entry;
if (string.IsNullOrEmpty(TE.Text))
{
e.Cancel = true;
vwDDJournal.SetColumnError(vwDDJournal.FocusedColumn, "This Column may not be blank");
return;
}
else
{
double tmp;
if (!Double.TryParse(TE.Text, out tmp))
{
e.Cancel = true;
vwDDJournal.SetColumnError(vwDDJournal.FocusedColumn, "This Column must contain a number");
return;
}
}
}
private void repNumberInput_Validating(object sender, System.ComponentModel.CancelEventArgs e)
{
TextEdit TE = sender as TextEdit;
//null is not valid for this entry;
if (!string.IsNullOrEmpty(TE.Text))
{
double tmp;
if (!Double.TryParse(TE.Text, out tmp))
{
e.Cancel = true;
vwDDJournal.SetColumnError(vwDDJournal.FocusedColumn, "This Column must contain a number");
return;
}
}
}
private void repTimeEdit_Validating(object sender, System.ComponentModel.CancelEventArgs e)
{
if (vwDDJournal.FocusedColumn.Equals(colToTime))
{//dont bother to check from time
//TIME TRAVEL CHECK!!!!
DateTime FromTime = Convert.ToDateTime(vwDDJournal.GetRowCellValue(vwDDJournal.FocusedRowHandle, colFromTime));
TimeEdit te = sender as TimeEdit;
DateTime ToTime = Convert.ToDateTime(te.EditValue);
if (ToTime < FromTime)
{//TIME TRAVEL
e.Cancel = true;
vwDDJournal.SetColumnError(vwDDJournal.FocusedColumn, "To Time must be greater than From Time");
return;
}
}
}
the problem is that everywhere I call this from, and whether I use vwDDJournal.GetRowCellValue(...) or vwDDJournal.GetFocusedRow() as MyObject, I still get the old edit value.
Requirements
I have to have the input validated before running the calculation.
I have to run the calculation immediately after making the change.
... What I need to do is calculated the value of one field in the grid, based on the values of other fields in the grid.
The best way to accomplish this task is using Unbound Columns feature.
The following example demonstrates how to implement this feature by handling the ColumnView.CustomUnboundColumnData event:
// Provides data for the Total column.
void gridView1_CustomUnboundColumnData(object sender, CustomColumnDataEventArgs e) {
if (e.Column.FieldName == "Total" && e.IsGetData) e.Value =
getTotalValue(e.ListSourceRowIndex);
}
// Returns the total amount for a specific row.
decimal getTotalValue(int listSourceRowIndex) {
DataRow row = nwindDataSet.Tables["Order Details"].Rows[listSourceRowIndex];
decimal unitPrice = Convert.ToDecimal(row["UnitPrice"]);
decimal quantity = Convert.ToDecimal(row["Quantity"]);
decimal discount = Convert.ToDecimal(row["Discount"]);
return unitPrice * quantity * (1 - discount);
}
Original example: How to: Add an Unbound Column Storing Arbitrary Data
You can also implement calculated value for unbound column using expressions:
GridColumn columnTotal = new GridColumn();
columnTotal.FieldName = "Total";
columnTotal.Caption = "Total";
columnTotal.UnboundType = DevExpress.Data.UnboundColumnType.Decimal;
columnTotal.UnboundExpression = "[Quantity] * [UnitPrice] * (1 - [Discount])";
gridView1.Columns.Add(columnTotal);
How about CustomCellValue?
After posting back to the data source refresh the data.
It's called whenever data is updated or view is changed.
Related
I want to make it work this way: when I write to NumericUpDown 1k the value should be 1000, when I write 4M the value should be 4000000. How can I make it?
I tried this:
private void NumericUpDown1_KeyDown(object sender, KeyEventArgs e)
{
if(e.KeyValue == (char)Keys.K)
{
NumericUpDown1.Value = NumericUpDown1.Value * 1000;
}
}
But it works with the original value that I wrote.
I want to make it work as a macroses. For example, If I want to get NUD1.Value 1000, I write 1 and then, when I press K the NUD1.Value becomes 1000.
Let's assume we have a NumericUpDown named numericUpDown1. Whenever the user presses k, we want to multiply the current value of the NUP by 1,000, and if the user presses m, the current value should be multiplied by 1,000,000. We also don't want the original value to trigger the ValueChanged event. So, we need to have a bool variable to indicate that the value is being updated.
Here's a complete example:
private bool updatingValue;
private void numericUpDown1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData != Keys.K && e.KeyData != Keys.M) return;
int multiplier = (e.KeyData == Keys.K ? 1000 : 1000000);
decimal newValue = 0;
bool overflow = false;
try
{
updatingValue = true;
newValue = numericUpDown1.Value * multiplier;
}
catch (OverflowException)
{
overflow = true;
}
updatingValue = false;
if (overflow || newValue > numericUpDown1.Maximum)
{
// The new value is greater than the NUP maximum or decimal.MaxValue.
// So, we need to abort.
// TODO: you might want to warn the user (or just rely on the beep sound).
return;
}
numericUpDown1.Value = newValue;
numericUpDown1.Select(numericUpDown1.Value.ToString().Length, 0);
e.SuppressKeyPress = true;
}
And the ValueChanged event handler should be something like this:
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
if (updatingValue) return;
// Simulating some work being done with the value.
Console.WriteLine(numericUpDown1.Value);
}
I've create an application which displays a DataGridView with a series of questions.
The dgv structure consists of one string column for the question text and three bool/checkbox columns for the answers (yes, no, N/A).
Each single question is displayed on its own row.
I would like my program to only allow the user to select only Yes, only No or only N/A on each row.
I think I would need to uncheck the other checkbox options when one option is checked but I'm not too sure on how to do this.
I've setup CellValueChanged and CellContentClick events but I'm unsure of the code needed to achieve the desired functionality.
DataGridView is bound to a DataTable.
Code I have so far:
private void dgvQuestions_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
int columnIndex = e.ColumnIndex;
int rowIndex = e.RowIndex;
DataGridViewCheckBoxCell chkYes = dgvQuestions.Rows[rowIndex].Cells[2] as DataGridViewCheckBoxCell;
DataGridViewCheckBoxCell chkNo = dgvQuestions.Rows[rowIndex].Cells[3] as DataGridViewCheckBoxCell;
DataGridViewCheckBoxCell chkNA = dgvQuestions.Rows[rowIndex].Cells[4] as DataGridViewCheckBoxCell;
if (Convert.ToBoolean(chkYes.Value) == true)
{
}
if (Convert.ToBoolean(chkNo.Value) == true)
{
}
if (Convert.ToBoolean(chkNA.Value) == true)
{
}
}
private void dgvQuestions_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
dgvQuestions.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
I hope this sample is useful for making DataGridView simple and powerful; it relates to the post as originally worded which was "Any help appreciated".
Does this video show the behavior that you're looking for? What works for me is to use a BindingList as the DataSource of the DataGridView. Then, using the 'CellDirty' event that occurs when a checkbox changes you can make them act like one-hot radio buttons and answer your question: "select only one checkbox from multiple checkbox items".
Here's an example of a class representing one line item of the questionaire.
class QuestionaireItem
{
public string Question { get; internal set; } = "Question " + _count++;
public bool Yes { get; set; }
public bool No { get; set; }
public bool Maybe { get; set; } // OOPS! I should have said "NA"
static int _count = 1;
}
When you bind this class to a DataGridView the columns will be automatically populated with a column named 'Question' (which is read-only (because the 'set' is marked internal) and the three checkbox columns whose value can be changed (because both get and set are public).
Taking this approach works for any class T and does nearly all the heavy lifting of using DataGridView.
Here's how you handle the CellDirty event to make the three checkboxes (I named them Yes, No and Maybe) act like radio buttons:
private void DataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
// The cell will be considered "dirty" or modified so Commit first.
dataGridView1.EndEdit(DataGridViewDataErrorContexts.Commit);
// Get the QuestionaireItem that is bound to the row
QuestionaireItem item = (QuestionaireItem)
dataGridView1
.Rows[dataGridView1.CurrentCell.RowIndex]
.DataBoundItem;
// Now see which column changed:
switch (dataGridView1.Columns[dataGridView1.CurrentCell.ColumnIndex].Name)
{
case "Yes":
item.No = false; // i.e. "unchecked"
item.Maybe = false;
break;
case "No":
item.Yes = false;
item.Maybe = false;
break;
case "Maybe":
item.Yes = false;
item.No = false;
break;
}
dataGridView1.Refresh(); // Update the binding list to the display
}
The binding itself is simple to do once the MainForm has its window Handle. We can override OnHandleCreated for this purpose. Here, the binding process will work properly and we can also set the display widths for the columns. This shows how to initialize dataGridView1. I've put comments in to explain what's happening:
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (!DesignMode) // We only want this behavior at runtime
{
// Create the binding list
BindingList<QuestionaireItem> testdata = new BindingList<QuestionaireItem>();
// And add 5 example items to it
for (int i = 0; i < 5; i++) testdata.Add(new QuestionaireItem());
// Now make this list the DataSource of the DGV.
dataGridView1.DataSource = testdata;
// This just formats the column widths a little bit
dataGridView1.Columns["Question"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridView1.Columns["Maybe"].Width =
dataGridView1.Columns["Yes"].Width =
dataGridView1.Columns["No"].Width = 40;
// And this subscribes to the event (one of them anyway...)
// that will fire when the checkbox is changed
dataGridView1.CurrentCellDirtyStateChanged += DataGridView1_CurrentCellDirtyStateChanged;
}
}
Clone or Download this example from GitHub.
It appears you have the CellContentClick set up properly, however, if there are other columns in the grid, then, it may be beneficial if you check to make sure that the cell whose content was clicked is actually one of the check box cells. Otherwise the code may be setting the cells value unnecessarily.
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e) {
string colName = dataGridView1.Columns[e.ColumnIndex].Name;
if (colName == "Yes" || colName == "No" || colName == "N/A") {
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
In the CellValueChanged event, again the code should check for only the check box values. In addition, I would assume that at least ONE (1) of the cells MUST be checked. Example, if the “N/A” cell is originally checked, then the user “unchecks” that cell, then the row would have NO check boxes checked. This is the final check in the code such that if the user “unchecks” the “N/A” cell AND this leaves ALL check boxes “unchecked”, then, the code will “check” the “N/A” cell. Also, it is important to “turn OFF” the CellValueChanged event before we change any of the check box values IN the CellValueChanged event to avoid reentrant. Something like…
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
if (e.RowIndex >= 0 && e.ColumnIndex >= 0) {
string colName = dataGridView1.Columns[e.ColumnIndex].Name;
bool checkValue;
dataGridView1.CellValueChanged -= new DataGridViewCellEventHandler(this.dataGridView1_CellValueChanged);
switch (colName) {
case "Yes":
checkValue = (bool)dataGridView1.Rows[e.RowIndex].Cells["Yes"].Value;
if (checkValue == true) {
dataGridView1.Rows[e.RowIndex].Cells["No"].Value = false;
dataGridView1.Rows[e.RowIndex].Cells["N/A"].Value = false;
}
else {
dataGridView1.Rows[e.RowIndex].Cells["N/A"].Value = true;
}
break;
case "No":
checkValue = (bool)dataGridView1.Rows[e.RowIndex].Cells["No"].Value;
if (checkValue == true) {
dataGridView1.Rows[e.RowIndex].Cells["Yes"].Value = false;
dataGridView1.Rows[e.RowIndex].Cells["N/A"].Value = false;
}
else {
dataGridView1.Rows[e.RowIndex].Cells["N/A"].Value = true;
}
break;
case "N/A":
checkValue = (bool)dataGridView1.Rows[e.RowIndex].Cells["N/A"].Value;
if (checkValue == true) {
dataGridView1.Rows[e.RowIndex].Cells["Yes"].Value = false;
dataGridView1.Rows[e.RowIndex].Cells["No"].Value = false;
}
else {
if ((bool)dataGridView1.Rows[e.RowIndex].Cells["Yes"].Value == false &&
(bool)dataGridView1.Rows[e.RowIndex].Cells["No"].Value == false) {
dataGridView1.Rows[e.RowIndex].Cells["N/A"].Value = true;
}
}
break;
default:
break;
}
dataGridView1.CellValueChanged += new DataGridViewCellEventHandler(this.dataGridView1_CellValueChanged);
}
Below is a simple example with the three columns “Yes”, “No” and “N/A” check box columns.
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
dataGridView1.DataSource = GetTable();
}
private DataTable GetTable() {
DataTable dt = new DataTable();
dt.Columns.Add("Yes", typeof(bool));
dt.Columns.Add("No", typeof(bool));
dt.Columns.Add("N/A", typeof(bool));
for (int i = 0; i < 10; i++) {
dt.Rows.Add(false, false, true);
}
return dt;
}
Hope this helps.
I have a function to duplicate a row in DataGridView which looks like this:
private void dgv_CellClick(object sender, DataGridViewCellEventArgs e)
{
DataGridView dgv = sender as DataGridView;
// If button clicked
if (e.ColumnIndex == 0)
{
// Get values what you want to use
var value1 = dgv.Rows[e.RowIndex].Cells[1].Value;
var value2 = dgv.Rows[e.RowIndex].Cells[2].Value;
// Insert a new row behind the click one
dgv.Rows.Insert(e.RowIndex + 1);
// Set the previously stored values
dgv.Rows[e.RowIndex + 1].Cells[1].Value = value1;
dgv.Rows[e.RowIndex + 1].Cells[2].Value = value2;
}
}
Now i wanted to write a function for one specific cell to increase + 1 with every click. For example: if in Cell[2] the value is 1, with the next added row the value should count up to 2, 3 and so on. I´ve tried to operate with + but i´ve got a error message that the operator cannot be applied to type of object. So i´ve tried to cast the specific cell like this:
var value2 = Convert.ToInt32(dgv.Rows[e.RowIndex].Cells[2].Value);
But now i get a SystemFormatException: Input string was not in a correct format.
Any ideas?
Many thanks in advance!
Allow only numeric values as below
private void dgv_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
// Textbox columns
if (dgv.CurrentCell.ColumnIndex == 2)
{
TextBox tb = e.Control as TextBox;
if (tb != null)
{
tb.KeyPress += TextBox_KeyPress;
}
}
}
private void TextBox_KeyPress(object sender, KeyPressEventArgs e)
{
// Only numeric characters allowed
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar))
{
e.Handled = true;
}
}
Problem:I want to get inserted characters(AccountID) in cell of datagridview, so that i can get respective username from database.
i had stuck in problem that on keypress event of datagridview, it is not getting recently insert char. For example,if user name is 'John Snow' its ID=JN. When I write 'J' in datagridview cell, and get value on keypress, it gets "". when I write further as 'JN', it gets 'J'. similarly, on 'JNE' , it gets 'JN' , so then i get JohnSnow from Database. In short, It get previous value not recently inserted.
I have Implement it as,
public DebitCredit()
{
InitializeComponent();
this.KeyPreview = true;
this.KeyPress += new KeyPressEventHandler(Control_KeyPress);
}
private void Control_KeyPress(object sender, KeyPressEventArgs e)
{
//Get the column and row position of the selected cell
int column = DGV_DEBIT_CREDIT.CurrentCellAddress.X;
int row = DGV_DEBIT_CREDIT.CurrentCellAddress.Y;
DataTable result=null;
if (column == 0)
{
string test = DGV_DEBIT_CREDIT[column, row].EditedFormattedValue.ToString();
result = getpro.SearchAccountByID(test);
if (result != null)
{
if (result.Rows.Count > 0)
{
DGV_DEBIT_CREDIT[column, row].Value = result.Rows[0][0].ToString();
DGV_DEBIT_CREDIT[column + 1, row].Value = result.Rows[0][1].ToString();
}
else
{
DGV_DEBIT_CREDIT[column, row].Value = "".ToString();
DGV_DEBIT_CREDIT[column + 1, row].Value = "".ToString();
}
}
}
}
First handle EditingControlShowing event of DataGridView as shown below. For the current control getting edited in the datagridview column, handle keyUp event as shown below.
Then you can see the expected result.
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
e.Control.KeyUp += new KeyEventHandler(Control_KeyUp);
}
private void Control_KeyUp(object sender, KeyEventArgs e)
{
//YOUR LOGIC
// string test = dataGridView1[0, 0].EditedFormattedValue.ToString();
}
First I make an unbound column in GridView (using DevExpress):
public void buttonl_Click(object sender, EventArgs e)
{
//GridColumn clmn = gridaaa.Columns["code"];
if (gridaaa.Columns.ColumnByFieldName("idx") != null)
{
gridaaa.Columns.ColumnByFieldName("idx").Dispose();
}
GridColunn unbColumn2 = gridaaa.Columns.AddField("idx");
unbColumn2.VisibleIndex = gridaaa.Columns.Count;
unbColumn2.UnboundType = DevExpress.Data.UnboundColumnType.Integer;
//// Disable editing.
unbColumn2.OptionsColunn.AllowEdit = false;
// gridaaa.Columns.ColumnByFieldName("idx").Dispose();
}
In the gridView1_CustomUnboundColumnData event I am using this code
private void gridView1_CustomUnboundColumnData(object sender, CustomColumnDataEventArgs e)
{
if (e.Column.FieldName == "idx")
{
e.Value = e.ListSourceRowIndex;
}
}
for the output or result was:
The question is how to make the index or idx column value become static when I click sorting in any column header. As you can see when I click genre header the value follow with code or index in database, I want to make it like autonumber from 0 whenever I keep click column header of genre or anything:
You can use ColumnView.GetRowHandle method to get the handle of current row which is always reflects to the position of row in GridView from up to down.
Here is example:
private void gridView1_CustomUnboundColumnData(object sender, CustomColumnDataEventArgs e)
{
if (e.Column.FieldName == "idx")
e.Value = gridView1.GetRowHandle(e.ListSourceRowIndex);
}