I have a Windows Form with a DataGridView and a ComboBox. When a user clicks a row in the DGV, I want the ComboBox text to display the value in the row whether the value exists in the ComboBox or not. However, I also want users to be able to select items in the ComboBox without the ability to enter custom text.
Changing the DropDownStyle to DropDownList will not work because then I will not be able to programatically enter values that don't exist.
One idea I had was to change the style to DropDownList and temporarily add custom items and then remove them when they are no longer selected in the DGV, but I was hoping for a more elegant solution. Any help would be appreciated.
You can use the DataError event to your advantage here and add the missing item to the collection by casting the cell to a DataGridViewComboBoxCell and add the missing item to its collection:
public Form1() {
InitializeComponent();
DataTable dt = new DataTable();
dt.Columns.Add("C1");
DataRow dr1 = dt.NewRow();
dr1[0] = "ccc";
dt.Rows.Add(dr1);
DataRow dr2 = dt.NewRow();
dr2[0] = "xxx";
dt.Rows.Add(dr2);
dgv.AutoGenerateColumns = false;
var dgvCB = new DataGridViewComboBoxColumn();
dgvCB.Items.AddRange(new string[] { "aaa", "bbb", "ccc", "ddd" });
dgv.Columns.Add(dgvCB);
dgv.Columns[0].DataPropertyName = "C1";
dgv.DataError += dgv_DataError;
dgv.DataSource = dt;
}
void dgv_DataError(object sender, DataGridViewDataErrorEventArgs e) {
if (e.ColumnIndex == 0) {
string value = dt.Rows[e.RowIndex][e.ColumnIndex].ToString();
var dgvCB = (DataGridViewComboBoxCell)dgv.Rows[e.RowIndex].Cells[e.ColumnIndex];
if (!dgvCB.Items.Contains(value)) {
dgvCB.Items.Add(value);
}
}
}
What about creating the combobox_KeyPress and then add the line e.KeyChar = Nothing that will prevent any keystrokes into the box
Related
I would like to ask how to copy and save selected elements from one dataGridView to another? In one dataGridView, I have elements, that can be selected, and upon selection, they are then transferred to the other dataGridView, where they will be ready for printing on paper. However, with the code below, it doesn't copy the elements correctly, as it displays ContextMenuStrip, DefaultCellStyle, DividerHeight, etc...
It also doesn't save my selection, but instead overwrites it everytime I choose new rows. How exactly do you copy and save elements (rows) from one dataGridView to another? I would like to avoid doing it with clicking, as there will be multiples of elements that need to be ready for printing.
private void button5_Click(object sender, EventArgs e)
{
dataGridView2.DataSource = dataGridView1.SelectedRows;
}
This is my example, try to following this perhap?
if (GMDSP1.Rows.Count > 0)
{
DataTable _dt1 = new DataTable();
DataTable _dt2 = new DataTable();
DataGridViewRow gvdr = GMDSP1.CurrentRow;
DataRow[] drArr = _dt1.Select("Name= '" + gvdr.Cells["Name1"].Value.ToString() + "'");
if (drArr.Length > 0)
{
DataRow dr = _dt2.NewRow();
if (_dt2.Columns.Count == 0)
{
foreach (DataColumn dc in _dt1.Columns)
{
DataColumn newDC = new DataColumn(dc.ColumnName, dc.DataType);
_dt2.Columns.Add(newDC);
}
}
dr["ID"] = drArr[0]["ID"].ToString();
dr["Name"] = drArr[0]["Name"].ToString();
_dt2.Rows.Add(dr);
_dt1.Rows.Remove(drArr[0]);
_dt1.AcceptChanges();
_dt2.AcceptChanges();
GMDSP1.DataSource = _dt1;
GMDSP2.DataSource = _dt2;
This is the basic ui of my app and when i click the red (engaged room) button it should highlight the row in datagridview with cusid = 1, and no other row should be clickable.
I'm not able to find the row index (row number) in the dgv where cusid = 1, so that i can select/highlight it, specifically.
Could this be done, any helps?
I have a bookmyroom app and the i add my datagridview by using following code:
OleDbConnection connection = new OleDbConnection();
connection.Open();
OleDbCommand command = new OleDbCommand();
command.Connection = connection;
string query = "select id,cusid,cusname,timein,
timeout,duration,amount,remark from entry";
command.CommandText = query;
OleDbDataAdapter da = new OleDbDataAdapter(command);
DataTable dt = new DataTable();
da.Fill(dt);
dataGridView1.DataSource = dt;
and my checkbox column using this code;
DataGridViewCheckBoxColumn checkColumn = new DataGridViewCheckBoxColumn();
checkColumn.Name = "logout";
checkColumn.HeaderText = "Logout";
checkColumn.Width = 50;
checkColumn.ReadOnly = false;
checkColumn.FillWeight = 10;
dataGridView1.Columns.Add(checkColumn);
Before you read the answer you should know in a windows application you can have multiple forms and you can do some tasks in other forms. For example, you can make your grid read only, then select a row and click on a check out button and perform editing actions in another form.
But based on your question:
You can assign an event handler for all buttons and then in the handler, for each row you can look in cusid column (column with index = 1) and check if the value equals to the button Text then activate logout cell, else make the row readonly:
private void button_Click(object sender, EventArgs e)
{
var button = sender as Button;
foreach (DataGridViewRow item in this.dataGridView1.Rows)
{
//This is the last row, ignore it.
if (item.IsNewRow)
continue;
//Compare value of cell1 with button text
//I supposed button.Tex is the value that you want to compare with cusid
if (item.Cells[1].Value.ToString() == button.Text)
{
//Make row editable
item.ReadOnly = false;
//Select the logout cell
this.dataGridView1.CurrentCell = item.Cells[5];
}
else
{
//Make row readonly
item.ReadOnly = true;
}
}
}
literally off the top of my head and untested, you may want to try something like this to get the next row down (if that's really what you need):
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
var senderGrid = (DataGridView)sender;
if (senderGrid.Columns[e.ColumnIndex] is DataGridViewCheckBoxColumn &&
e.RowIndex >= 0)
{
var nextRowNum = senderGrid.Rows[e.RowIndex] + 1;
// do what you need with the custId cell/value
// i.e. select that row and hilite it etc
}
}
apologies if I've misunderstood your requirement for the images you added.
I have created a web page that displays a table. Most of the cells of the table are already filled in but some of them require user input. I would like to put drop down menus in some of these table cells such that the cell will be populated with the selection from the menu and the drop down list will disappear.
I have gotten this to work in a single cell. However, when I tried to get it to work in a second cell everything broke. I have hacked around so much trying to find the problem that I am not even sure I remember how I got the first one working now. :-)
Here is the relevant code:
protected void Page_Load(object sender, EventArgs e)
{
// Create a DropDownList control.
DropDownList DropList = new DropDownList();
// Set the properties for the DropDownList control.
DropList.ID = "TrendList";
DropList.AutoPostBack = true;
// Manually register the event-handling method for the
// SelectedIndexChanged event.
DropList.SelectedIndexChanged += new EventHandler(this.Selection_Change);
// Because the DropDownList control is created dynamically each
// time the page is loaded, the data must be bound to the
// control each time the page is refreshed.
// Specify the data source and field names for the Text and
// Value properties of the items (ListItem objects) in the
// DropDownList control.
DropList.DataSource = CreateDataSource();
DropList.DataTextField = "ColorTextField";
DropList.DataValueField = "ColorValueField";
// Bind the data to the control.
DropList.DataBind();
// Set the default selected item when the page is first loaded.
if (!IsPostBack)
{
DropList.SelectedIndex = 0;
}
// Add the DropDownList control to the Controls collection of
// the PlaceHolder control.
p1.Controls.Add(DropList);
p2.Controls.Add(DropList);
}
ICollection CreateDataSource()
{
// Create a table to store data for the DropDownList control.
DataTable dt = new DataTable();
// Define the columns of the table.
dt.Columns.Add(new DataColumn("ColorTextField", typeof(String)));
dt.Columns.Add(new DataColumn("ColorValueField", typeof(String)));
// Populate the table with sample values.
dt.Rows.Add(CreateRow("GreenUp", "GreenUp", dt));
dt.Rows.Add(CreateRow("GreenFlat", "GreenFlat", dt));
dt.Rows.Add(CreateRow("GreenDown", "GreenDown", dt));
dt.Rows.Add(CreateRow("YellowUp", "YellowUp", dt));
dt.Rows.Add(CreateRow("YellowFlat", "YellowFlat", dt));
dt.Rows.Add(CreateRow("YellowDown", "YellowDown", dt));
dt.Rows.Add(CreateRow("RedUp", "RedUp", dt));
dt.Rows.Add(CreateRow("RedFlat", "RedFlat", dt));
dt.Rows.Add(CreateRow("RedDown", "RedDown", dt));
dt.Rows.Add(CreateRow("ClearUp", "ClearUp", dt));
dt.Rows.Add(CreateRow("ClearFlat", "ClearFlat", dt));
dt.Rows.Add(CreateRow("ClearDown", "ClearDown", dt));
// Create a DataView from the DataTable to act as the data source
// for the DropDownList control.
DataView dv = new DataView(dt);
return dv;
}
DataRow CreateRow(String Text, String Value, DataTable dt)
{
// Create a DataRow using the DataTable defined in the
// CreateDataSource method.
DataRow dr = dt.NewRow();
// This DataRow contains the ColorTextField and ColorValueField
// fields, as defined in the CreateDataSource method. Set the
// fields with the appropriate value. Remember that column 0
// is defined as ColorTextField, and column 1 is defined as
// ColorValueField.
dr[0] = Text;
dr[1] = Value;
return dr;
}
void Selection_Change(Object sender, EventArgs e)
{
// Retrieve the DropDownList control from the Controls
// collection of the PlaceHolder control.
DropDownList DropList1 = (DropDownList)p1.FindControl("TrendList");
DropDownList DropList2 = (DropDownList)p2.FindControl("TrendList");
switch (sender.ToString())
{
case "p1":
s1.InnerHtml = DropList1.SelectedItem.Value;
break;
case "p2":
s2.InnerHtml = DropList2.SelectedItem.Value;
break;
}
}
And here is the relevant snippet from the table:
<td><span id="s1" runat="server"><asp:PlaceHolder ID="p1" runat="server"></asp:PlaceHolder></span>
<td><span id="s2" runat="server"><asp:PlaceHolder ID="p2" runat="server"></asp:PlaceHolder></span>
Now I realize that the switch control is all wrong since sender does not represent the id of the caller. But I need some way to distinguish which drop down menu is the caller so I know which HTML to replace. Also, I can only get one drop down menu to display at a time.
Any advice is appreciated.
Regards.
This code solved the problem:
public void EditTable()
{
ICollection trends = CreateDataSource();
for (int x = 1; x <= 27; x++)
{
DropDownList ddl = new DropDownList();
string index = x.ToString();
ddl.ID = "TrendList" + index;
ddl.AutoPostBack = true;
ddl.SelectedIndexChanged += new EventHandler(this.Selection_Change);
ddl.DataSource = trends;
ddl.DataTextField = "TrendTextField";
ddl.DataValueField = "TrendValueField";
ddl.DataBind();
if (!IsPostBack)
{
ddl.SelectedIndex = 0;
}
HtmlGenericControl span = (HtmlGenericControl)form1.FindControl("s" + index);
PlaceHolder placeHolder = (PlaceHolder)span.FindControl("p" + index);
if (placeHolder != null)
{
placeHolder.Controls.Add(ddl);
}
}
}
I have a ComboBox with its DataSource set to an instance of a DataTable. When rows are added to the DataTable, they show in the ComboBox without additional code, but when a row is deleted, the ComboBox remains unchanged. A short summary of my code:
ComboBox selector = new ComboBox();
DataTable tbl = new DataTable();
PopulateTable()
{
DataRow row1 = tbl.NewRow();
row1["field1"] = 1;
row1["field2"] = "Some Text";
tbl.Rows.Add(row1);
DataRow row2 = tbl.NewRow();
row2["field1"] = 2;
row2["field2"] = "More Text";
tbl.Rows.Add(row2);
}
PopulateSelector()
{
selector.DisplayMember = "field2";
selector.ValueMember = "field1";
selector.DataSource = tbl;
}
RemoveRow()
{
tbl.Rows[0].Delete();
}
At this point, the ComboBox appears to be correct, but clicking it resets it to its previous data. The DataTable remains correct, deleting the row causes no problem in that instance, I just can't make the ComboBox reflect the changes.
Try this:
PopulateSelector()
{
selector.DataSource = null;
selector.DisplayMember = "field2";
selector.ValueMember = "field1";
selector.DataSource = tbl;
}
RemoveRow()
{
tbl.Rows[0].Delete();
PopulateSelector()
}
You can use a bindingsource inbetween the datatable and the combobox and call ResetBindings
I'm using C#, winforms.
Got a small problem. I've debugged as much as possible that leads me to believe the code I'm using is causing the problem.
Ok so I have a combo box that is filled with data from a query.
There are 2 columns "name", and "keycode". I display the name only using:
accCollection.DisplayMember = "name";
Then I use the following to get the keycode value that corresponds to the name.
string acct = accCollection.SelectedValue.ToString();
The problem I have is the keycode does NOT match the name. I though this may be my query so what I did was to display the query results in a data grid view JUST before I fill the comboBox. The data grid view displays the correct results which leads me to believe that I'm using the wrong code!
Hopefully it's a silly one line problem, if you guys want any more code let me know and I will edit this post.
UPDATE heres the code i forgot to mention, as you can see i already have assigned the data member
accCollection.DataSource = myTable;
accCollection.DisplayMember = "name";
accCollection.ValueMember = "keycode";
UPDATE:::: Ok some more info. the selected value should be 1557 which is the names account number. but i get 1855, which is a different account number. like i said the datatable is correct....this is why im sooooooo confused!
UPDATE:: heres some code so you can see how i update the combo box with the info!
SqlCommand accountFill = new SqlCommand("SELECT name, keycode FROM dbo.Customer", conn1);
SqlDataAdapter readacc = new SqlDataAdapter(accountFill);
DataTable dt = new DataTable();
readacc.Fill(dt);
dataGridView3.DataSource = dt;
conn1.Close();
accCollection.DataSource = dt;
accCollection.DisplayMember = "name";
accCollection.ValueMember = "keycode";
then i pass the following into my task that calls my other query.
private void button1_Click_1(object sender, EventArgs e)
{
checkBox1.Checked = true;
string acct = accCollection.SelectedValue.ToString();
Task t = new Task(() => GetsalesFigures(acct));
t.Start();
}
UPDATE:: just to show the data i get!
ok so after some help with debugging, ive used the follwing code to get these resuults.
var result = accCollection.SelectedValue;
if (result != null)
{
MessageBox.Show(result.ToString());
}
this = 1885 which is wrong
BUT when i do this.
DataRowView row = (DataRowView)accCollection.SelectedItem;
if (row != null)
{
MessageBox.Show(row[0] + " " + row[1]);
}
its shows the correct data, "melksham" "1557"
why is this?
You may use SelectedValue or SelectedItem property but also you have to check whether the returned value is null or not.
If you bind the DataTable then the SelectedItem property return DataRowView.
DataRowView row = (DataRowView)accCollection.SelectedItem;
if(row!=null)
{
MessageBox.Show(row[0] + " " + row[1]);
}
In case of SelectedValue,
var result = accCollection.SelectedValue;
if (result != null)
{
MessageBox.Show(result.ToString());
}
EDIT:
Code in Form_Load event:
private void Form1_Load(object sender, EventArgs e)
{
DataTable dt = new DataTable();
dt.Columns.Add("A1", typeof(int));
dt.Columns.Add("A2");
dt.Rows.Add(1, "A");
dt.Rows.Add(2, "B");
comboBox1.DataSource = dt;
comboBox1.DisplayMember = "A2";
comboBox1.ValueMember = "A1";
}
Code in Click handler of Button:
var result = comboBox1.SelectedValue;
if (result != null)
{
MessageBox.Show(result.ToString());
}
DataRowView row = (DataRowView)comboBox1.SelectedItem;
if (row != null)
{
MessageBox.Show(row[0] + " " + row[1]);
}
Several Things:
DisplayMember property should be the name of the column to be displayed inside the combobox.
ValueMember property should be the name of the column which is the value of the item.
accCollection.DisplayMember = "name";
accCollection.ValueMember = "key";
If you want the value of the selected item you should use:
string acct = accCollection.SelectedValue.ToString();
Get the Display text as :
string acct = accCollection.SelectedText;
See this for details .
I have created a DataTable & i used to bind the dataTable to the combobox to display the title types and i wanted to get the title id which is in the datatable to a variable.
cmbTitle.DataSource=dataTable;
cmbTitle.DisplayMember="title_type";
cmbTitle.ValueMember="id";
then on the selection changed event of cmbtitle, i got the selected value member to a string and parsed in into an integer to save it back for the database as it is a foreign key.
private void cmb_title_SelectedIndexChanged(object sender, EventArgs e)
{
string userTitle = cmb_title.SelectedValue.ToString();
int.TryParse(userTitle, out this.userTitle);
MessageBox.Show(this.userTitle.ToString());
}