How to add data source to Lookup Edit at run time? - c#

I have 2 Lookup edits in my winform. If I select 1st Lookup edit, need to add data source to 2nd Lookup edit based on what i selected in value in 1st Lookup edit. This all function can be done at run time.
This code i use currently for 1st Lookup edit and If i select any Item from 1st Lookup edit I need to fill / attach data source to 2nd Lookup edit.
How to complete my task ?
private void lookupedit1_EditValueChanged(object sender, EventArgs e)
{
object row = lookupedit1.Properties.GetDataSourceRowByKeyValue(lookupedit1.EditValue) as object;
memoedit1.Text = (row as DataRowView)["BFlatNo"].ToString() + ", " + (row as DataRowView)["BLocation"].ToString() + ".";
memoedit2.Text = (row as DataRowView)["DFlatNo"].ToString() + ", " + (row as DataRowView)["DLocation"].ToString() + ".";
}
Thanks in advance.

You can do this as follows:
yourLookUpEdit.Properties.DataSource = //Your List or DataTable

Related

Filter values in combo box A based on selected value in combo box B in DataGridView

I know it is quite popular question but I cannot find the solution to my problem. There are two combo boxes comboFrom and comboTo in DataGridView dgvTransfer - here is how their are populated:
private void PopComboFrom(DataGridViewComboBoxColumn combo, string valMember, int parValue1, int parValue2)
{
combo.DataSource = null;
DataTable dt = Helper.ExecuteDataTable("pp_sp_MachineAndOp",
new SqlParameter("#MachineAndOpID", SqlDbType.Int) { Value = parValue1 },
new SqlParameter("#Seq", SqlDbType.Int) { Value = parValue2 });
//Add field to be displayed
dt.Columns.Add("ToShow", typeof(string), "'Seq: ' + Seq + ' ID: ' + MachineAndOpID + ' (' + OperationID + ') ' + Operation + ' + ' + Machine");
// bind data table into combo box.
combo.DisplayMember = "ToShow";
combo.ValueMember = valMember;
combo.DataSource = dt;
}
private void PopComboTo(DataGridViewComboBoxColumn combo, string valMember, int parValue1, int parValue2, string seqFilter)
{
//combo.DataSource = null;
DataTable dt = Helper.ExecuteDataTable("pp_sp_MachineAndOp",
new SqlParameter("#MachineAndOpID", SqlDbType.Int) { Value = parValue1 },
new SqlParameter("#Seq", SqlDbType.Int) { Value = parValue2 });
//Add field to be displayed
dt.Columns.Add("ToShow", typeof(string), "'Seq: ' + Seq + ' ID: ' + MachineAndOpID + ' (' + OperationID + ') ' + Operation + ' + ' + Machine");
// bind data table into combo box.
DataView dv = new DataView(dt);
dv.RowFilter = "Seq > " + seqFilter;
combo.DisplayMember = "ToShow";
combo.ValueMember = valMember;
combo.DataSource = dv;
}
When comboFrom is changed the datasource of comboTo is filtered in this event handler:
private void dgvTransfers_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if(dgvTransfers.CurrentCell.ColumnIndex == dgvTransfers.Columns[controlFrom].Index && e.Control is ComboBox)
{
ComboBox comboBox = e.Control as ComboBox;
comboBox.SelectedIndexChanged -= ComboFromSelectionChanged;
comboBox.SelectedIndexChanged += ComboFromSelectionChanged;
}
}
private void ComboFromSelectionChanged(object sender, EventArgs e)
{
PopComboTo(cmbMachineAndOpIDTo, "MachineAndOpID", 0, 0, "7");
}
Filter works on comboTo but the problem is that values not visible in filtered comboTo also disappears from DataGridView!
Not filtered:
Filtered:
And this error raised for every row with empty data as a filter result:
How to show values in GridViewData and have filtered them in comboTo at same time?
In this example, we have two DataGridViewComboBoxColumns. Each column contains the same data source and obviously contain the same values. The goal is such that if the user selects one value in one combo box on a particular row, then, when the user selects the other combo box on the same row, then, the selected value in the first combo box will NOT be available in the second combo box. In other words, no two combo boxes on the same row in the grid will contain the same value.
I am sure there are numerous ways to do this. It may help to keep in mind that this is in reference to two DataGridViewComboBoxColumnss. Dealing with ComboBoxes in a column in a DataGridView can be challenging and it is not forgiving like a regular ComboBox is. Compound this with the idea that “each” combo box in each cell MAY contain a “different” DataSource only exacerbates things more and makes it easier for the grid to throw its dreaded DataError.
One BIG issue when using the combo box columns is if (somehow) a combo box cell’s value gets set to a value that is NOT in the combo boxes list of items. A regular ComboBox just ignores this, a DataGridViewComboBoxCell on the other hand, will complain about this and cause the grid to throw up its DataError. Or more precisely… a code crashing problem. Therefore, it is imperative that you take care and make sure that no combo box value is improperly set.
Given that we do not want the two combo boxes on a row to have duplicate values, there is one situation where it IS possible for the two combo box values to be the same… when both values are “empty,” or… not selected yet. In other words, it IS possible for the two combo boxes to have the same “NON” value. This is a “special” case and we will look for it, however, I want to stress that simply using the combo box cells null value for this “empty/non-choice” can be problematic. SO… to help, I suggest you ADD an “empty/non-choice” value as one of the items in each combo box. It may not be obvious at this point; however, this simple “non-choice” item in the combo boxes list of items is going to simplify things later.
To start, we need to break this down in to two (2) parts. Part one (1) is dealing with the user interaction with the combo boxes. If we have an “empty” grid with one row, then, when the user changes one of the combo boxes, the other combo box gets its data source filtered and vise versa. Part two (2) deals with what to do after the grid is loaded with existing data. If we simply load the data, then each combo box will be set properly, however, if the user clicks on one of the combo boxes, then the other combo boxes value will be available to duplicate. Once the user changes the combo boxes value, part one kicks in and filters the other combo box. In other words, we will need to do something AFTER the data is loaded into the grid to filter each of the combo box cells that were loaded.
If you create a new winforms solution and drop a DataGridView onto the form, you should be able to follow along in the steps below to demonstrate this and proceed in a step-by-step fashion. First we will create some simple combo box data to display in the combo boxes list of items. Then, use that data in both combo boxes. After this, both combo boxes will contain the same data and the user will be able to duplicate values on a row.
The combo box data will be a DataTable with two properties/fields… an int MachineID and a string MachineOPID. The data table will have 11 rows of data. It should be noted, that here we also want to add our “non” choice item with…
dt.Rows.Add(0, "-");
It should be noted, that I did NOT use an “empty” string or null for the MachineOPID value. We WANT “something” there. In this case it is a simple dash character. We will look for the MachineID of zero (0) later to know that the user selected the “non-selected” value.
private DataTable GetComboData() {
DataTable dt = new DataTable();
dt.Columns.Add("MachineID", typeof(int));
dt.Columns.Add("MachineOPID", typeof(string));
dt.Rows.Add(0, "-");
for (int i = 1; i < 11; i++) {
dt.Rows.Add(i, "Mac_" + i);
}
return dt;
}
Next we need to add the combo box columns to the grid. For this a helper method is created that takes four parameters. A column name, colName; header text, a data source and finally a DataPropertyName which will be utilized in part two (2).
private DataGridViewComboBoxColumn GetComboCol(string colName, string headerText, DataTable data, string dpn) {
DataGridViewComboBoxColumn col = new DataGridViewComboBoxColumn();
col.Name = colName;
col.HeaderText = headerText;
col.ValueMember = "MachineID";
col.DisplayMember = "MachineOPID";
col.DataPropertyName = dpn;
col.DataSource = data;
return col;
}
That should get the combo boxes working as shown below. The combo boxes each have the same values and the user can select the same value for both combo boxes on any row.
DataTable ComboData;
public Form1() {
InitializeComponent();
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
}
private void Form1_Load(object sender, EventArgs e) {
ComboData = GetComboData();
dataGridView1.Columns.Add(GetComboCol("FromMachine", "From Machine", ComboData, "FromMachine"));
dataGridView1.Columns.Add(GetComboCol("ToMachine", "To Machine", ComboData, "ToMachine"));
}
This obviously is not doing anything in relation to checking for duplicate combo box values. In this case, I will use the grids CellValueChanged event to “filter” the other combo box. Example, if the user selects a value in the “FromMachine” column and then leaves the cell, then we will then filter the “ToMachine” combo box. The same idea applies if the changed cell is the “ToMachine” cell. Therefore, a helper method is created that takes three parameters, an int grid row index; a string column name of the column that was changed and finally the name of the column of the cell we want to filter. This way, we can use this same method for both combo box cells. It may look something like…
private void SetComboData(int rowIndex, string curColName, string targetColName) {
if (dataGridView1.Rows[rowIndex].Cells[curColName].Value != null) {
string selectedValue = dataGridView1.Rows[rowIndex].Cells[curColName].Value.ToString();
DataGridViewComboBoxCell cell = (DataGridViewComboBoxCell)dataGridView1.Rows[rowIndex].Cells[targetColName];
DataView dv = new DataView(ComboData);
// we do not want to filter out the "empty" value - i.e. BOTH From and To can be empty
if (selectedValue != "0") {
dv.RowFilter = "MachineID <> '" + selectedValue + "'";
}
cell.DataSource = dv;
}
}
First a simple check to see if the cell is not null. Then, grab that cells value as this will be the item we want to filter out of the other combo box. Grab the other combo box cell and cast it to a DataGridViewComboBoxCell so we can set its data source. Create a new DataView, set the row filter unless the selected item is our “non-selected” item which we do not want to filter out. Finally set the cells data source.
This helper method should simplify the code in the grids CellValueChanged event to something like…
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
if (dataGridView1.Columns[e.ColumnIndex].Name == "FromMachine") {
SetComboData(e.RowIndex, "FromMachine", "ToMachine");
}
else {
if (dataGridView1.Columns[e.ColumnIndex].Name == "ToMachine") {
SetComboData(e.RowIndex, "ToMachine", "FromMachine");
}
}
}
// updated constructor to subscribe to the grids `CellValueChanged` event
public Form1() {
InitializeComponent();
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
dataGridView1.CellValueChanged += new DataGridViewCellEventHandler(dataGridView1_CellValueChanged);
}
This change should work as shown below. No two combo boxes on the same row can be set to the same value. This should update if either combo box is changed and the combo boxes can both be set to the same “non-selected” values.
This should finish part one with the user’s interaction with the combo boxes. Part two is what to do when data is loaded into the grid. IMO, if you use a combo box column in a grid that is data bound… it is wise to “check” the combo box data bound to the combo box column. Any bad data is going to cause the grid to throw its code crashing DataError. Even in a simplistic approach, checking the grids data BEFORE binding the data to the grid is simply a CYA approach.
So… the question will always be…
”what do you do if a row in the data has offending data in the combo
boxes… i.e.…. a non-existent value (out of bounds)… or in this case,
duplicate FromMachine and ToMachine values?” …
Unfortunately this is something we can NOT ignore. You either have to remove the offending data or change it. Both ideas are bad ideas, but what choice do you have? In this example, if both values are the same, then is what we will do is “change” the “ToMachine” to our “non-selected” value. If the value is out of bounds, we will change it to the non-selected value. Obviously this “change” may be some needed info in a log file. However, in this example… we just want to change it, log it and move on.
To test this, below is some test data we can use to bind to the grid. It has three (3) rows with duplicate values. Also, it has three rows of bad data such that the MachineID is out of range (12, -1). It may look something like…
private DataTable GetGridData() {
DataTable dt = new DataTable();
dt.Columns.Add("FromMachine", typeof(int));
dt.Columns.Add("ToMachine", typeof(int));
dt.Rows.Add(1, 1);
dt.Rows.Add(4, 1);
dt.Rows.Add(5, 5);
dt.Rows.Add(0, 9);
dt.Rows.Add(4, 0);
dt.Rows.Add(0, 0);
dt.Rows.Add(12, 0);
dt.Rows.Add(0, -1);
dt.Rows.Add(-1, 12);
return dt;
}
If we bind this table to the grid, we will get the grids DataError. In addition, the duplicate values will be displayed in the grid. The duplicate values are a secondary problem to the obvious and constant DataError because of the out of bounds values in the data… namely the 12 and -1 values. This is why we need to check the grids combo box data values BEFORE we bind the data to the grid. Do NOT think you can simply catch the DataError and ignore/swallow the error… You MUST do something.
For this, we will create a simple method that takes the original grid data table and loops through the table’s rows. We will check for the out of bounds values and since we are doing this, we can check for “duplicate” combo box values and act accordingly. In both cases, if the value is out of bounds, we will set that value to the “non-selected” value 0. The same will apply if any two values on the same row are equal.
After we make these changes we “should” be able to breath a little easier knowing that the grids data error is NOT going to get thrown because the combo box values are out of bounds. In addition, this will fix any row that had the same combo box values. This fixer method may look something like…
private void FixDuplicateComboData(DataTable originalData) {
int curRowIndex = 0;
int fromValue;
int toValue;
foreach (DataRow row in originalData.Rows) {
fromValue = (int)row["FromMachine"];
toValue = (int)row["ToMachine"];
if (fromValue < 0 || fromValue > 10) {
Debug.WriteLine("Row: " + curRowIndex + " From: " + row["FromMachine"] + " value is out of range - setting to no-choice");
row["FromMachine"] = 0;
}
if (toValue < 0 || toValue > 10) {
Debug.WriteLine("Row: " + curRowIndex + " To: " + row["ToMachine"] + " value is out of range - setting to no-choice");
row["ToMachine"] = 0;
}
if (fromValue == toValue) {
Debug.WriteLine("Row: " + curRowIndex + " From: " + row["FromMachine"] + " and To: " + row["ToMachine"] + " - Machines are the same... Setting to no-choice");
row["ToMachine"] = 0;
}
curRowIndex++;
}
}
This should quiet down the data error and the duplicates have been fixed so no duplicates are displayed in the grid after the data is loaded. HOWEVER, there is still a little problem. The problem is that when the data is loaded into the grid, each combo box cell was NOT filtered. So, after the data is loaded, if we click on a combo box row with two values, you will see that the “other” combo box value IS in the combo boxes list of items. Further, if we select that item… our data error friend will start complaining.
Therefore, we have one last step to complete this. After the good data is loaded into the grid, we need to loop through ALL the rows in the grid and set each combo box cell’s data source to the properly set filter. Fortunately, our previous SetComboData method should make this relatively simple… like…
private void SetComboDataAfterDataLoad() {
foreach (DataGridViewRow row in dataGridView1.Rows) {
SetComboData(row.Index, "FromMachine", "ToMachine");
SetComboData(row.Index, "ToMachine", "FromMachine");
}
}
The final product should work something like shown below…
DataTable ComboData;
DataTable GridTable;
public Form1() {
InitializeComponent();
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
dataGridView1.CellValueChanged += new DataGridViewCellEventHandler(dataGridView1_CellValueChanged);
dataGridView1.DataError += new DataGridViewDataErrorEventHandler(dataGridView1_DataError);
}
private void Form1_Load(object sender, EventArgs e) {
ComboData = GetComboData();
dataGridView1.Columns.Add(GetComboCol("FromMachine", "From Machine", ComboData, "FromMachine"));
dataGridView1.Columns.Add(GetComboCol("ToMachine", "To Machine", ComboData, "ToMachine"));
GridTable = GetGridData();
FixDuplicateComboData(GridTable);
dataGridView1.DataSource = GridTable;
SetComboDataAfterDataLoad();
}
private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e) {
MessageBox.Show("Data Error: " + e.Exception.Message);
Debug.WriteLine("Data Error: " + e.Exception.Message);
}
This should complete the example. I hope it makes sense.

move to the next row below even upon a datagrid refresh

I have this code below which upon click it changes the default value for the Progress column from 0 to 1. The problem am having is that upon clicking the datagrid is also refreshed. I want it to be refreshed but I want to stay where it is and move to the next row below it. Upon refresh the cursor goes back to the beginning as expected. Yes I know it is obvious is because I call the refreshDataGrid function at the end so it will always do that.
private void button4_Click(object sender, EventArgs e)
{
string connectionString2 = "Data Source=LPMSW09000012JD\\SQLEXPRESS;Initial Catalog=Pharmacies;Integrated Security=True";
string query2 = "UPDATE dbo.[" + comboBox4.Text + "] SET Progress= '1' where code = '" + comboBox2.Text + "'; ";
using (SqlConnection connection = new SqlConnection(connectionString2))
{
SqlCommand command = new SqlCommand(query2, connection);
command.Connection.Open();
command.ExecuteNonQuery();
command.Connection.Close();
}
textBox1.Clear();
textBox3.Clear();
comboBox3.ResetText();
comboBox2.SelectedIndex = comboBox2.SelectedIndex + 1;
if (dataGridView3.CurrentRow != null)
dataGridView3.CurrentCell =
dataGridView3.Rows[Math.Min(dataGridView3.CurrentRow.Index + 1, dataGridView3.Rows.Count - 1)]
.Cells[dataGridView3.CurrentCell.ColumnIndex];
refreshDataGrid();}
I was hoping this particular block of code would solve the problem:
if (dataGridView3.CurrentRow != null)
dataGridView3.CurrentCell =
dataGridView3.Rows[Math.Min(dataGridView3.CurrentRow.Index + 1, dataGridView3.Rows.Count - 1)]
.Cells[dataGridView3.CurrentCell.ColumnIndex];
What I was trying to do is capture the current position of the highlighted row index and no matter if I call the refreshDataGrid function, it would still move in sequence rather than starting from the beginning each time.
Is there a way to accomplish this? To explain once more, I want to keep the current highlighted row index and let it move to the one following without having to worry about starting over upon refresh. eg. if I start at row 1 then it would go to row two even if I call the refreshDataGrid function.
You should save the current cell selection before refreshing the grid, then reset it after refreshing the grid completes. Depending on how the refresh works, you can save row and column position or save some key values to find the row again if the order or count might change.
if (dataGridView3.CurrentRow != null)
SaveCurrentCellLocation();
refreshDataGrid();
ResetCurrentCellLocation();
Fixed the problem.
stored the current row index to a variable and then incremented that value:
var i = dataGridView3.CurrentRow.Index;
refreshDataGrid();
dataGridView3.CurrentCell = dataGridView3.Rows[Math.Min(i + 1, dataGridView3.Rows.Count - 1)].Cells[0];

In a DataGridView, how do you get the pre-edited cell values back in that row?

I created a DataRow and set it equal to the row of the DataSet that is the equivalent current row of the grid. I want to isolate the original pre-edited values so I can revert back to these values in the grid if the user cancels the edits for that row. But the grid seems to be bound to the DataSet so that it is not isolating the original pre-edited cell values. I coded so it only creates the DataRow once in the CellBeginEdit event. But each time I edit a cell, the DataRow is changing and reflecting the new edited values. I tried using a DataGridViewRow but it also changed automatically with each cell edit rather than being an independent snapshot of the row at the time it was created. Here is my code:
private void gridDB_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
{
string test;
//DataGridViewRow currRow= new DataGridViewRow(); This kept updating with each edit
//rowSet is public bool and currRow is public DataRow
if (rowSet != true)
{
currRow = dSet.Tables[0].Rows[gridDB.CurrentRow.Index];
rowSet = true;
}
test = "In CellBeginEdit " + currRow[0].ToString() + " " +
currRow[1].ToString() + " " +
currRow[2].ToString() + " " +
currRow[3].ToString() + " " +
currRow[4].ToString();
MessageBox.Show(test);
}
Each time it came to this test string, it was showing each new edited cell edit. This told me that currRow is not a static snapshot of the DataSet row at the time it was created.
Please tell me why this does not work and advise me of the best way to be accomplish this.
Thank you!
Valhalla

MySQL update once WPF DataGrid cell has been edited

I have a DataGrid that is generated from a MySQL query. I would like to to be able to edit the database directly by modifying the DataGrid. Basically what I need is to generate a string like this after user has finished editing a cell:
pseudocode:
"UPDATE current_table SET current_column_name=new_cell_value WHERE id=number that is in the hidden first cell of the edited row"
What would be the most simple way to achieve this?
I've tried looking at the DataGrid properties and events but so far I could not find any "CellEditEnded" events nor can I find how to get the new value or the id that's in the first cell of the row.
EDIT:
Ok, I've made some progress, I am now able to get the id and the column name, but the problem is that I am unable to get the new cell value. I think the problem is that this code fires in CellEditEnding-event, so the change hasn't been commited yet. How can I fix this?
DataRowView dataRow = (DataRowView)gameDatagrid.SelectedItem;
int index = gameDatagrid.CurrentCell.Column.DisplayIndex;
string columnName = gameDatagrid.CurrentCell.Column.Header.ToString();
string cellValue = dataRow.Row.ItemArray[index].ToString();
string id = dataRow.Row.ItemArray[0].ToString();
MessageBox.Show(id + " : " + columnName + " : " + cellValue);
Thanks to a helpful comment I was able to solve this. The full code is as follows:
private void gameDatagrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
TextBox t = e.EditingElement as TextBox;
DataRowView dataRow = (DataRowView)gameDatagrid.SelectedItem;
int index = gameDatagrid.CurrentCell.Column.DisplayIndex;
string columnName = gameDatagrid.CurrentCell.Column.Header.ToString();
string newValue = t.Text;
int id = (int)dataRow.Row.ItemArray[0];
string query = "UPDATE table_name SET " + columnName + "=\"" + newValue + "\" WHERE id=" + id;
}

Getting values from templatefields gridview - C#

I am using a gridview to display a table queried from a database...
In this gridview I also added a buttonfield, and 3 template fields...
When i press the buttonfield the data from the database is acquired, but the
manual data inserted from the template fields are not.. It seems that null values are acquired.
From the xml file i am firing the following event:
OnRowCommand="GridView1_SelectedIndexChanged1"
and have the following method to catch that event:
protected void GridView1_SelectedIndexChanged1(object sender, GridViewCommandEventArgs e)
{
int x = Convert.ToInt32(e.CommandArgument);
GridView1.SelectRow(x);
GridViewRow row = GridView1.Rows[x];
Response.Write(row.Cells[0].Text + "\t");
Response.Write(row.Cells[1].Text + "\t");
Response.Write(row.Cells[2].Text + "\t");
Response.Write(row.Cells[3].Text + "\t");
Response.Write(row.Cells[4].Text + "\t");
Response.Write(row.Cells[5].Text + "\t");
Response.Write(row.Cells[6].Text + "\t");
Response.Write(row.Cells[7].Text + "\t");
Response.Write(row.Cells[8].Text + "\t");
}
Any ideas on how i can get the values from that template field? do i need to catch another event rather than GridViewCommandEventArgs? If so what event should i throw from the xml part?
Thanks,
You can find the control u added on to the row and then use the text property to get the value of it.
Something similar to row.FindControl(<urIdgoeshere>)\\cast it to textBoxand then use .Text to get the value of it .
GridViewRow row = GridView1.Rows[x];
Response.Write((TextBox)row.FindControl('txtbox1')).Text;
I am fighting with the same, I used to use the Cells[] code with asp BoundField.
Now i had to switch to template fields as I need buttons etc in the footer.
I am pulling my hair out, I use the same code as suggested by Ashley, and I get System.NullReferenceException: Object reference not set to an instance of an object.
I am doing this within the row command event.
Been this way since 3 days :(
Edit:
Quick Solution to my issue: I've created the ID given Field on the footer, and I had not given and ID to my ItemTemplate, i.e the only thing I had in there was <%# Eval("Description")%>
There looks like to be NO WAY to select this data without giving it some kind of ID in code behind. I was stupid and had not noticed this simple fact later on.

Categories

Resources