I am Developing a winforms application. I have two datagrid views populated from two different bindingsources(control). I am using these to implement the master detail approach. My problem is that when the first datagridview is populated using the binding source I can't select the first row of it ,because the first element in the binding source is defaultly selected and can't be selected. Can any one provide me a solution for this
As you say the first row is selected by default. So after populating the DataSource to your first GridView you can set the second GridView based on first entry. Later you check the selectionChanged Event to populate the second GridView based on selectedRow of your first one.
Code could look sth. like this:
private void PopulateDataSource()
{
dataGridView1.DataSource = myBindingSource;
DataRowView selectedRow;
if (dataGridView1.SelectedRows.Count > 0)
selectedRow = dataGridView1.SelectedRows[0] as DataRowView;
if (selectedRow != null)
dataGridView2.DataSource = myBindingSource2; //Set the BindingSource based on selectedRow in first Grid
}
private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
DataRowView selectedRow;
if (dataGridView1.SelectedRows.Count > 0)
selectedRow = dataGridView1.SelectedRows[0] as DataRowView;
if (selectedRow != null)
dataGridView2.DataSource = myBindingSource2; //Set the BindingSource based on selectedRow in first Grid
}
If this doesn't work let me know but should do the job.
UDPATE
Here is a similar example using the events and methods of the bindingSource:
private void Initialize()
{
RegisterBindingSourceEvents();
dataGridView1.DataSource = bindingSource1;
dataGridView2.DataSource = bindingSource2;
bindingSource1.DataSource = myDataSource;
}
private void RegisterBindingSourceEvents()
{
bindingSource1.DataSourceChanged += BindingSource1_DataSourceChanged;
bindingSource1.CurrentChanged += BindingSource1_CurrentChanged;
}
private void BindingSource1_CurrentChanged(object sender, EventArgs e)
{
DataRowView row = bindingSource1.Current as DataRowView;
if (row != null)
bindingSource2.DataSource = myDataSource2BasedOnRow;
}
private void BindingSource1_DataSourceChanged(object sender, EventArgs e)
{
DataRowView row = bindingSource1.Current as DataRowView;
if (row != null)
bindingSource2.DataSource = myDataSource2BasedOnRow;
}
Further you maybe can use:
bindingSource.MoveNext();
bindingSource.MoveFirst();
To simulate focusing seconde row and directly first row. A bit ugly but i would guess this fires current_changed (untested). Better use first approach.
UDPATE-2
I'm sorry to tell you that this is not possible in a beautiful manner. The problem is that the Current Property of your bindingList is always set if your DataSource contains items. So if the user select the same row as the bindingSource Current Property contains your event won't get called. I found a solution which works in my example. You will need one gridEvent and maybe have to do some improvments but the idea should do the job. Sorry but without gridEvent i can't solve this:
Notice that iam using List as DataSource for my Testcase. You got DataTable and have to cast to DataRowView instead of Dummy for sure.
private bool _automatedRowChange;
private void Initialize()
{
List<Dummy> dummies = new List<Dummy> { new Dummy { Id = 1, Text = "Test1" }, new Dummy { Id = 2, Text = "Test2" } };
bindingSource1.DataSource = dummies;
dataGridView1.DataSource = bindingSource1;
//So the first row isn't focused but the bindingSource Current Property still holds the first entry
//That's why it won't fire currentChange even if you click the first row. Just looks better for the user i guess
dataGridView1.ClearSelection();
bindingSource1.CurrentChanged += BindingSource1_CurrentChanged;
dataGridView1.CellClick += DataGridView1_CellClick;
}
private void DataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
var clickedRow = dataGridView1.Rows[e.RowIndex].DataBoundItem as Dummy;
var currentRow = bindingSource1.Current as Dummy;
if (clickedRow != null &&
currentRow != null &&
clickedRow.Equals(currentRow))
{
_automatedRowChange = true;
bindingSource1.MoveNext();
_automatedRowChange = false; //MovePrevious is based on the click and should load the dataSource2
bindingSource1.MovePrevious();
}
}
private void BindingSource1_CurrentChanged(object sender, EventArgs e)
{
if (!_automatedRowChange) //Check if you jump to next item automatically so you don't load dataSource2 in this case
{
//Set the second DataSource based on selectedRow
}
}
Related
I want to add a new row to datagridview after validating the row. I call my AddNewRow method inside of the RowValidated event. But it gives "Operation cannot be performed in this event handler" error.
My datagridview is not bounded any data source, and AllowUsersToAddRow property is setted to false.
DataGridView have only one column, which is named "Cost".
Here is my code;
private void RMaterialDGV_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
{
DataGridView dgv = sender as DataGridView;
if (dgv.IsCurrentRowDirty)
{
if(dgv.CurrentRow.Cells["Cost"].Value == null || dgv.CurrentRow.Cells["Cost"].Value.ToString().Length == 0
|| dgv.CurrentRow.Cells["Cost"].Value.ToString() == 0.ToString())
{
e.Cancel = true;
}
}
}
private void RMaterialDGV_RowValidated(object sender, DataGridViewCellEventArgs e)
{
DataGridView dgv = sender as DataGridView;
if (dgv.CurrentRow.Index == dgv.Rows.Count - 1 && dgv.CurrentRow.Cells["Cost"].Value != null)
{
if (dgv.CurrentRow.Cells["Cost"].Value.ToString().Length > 0)
AddNewRowToDGV();
}
}
private void AddNewRowToDGV()
{
int index = RMaterialDGV.Rows.Add();
RMaterialDGV.Rows[index].Cells["Cost"].Value = 0;
}
I use this method to add new rows before(other projects) but never get these error.
What am I missing?
Thanks in advance.
Rows cannot be programmatically added to the DataGridView's rows collection when the control is data-bound.
Instead you can add rows to your datasource. Update your AddNewRowToDGV method to something like below.
private void AddNewRowToDGV()
{
var data = (DataTable) dataGridView1.DataSource;
var row = data.NewRow();
row["Cost"] = 1;
data.Rows.Add(row);
}
I solved this problem creating a DataTable and binding this DataTable to my DataGridView. But I can't really understand what cause the actual problem.
After I click a particular row and I click a button, I want to change the cell type from default type (DataGridViewTextBoxCell) to DataGridViewComboBoxCell and to change that cell data source to a list.
Somehow I encountered an unexplained behavior, In one line it works and shows the ComboBox, in other, it just enters to edit mode.
My code looks something like this:
private void btnClick(object sender, EventArgs e)
{
dataGridView.BeginEdit(true);
var selectedRow = CurrentCell.RowIndex;
var selectedColumn = CurrentCell.ColumnIndex;
var cellName = dataGridView[0, _selectedCell.RowIndex].Value.ToString();
var dict = GetDict(cellName);
if (dict != null)
{
var comboBoxCell = new DataGridViewComboBoxCell
{
DataSource = dict.Keys.ToList()
}
dataGridView[1, selectedRow] = comboBoxCell;
dataGridView.CurrentCell = dataGridView.Rows[selectedRow].Cells[selectedColumn];
}
dataGridView.BeginEdit(false);
}
Update: It seems only after the event CellBeginEdit is fired, which is caused by pressing other cell it updates, But then it takes like 3-4 click for the ComboBox to open.
Try This Code. You should override CellClick Event or Call You Method There. This code is written in CellClick Event, Don't get confused with Name
//This Code is written in CellClick Event not in CellContentClick (Don'tconfuse with signature)
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
dataGridView1.BeginEdit(true);
var selectedRow = (sender as DataGridView).CurrentCell.RowIndex;
var selectedColumn = (sender as DataGridView).CurrentCell.ColumnIndex;
var comboBoxCell = new DataGridViewComboBoxCell
{
};
dataGridView1.CurrentCell = dataGridView1.CurrentCell as DataGridViewComboBoxCell;
dataGridView1[selectedColumn, selectedRow] = comboBoxCell;
dataGridView1.CurrentCell = dataGridView1.Rows[selectedRow].Cells[selectedColumn];
dataGridView1.BeginEdit(false);
}
I am trying to add the column names of a gridview to a dropdown list.
The problem is when I change the datasource of the gridview, it returns empty.
I am not sure whether the event is working properly, as it does not work with/without it.
I wanted to use the DataBindComplete event, but I could not see it so I tried DataBound instead.
private void BindTable()
{
if (ddTableSearch.SelectedIndex == 0)
{
tblCustomerTableAdapter customerAdapter = new tblCustomerTableAdapter();
GridView2.DataSource = customerAdapter.GetData();
GridView2.DataBind();
}
else if (ddTableSearch.SelectedIndex == 1)
{
tblInvoiceTableAdapter invoiceAdapter = new tblInvoiceTableAdapter();
GridView2.DataSource = invoiceAdapter.GetData();
GridView2.DataBind();
}
else if (ddTableSearch.SelectedIndex == 2)
{
tblEstimateTableAdapter estimateAdapter = new tblEstimateTableAdapter();
GridView2.DataSource = estimateAdapter.GetData();
GridView2.DataBind();
}
}
protected void GridView2_DataBound(object sender, EventArgs e)
{
// Populate dropdown with column names
ddColumnSearch.Items.Clear();
for (int i = 0; i < GridView2.Columns.Count; i++)
{
ddColumnSearch.Items.Add(new ListItem(GridView2.Columns[i].ToString()));
}
}
What am I doing wrong?
The databound event runs for each record in the gridview. So one issue is that each time you are clearing out the items you added before. Another issue is that you need to get the data from the EventArgs instead of from the gridview.columns since those are not there until after all the data is bound. I think all you need to do is get the data from the header row:
protected void GridView2_DataBound(object sender, EventArgs e)
{
// Populate dropdown with column names
if(e.Row.RowType != DataControlRowType.Header) return; //only continue if this is hdr row
ddColumnSearch.Items.Clear();
foreach (TableCell cell in e.Row.Cells)
{
ddColumnSearch.Items.Add(new ListItem(cell.Text));
}
}
I have a Gridview and a RepositoryItemGridLookUpEdit in that GridView
I want to show a CustomDisplayText in the RepositoryItemGridLookUpEdit
private void rgluePerson_CustomDisplayText(object sender, DevExpress.XtraEditors.Controls.CustomDisplayTextEventArgs e)
{
var person = rgluePerson.GetRowByKeyValue(e.Value) as Person;
var name = person.Name;
var surname = person.Surname;
e.DisplayText = name + ", " + surname;
}
}
The problem is that the person name depends of another cell in the same row (in the main Gridview), and i don't know how to get the current's Gridview row being processed (current row doesn't work since i need the row being processed in the moment)...... i can't use a gridView event cuz it will change the cell value, but i want to change the Text value.
Does anyone knows how to do that?
You cannot get the row being processed by the CustomDisplayText event because there are no such fields or properties that contains current row. You can only use this event for focused row. For this your must check if sender is type of GridLookUpEdit:
private void rgluePerson_CustomDisplayText(object sender, CustomDisplayTextEventArgs e)
{
if (!(sender is GridLookUpEdit))
return;
var anotherCellValue = gridView1.GetFocusedRowCellValue("AnotherCellFieldName");
//Your code here
e.DisplayText = yourDisplayText;
}
For not focused rows you can use only ColumnView.CustomColumnDisplayText event:
private void gridView1_CustomColumnDisplayText(object sender, CustomColumnDisplayTextEventArgs e)
{
if (e.Column.ColumnEdit != rgluePerson)
return;
var anotherCellValue = gridView1.GetListSourceRowCellValue(e.ListSourceRowIndex, "AnotherCellFieldName");
//Your code here
e.DisplayText = yourDisplayText;
}
I have a problem with datagridview in my windows form application.
I set AllowUserToAddRows=true, so on user double click on last blank rows, the selected cell goes in edit mode and when user write something in the textboxcolumn a new row is added.
This is all fine, but now I would that when new row is edit by user(double click) all the fields are filled with default values, for example using values from first row, so I set DefaultValuesNeeded event on my datagridview and in code behind I fill all field in the selected row.
The problem is that now no new row appear on bottom after DefaultValuesNeeded fire.
How can I solve this issue ?
If you have a binding source to your DataGridView, you can call EndCurrentEdit() in the DefaultValuesNeeeded event handler to commit the new row immediately with the default values.
{
dt = new DataTable();
dt.Columns.Add("Cat");
dt.Columns.Add("Dog");
dataGridView1.AllowUserToAddRows = true;
dataGridView1.DefaultValuesNeeded += dataGridView1_DefaultValuesNeeded;
dataGridView1.DataSource = dt;
}
void dataGridView1_DefaultValuesNeeded(object sender, DataGridViewRowEventArgs e)
{
var dgv = sender as DataGridView;
if(dgv == null)
return;
e.Row.Cells["Cat"].Value = "Meow";
e.Row.Cells["Dog"].Value = "Woof";
// This line will commit the new line to the binding source
dgv.BindingContext[dgv.DataSource].EndCurrentEdit();
}
If you don't have a binding source, we cannot using the DefaultValuesNeeded event since it doesn't work. But we can simulated it by capturing the CellEnter event.
{
dataGridView1.Columns.Add("Cat", "Cat");
dataGridView1.Columns.Add("Dog", "Dog");
dataGridView1.AllowUserToAddRows = true;
dataGridView1.CellEnter += dataGridView1_CellEnter;
}
void dataGridView1_CellEnter(object sender, DataGridViewCellEventArgs e)
{
var dgv = sender as DataGridView;
if (dgv == null)
return;
var row = dgv.Rows[e.RowIndex];
if (row.IsNewRow)
{
// Set your default values here
row.Cells["Cat"].Value = "Meow";
row.Cells["Dog"].Value = "Woof";
// Force the DGV to add the new row by marking it dirty
dgv.NotifyCurrentCellDirty(true);
}
}