How to program Combox in dataGridView?
The following code does not lead to the result.
Data tables parsitsya in Datatable.
I want to make the "combox" displayed for the "Type" field
Code
DataTable dt;
OleDbConnection connection;
OleDbDataAdapter adapter;
OleDbCommandBuilder commandBuilder;
static string catBD = #"z:\vs\csharp\prb\db_GridVAccess.accdb";
string connectionString = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0}", catBD);
string sql = "SELECT * FROM tbl_01_Combox";
public Form5()
{
InitializeComponent();
using (OleDbConnection cn = new OleDbConnection())
{
connection = new OleDbConnection(connectionString);
connection.Open();
adapter = new OleDbDataAdapter(sql, connection);
commandBuilder = new OleDbCommandBuilder(adapter);
// На соновании DataTable
dt = new DataTable();
adapter.Fill(dt);
dataGridView1.DataSource = dt;
}
}
private void Form5_Load(object sender, EventArgs e)
{
// Ширина поля
dataGridView1.Columns[0].Width = 50;
// Комбокс
string[] countrys = new string[] { "США", "ОАЭ", "ЮАР" };
(dataGridView1.Columns[1] as DataGridViewComboBoxColumn).DataSource = countrys;
}
I believe the problem is your "countrys" isn't observable, plus it has to be a property.
Consider the following code:
private ObservableCollection<string> _Countries;
public ObservableCollection<string> Countries
{
get { return _Countries; }
set { _Countries = value; }
}
Initialize and set its contents in your Form5_Load and see if it works.
EDIT 1:
I was curious, so I actually created a project and tried to recreate the problem, and I believe the real problem here is that your DataGridView is populated according to the info you get from DB. I looked into it a bit further, and found this post on SO:
C# Replace default textbox in DataGridView with a Combobox
According to it, you can't change the type of the column to DataGridViewComboBoxColumn, after it's already been created as DataGridViewTextBoxColumn according to the data you got from DB. It also suggests two different approaches to the case, which do sound realistic. I hope you can figure it out, good luck.
EDIT 2:
Per your request, I'll try to figure the way to accomplish it with changing the data you get from DB.
In the following piece of code, you have a "dt" object which has data that you want to show in your DataGridView.
dt = new DataTable();
adapter.Fill(dt);
dataGridView1.DataSource = dt;
I've never done it myself, and I can't really recreate it anyhow fast, but I suggest you try and play with the contents of the object "dt". I suppose the code which happens in "adapter.Fill" creates the DataTable according to the data from db, you can try and add there some code which will add a List instead of a string for the "type" column (It will then help create a ComboBoxColumn instead of TextBoxColumn as far as I follow). I actually checked it our right now, and it looks to be a bit complicated, but I really don't know what's happening there in your program, it might just be as easy as it gets.
Anyway, I did try to do it another way, instead of changing the existing column type, I added a new DataGridViewComboBoxColumn of my own, to the end of the automatically populated DataGridView:
DataTable dt = new DataTable();
dataGridView2.DataSource = dt;
DataGridViewComboBoxColumn newColumn = new DataGridViewComboBoxColumn();
newColumn.DataSource = new List<string> { "asd", "qwe", "zxc" };
dataGridView2.Columns.Add(newColumn);
You then can just remove/delete the irrelevant "type" column that you got from db. But, then you'll face some other problems along the way, as connecting the selected object in combobox to the other data in the row. It's not complicated, but can get relatively ugly.
Edited:
If you have set the DataGridView.AutoGenerateColumns = true and the columns are generated automatically, the type of column "type" is not ComboBox. in that case you can create a new column, set it up and then replace the old column with it:
DataGridViewComboBoxColumn column = new DataGridViewComboBoxColumn();
int i = 0;
column.DataPropertyName = "type";
column.DataSource = countrys.Select(x => new { Key = i++, Value = x }).ToList();
column.DisplayMember = "Value";
column.ValueMember = "Key";
dataGridView1.Columns.RemoveAt(1);
dataGridView1.Columns.Insert(1, column);
the previous code didn't work because column[1] could not be cast into DataGridViewComboBoxColumn so (dataGridView1.Columns[1] as DataGridViewComboBoxColumn) would return a null and because you have put the code in Form_Load the Exception was omitted.
Original:
try this:
int i = 0;
(dataGridView1.Columns[1] as DataGridViewComboBoxColumn).DataSource = countrys.Select(x=> new {Key = i++, Value = x}).ToList();
(dataGridView1.Columns[1] as DataGridViewComboBoxColumn).DisplayMember = "Value";
(dataGridView1.Columns[1] as DataGridViewComboBoxColumn).ValueMember = "Key";
Related
I am in need of adding in the ALL option as the top choice of a combobox. I have tried this code below, but All is not addded, what must I alter in order to have that added?
string query = "select [empname] from [server].[dbo].[table]";
SqlDataAdapter da = new SqlDataAdapter(query, conn);
conn.Open();
DataSet ds = new DataSet();
da.Fill(ds, "tables");
cbotables.DisplayMember = "empname";
cbotables.Items.Insert(0, "All");
cbotables.DataSource = ds.Tables["tables"];
EDIT
I just realized a few things were not showing in my code...my connection string is declared above, and the contents of the combobox display as they should from the database, just no ALL option added.
Perhaps the simplest way would be to Insert a row into the DataTable assuming its only role is to be a DataSource:
// fill the datatable
dt.Load(cmd.ExecuteReader());
var dr = dt.NewRow();
dr["Id"] = -1;
dr["Name"] = "All";
dt.Rows.InsertAt(dr, 0);
cboEmp.DisplayMember = "Name";
cboEmp.ValueMember = "Id";
cboEmp.DataSource = dt;
It is more common to have a ValueMember with these so you can tell what was selected, thus the "Id" column. If the DataTable has other purposes you may not want to add fake data to it. For that, you could transfer the data to an anonymous list:
dt.Load(cmd.ExecuteReader());
// xfer data to anon collection
var myDS = dt.AsEnumerable()
.Select(q => new {Name = q.Field<String>("Name"),
Value = q.Field<Int32>("Id") }
)
.ToList();
// add ALL with fake id
myDS.Insert(0, new { Name = "ALL", Value = -1 });
cboEmp.DisplayMember = "Name";
cboEmp.ValueMember = "Value";
cboEmp.DataSource = myDS;
Either way gets the same results:
If you truly dont need the Id, you dont need an anon type and can just select the name and a List<string>
For example, I have a Combo box with these contents coming from the database using SqlCommand, SqlDataAdapter and DataTable:
Japan
UK
USA
Philippines
Now, I want to add another choices. It should look like this:
----ALL----
Japan
UK
USA
Philippines
If I chose Japan, it will filter and show all the landmarks in the country like Mt. Fuji, Tokyo Tower.
If I chose UK, it will filter and show Big Ben, Westminster, Olympic Stadium.
If I chose USA, it will show fat people, Kim Kardashian.
IF I CHOSE ----ALL----, IT WILL SHOW ALL THE LANDMARKS FROM ALL COUNTRIES.
How do I do this? I'm using C# & SQL Server.
EDIT:
This is how I fill a combo box.
_con.Open();
SqlCommand _viewLandmarks = new SqlCommand("dbo.SelectDevices_AddingForm", _con);
_viewDevices.CommandType = CommandType.StoredProcedure;
_viewDevices.ExecuteNonQuery();
SqlDataAdapter da = new SqlDataAdapter(_viewLandmarks);
DataTable dTable = new DataTable("LANDMARK");
da.Fill(dTable);
comboModel.DataSource = dTable;
comboModel.DisplayMember = "Landmark";
comboModel.ValueMember = "LandmarkID";
_con.Close();
As said in comments it is just a matter to add a fake record in your returned datatable
_con.Open();
SqlCommand _viewLandmarks = new SqlCommand("dbo.SelectDevices_AddingForm", _con);
_viewDevices.CommandType = CommandType.StoredProcedure;
_viewDevices.ExecuteNonQuery();
SqlDataAdapter da = new SqlDataAdapter(_viewLandmarks);
DataTable dTable = new DataTable("LANDMARK");
da.Fill(dTable);
DataRow fakeRow = dTable.NewRow();
fakeRow["Landmark"] = "(ALL)";
fakeRow["LandmarkID"] = -1;
dTable.Rows.Add(fakeRow);
comboModel.DataSource = dTable;
comboModel.DisplayMember = "Landmark";
comboModel.ValueMember = "LandmarkID";
_con.Close();
I have added the open/close parenthesys around ALL to to force the new added row to the beginning of your datatable (assuming that it is sorted by Landmark).
Notice also that this approach assumes also that your fields are all NULLABLE. If one or more fields doesn't allow null values you need to set an appropriate value before adding the fakeRow to the Datatale rows collection
Just add a row in your datatable before binding and set the value to All.
Below is a example of binding the combo box
private void BindCombo()
{
DataTable dataTable = GetDataTable();
DataRow row = dataTable.NewRow();
row["Value"] = "0";
row["Name"] = "All";
dataTable.Rows.InsertAt(row, 0);
comboBox1.DisplayMember = "Name";
comboBox1.ValueMember = "Value";
comboBox1.DataSource = dataTable;
}
I am trying to get one column from my datatable to be displayed as a combo box in my grid view. Also need to have the ability to fill the combo with a small collection to chose from. It will not show the values when i bind it from the Gui so I am trying it programmatic but cant seem to find the right code.
connection2 = new MySqlConnection(ConnectionString2);
try
{
string proid = txtbxProjId.Text;
//prepare query to get all records from items table
string query2 = "select sl.sample_id As sample_id, sl.sample_number As Sample_Number, sl.sample_date As Sample_Date, sl.sample_time As Sample_Time, sl.sample_comments As Sample_Comments FROM spt_sample_login sl Where project_id = '"+proid+"'";
//prepare adapter to run query
adapter2 = new MySqlDataAdapter(query2, connection2);
adapter3 = new MySqlDataAdapter(query2, connection2);
//create a DataTable to hold the query results
DataTable dTable2 = new DataTable();
DataSet DS2 = new DataSet();
//fill the DataTable
//get query results in dataset
adapter2.Fill(dTable2);
adapter3.Fill(DS2);
//return datatable with all records
//BindingSource to sync DataTable and DataGridView
//set the BindingSource DataSource
GridView1.DataSource = dTable2;
this.GridView1.Columns["sample_id"].Visible = false;
this.GridView1.Columns["Sample_Type"].DisplayIndex = 4;
this.GridView1.Columns["Sample_Type"].Visible = true;
//set the DataGridView DataSource
}
catch (MySqlException ex)
{
}
}
This works but shows the Sample_Type as a text box where i want it to be a combo with F,P Q,B as options.
Thank you
Brent
You have to put your ComboBox in a itemTemplate, and you have to fill it during rowDataBound event.
Quick exemple from my current project:
DropDownList ddlRole = (DropDownList)e.Row.FindControl("ddlRole");
ddlRole.DataSource = GetTruckersRoles();
string[] rolesList = Roles.GetRolesForUser((string)gvTruckers.DataKeys[e.Row.RowIndex][0]);
if (rolesList.Length > 0)
{
//If user is not in any roles, dont' do this
ddlRole.SelectedValue = rolesList[0];
}
ddlRole.DataBind();
Just adapt the code to your situation
I want to put a search option in DataGridView, i.e., User types the string or int in TextBox and similar records the DataGridView should be highlighted. I tried it using KeyPress event but didn't work.
if (Char.IsLetter(e.KeyChar))
{
for (int i = 0; i < (dgemployee.Rows.Count); i++)
{
if (dgemployee.Rows[i].Cells["Employee"].Value.ToString().
StartsWith(e.KeyChar.ToString(), true,
CultureInfo.InvariantCulture))
{
dgemployee.Rows[i].Cells[0].Selected = true;
return;
}
}
Actually, I need to search the whole DataGridView, not only one column and row. So any best solution?
If your DataGridView is bound to a DataTable or a DataView, you can do this:
Create a BindingSource and make BindingSource.DataSource the Datatable or DataView that your DGV is currently using. Then set your DataGridView.DataSource to the BindingSource. Then you can use the BindingSource.Filter property to query your datasource by setting the BindingSource.Filterto your query string which will automatically filter the DGV. You can find the syntax here - it is very similar to basic SQL queries, except you can only use wild cards on the beginning and end of the string.
Use the DataView.RowFilter property.
Also take a look at DataTable.DefaultView
Take a look at this article I wrote some time back on filtering data real time.
private void txtsearchgroup_KeyUp(object sender, KeyEventArgs e) {
SqlConnection objconnection = new SqlConnection(servername and ...);
DataView Dv = new DataView();
objcommand = new SqlCommand("select name from groupitems", objconnection);
objdataadapter = new SqlDataAdapter();
objdataadapter.SelectCommand = new SqlCommand();
objdataadapter.SelectCommand = objcommand;
objdataset = new DataSet();
objconnection.Open();
objdataadapter.Fill(objdataset);
Dv.Table = objdataset.Tables[0];
Dv.RowFilter = " name LIKE '%" + txtsearchgroup.Text + "%'";
dataGridView1.DataSource = Dv;
objconnection.Close(); }
txtsearchgroup : name of textbox for search word in datagridview1 and
txtsearchgroup_KeyUp : event of keyup for search and filter word in datagridview1 and
select name from groupitems : name is field for groupitems table and
Dv.Table = objdataset.Tables[0] : zero (0) is first table in dataset
all. I'm having a problem getting the GridView.AutoGenerateEditButton property to do anything for me. Right now, I set it to true, and I don't even get the button added to my GridView, so obviously I can't even get any further to code the actual events. Here is what I have so far (sorry for the messy code; I'm getting close to my deadline and it's been a bit frantic):
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = cmd;
using(SqlDataReader dr = cmd.ExecuteReader())
{
DataSet ds1 = new DataSet();
DataTable dt1 = ds1.Tables.Add("Specs 1");
if (dt1.Rows.Count == 0)
{
ds1.Tables["Specs 1"].Columns.Add("General Information");
ds1.Tables["Specs 1"].Columns.Add("Customer Name");
ds1.Tables["Specs 1"].Columns.Add("Address");
// etc.
}
while (dr.Read())
{
DataRow newRow = ds1.Tables["Specs 1"].NewRow();
int x = 0;
foreach (DataColumn thisColumn in ds1.Tables["Specs 1"].Columns)
{
newRow[ds1.Tables["Specs 1"].Columns[x]] = Convert.ToString(dr[x]);
x += 1;
}
ds1.Tables["Specs 1"].Rows.Add(newRow);
}
DataSet flipped_ds1 = FlipDataSet(ds1); // Flips the dataset's orientation.
Then, when it's time for me to add the GridView to the page, I use the following code:
GridView outputGrid1 = new GridView();
outputGrid1.ShowHeader = false; // Remove the integer headings.
outputGrid1.DataSource = flipped_ds1;
outputGrid1.DataBind();
outputGrid1.AutoGenerateEditButton = true;
Controls.Add(outputGrid1);
Now, the columns are being added exactly the way that I want, and the data is populating properly. However, I don't have the edit buttons that I assume I should have by setting the GridView.AutoGenerateEditButton property to true. I'm sure I'm missing something simple; can someone help? Thanks very much.
Have you tried setting the AutoGenerateEditButton before the call to DataBind() ? Is there a way to prevent updates in your DataSet (the introspection may detect it and avoid providing a useless button maybe) ?