I'm having some trouble trying to get the ValueMember value I've set. I'm trying to use a combobox to read XML data to textbox. Here's my code:
private void zamowienie_Load(object sender, EventArgs e)
{
label2.Text = DateTime.Now.ToShortDateString();
DataSet dsSet = new DataSet();
dsSet.ReadXml("E:\\baza\\spis_klientow.xml");
comboBox2.DataSource = dsSet.Tables["spis_klientow"];
comboBox2.DisplayMember = "ID";
comboBox2.ValueMember = "Name";
comboBox2.ValueMember = "Phone";
}
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
DataSet ds = new DataSet();
ds.ReadXml("E:\\baza\\spis_klientow.xml");
foreach (DataRow item in ds.Tables["spis_klientow"].Rows)
{
label10.Text = item[1].ToString();
label11.Text = item[2].ToString();
}
}
When you bind a DataTable as ComboBox data source, the ComboBox.Items collection is populated with DataRowView objects. Then you can use ComboBox.SelectedItem like this
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
var source = (DataRowView)comboBox2.SelectedItem;
label10.Text = source["Name"].ToString();
label11.Text = source["Phone"].ToString();
// ...
}
However, there is a better way. WinForms data binding infrastructure supports such scenario without a need to hook into a concrete control events. All you need is to bind combo box and text boxes to one and the same data source (DataTable in your case).
Here is a full working example of the data binding "magic" in action:
using System;
using System.Data;
using System.Windows.Forms;
namespace Samples
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form();
var comboBox = new ComboBox { Left = 16, Top = 16, DropDownStyle = ComboBoxStyle.DropDownList };
var textBoxID = new TextBox { Left = 16, Top = comboBox.Bottom + 8 };
var textBoxName = new TextBox { Left = 16, Top = textBoxID.Bottom + 8 };
var textBoxPhone = new TextBox { Left = 16, Top = textBoxName.Bottom + 8 };
form.Controls.AddRange(new Control[] { comboBox, textBoxID, textBoxName, textBoxPhone });
// Begin essential part
var dataSource = GetData();
textBoxID.DataBindings.Add("Text", dataSource, "ID");
textBoxName.DataBindings.Add("Text", dataSource, "Name");
textBoxPhone.DataBindings.Add("Text", dataSource, "Phone");
comboBox.DisplayMember = "Name";
comboBox.ValueMember = "ID";
comboBox.DataSource = dataSource;
// End essential part
Application.Run(form);
}
static DataTable GetData()
{
//DataSet dsSet = new DataSet();
//dsSet.ReadXml("E:\\baza\\spis_klientow.xml");
//return dsSet.Tables["spis_klientow"];
var table = new DataTable();
table.Columns.Add("ID", typeof(int));
table.Columns.Add("Name");
table.Columns.Add("Phone");
for (int i = 1; i <= 10; i++)
table.Rows.Add(i, "Name" + i, "Phone" + i);
return table;
}
}
}
Related
When I select a table from ComboBox I get every column that exists in that table(and a option to hide it when I check it).
When I select the next table from 'combobox' I get the new table columns plus the previous from the first table.
How can I fix that and save only the selected table columns?
private void ComboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
var tableName = comboBox1.SelectedItem;
sqlDa = new SqlDataAdapter($"SELECT * FROM {tableName}", form1.conn = new SqlConnection($"Server = {form1.ServerBox.Text }; Database = { form1.DBBox.Text}; Trusted_Connection = True"));
dataTable = new DataTable();
sqlDa.Fill(dataTable);
dataGridView1.DataSource = dataTable;
foreach (DataGridViewColumn column in dataGridView1.Columns)
{
var item = (ToolStripMenuItem)contextMenuStrip1.Items.Add(column.HeaderText);
var item2 = comboBox2.Items.Add(column.HeaderText);
item.Tag = column.Name;
item.Checked = column.Visible;
item.CheckOnClick = true;
item.CheckedChanged += (obj, args) =>
{
var i = (ToolStripMenuItem)obj;
dataGridView1.Columns[(string)i.Tag].Visible = i.Checked;
};
}
}
You need to clear the Items collection of contextMenuStrip1 before filling it. It could be simply done by contextMenuStrip1.Items.Clear(), however to prevent a probable handle/memory leak in your application, it's better to dispose the menu items right after removing them:
var items = contextMenuStrip1.Items.Cast<ToolStripItem>().ToList();
contextMenuStrip1.Items.Clear();
items.ForEach(x => x.Dispose());
Example
Here is a working example which needs a DataGridView, a ComboBox and a ContextMenuStrip. It loads tables of a database into the ComboBox and when you select an index from ComboBox, it loads data of the selected table into the DataGridView and if you right click on the column headers of the DataGridView, a ContextMenuStrip will be shown which allows you to set visibility of the columns:
string server = ".";
string db = "TestDB";
private void Form1_Load(object sender, System.EventArgs e)
{
var connection = $"SERVER={server};DATABASE={db};Integrated Security=true";
//Hanlde combo box selected index changed
comboBox1.SelectedIndexChanged += (obj1, args1) =>
{
dataGridView1.DataSource = null;
if (comboBox1.SelectedIndex > -1)
{
//Fill data table and show data in data grid view
var data = GetDataTable($"SELECT * FROM {comboBox1.SelectedValue}", connection);
dataGridView1.Columns.Clear();
dataGridView1.DataSource = data;
}
//Clear existing items of context menu strip
var items = contextMenuStrip1.Items.Cast<ToolStripItem>().ToList();
contextMenuStrip1.Items.Clear();
items.ForEach(x => x.Dispose());
//Add the columns to context menu strip
foreach (DataGridViewColumn c in dataGridView1.Columns)
{
var item = (ToolStripMenuItem)contextMenuStrip1.Items.Add(c.HeaderText);
item.Tag = c.Name;
item.Checked = c.Visible;
item.CheckOnClick = true;
//Hanlde CheckStateChanged event of context menu strip items
item.CheckStateChanged += (obj, args) =>
{
var i = (ToolStripMenuItem)obj;
dataGridView1.Columns[(string)i.Tag].Visible = i.Checked;
};
}
};
//Load table names
var tables = GetDataTable("SELECT Name FROM Sys.Tables", connection);
comboBox1.ValueMember = "Name";
comboBox1.DisplayMember = "Name";
comboBox1.DataSource = tables;
//Show context menu strip on right click on data grid veiw header
dataGridView1.CellMouseClick += (obj, args) =>
{
if (args.RowIndex == -1 && args.Button == MouseButtons.Right)
contextMenuStrip1.Show(Cursor.Position);
};
}
DataTable GetDataTable(string commandText, string connectionString)
{
using (var da = new SqlDataAdapter(commandText, connectionString))
{
var dt = new DataTable();
da.Fill(dt);
return dt;
}
}
I have a DatagridView contains students information, including class, age, etc. now I want to create a ComboBox that have function to filter which class (for example if I select class IIA on combobox, the datagridview only show students from class IIA).
Here is my code in the form to load the data from objectbindingsource:
private void frmdbSiswa_Load(object sender, EventArgs e)
{
db = new SiswaSMSEntities();
tabelSiswaBindingSource.DataSource = db.Tabel_Siswa.ToList();
agamaSiswaBindingSource.DataSource = db.Agama_Siswa.ToList();
kelasBindingSource.DataSource = db.Kelas.ToList();
jenisKelaminBindingSource.DataSource = db.Jenis_Kelamin.ToList();
dataGridViewSiswa.DataSource = db.Tabel_Siswa.ToList();//to show the datagridview
cboKelas.DataSource = db.Kelas.ToList();//combobox
}
and here is the code for combobox:
private void cboKelas_SelectionChangeCommitted(object sender, EventArgs e)
{
dataGridViewSiswa.DataSource = db.Tabel_Siswa.Where(x => x.IdKelas == cboKelas.SelectedIndex).ToList();
}
I also bind the combobox to the datasource.
I am so confuse for hours working on this. I am new to programming, so please forgive me if I am asking a very basic question.
The problem is, when I run the code, it does filter the data, but when I select the class IA, datagridview shows nothing, and when I select class IB, the datagridview shows students from class IA and so on. and also when I select the datagridview, combobox on show system.Data.Entity.Error.
Upon combo box selection changed event, set data grid view RowFilter property to selected value :
dv.RowFilter = "YourColumnName = ComboBoxSelectedValue";
Updating this with similar problem/solution here
Repalce this Add !IsPostBack in Page Load
private void frmdbSiswa_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
db = new SiswaSMSEntities();
tabelSiswaBindingSource.DataSource = db.Tabel_Siswa.ToList();
agamaSiswaBindingSource.DataSource = db.Agama_Siswa.ToList();
kelasBindingSource.DataSource = db.Kelas.ToList();
jenisKelaminBindingSource.DataSource = db.Jenis_Kelamin.ToList();
dataGridViewSiswa.DataSource = db.Tabel_Siswa.ToList();//to show the datagridview
dataGridViewSiswa.DataBind();
cboKelas.DataSource = db.Kelas.ToList();//combobox
cboKelas.DataBind();
}
}
private void cboKelas_SelectionChangeCommitted(object sender, EventArgs e)
{
dataGridViewSiswa.DataSource = db.Tabel_Siswa.Where(x => x.IdKelas == cboKelas.SelectedIndex).ToList();
dataGridViewSiswa.DataBind();
}
Can you try it like this?
using System;
using System.Data;
using System.Windows.Forms;
using System.Data.SqlClient;
namespace WindowsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
dataGridView1.ColumnCount = 3;
dataGridView1.Columns[0].Name = "Product ID";
dataGridView1.Columns[1].Name = "Product Name";
dataGridView1.Columns[2].Name = "Product Price";
string[] row = new string[] { "1", "Product 1", "1000" };
dataGridView1.Rows.Add(row);
row = new string[] { "2", "Product 2", "2000" };
dataGridView1.Rows.Add(row);
row = new string[] { "3", "Product 3", "3000" };
dataGridView1.Rows.Add(row);
row = new string[] { "4", "Product 4", "4000" };
dataGridView1.Rows.Add(row);
DataGridViewComboBoxColumn cmb = new DataGridViewComboBoxColumn();
cmb.HeaderText = "Select Data";
cmb.Name = "cmb";
cmb.MaxDropDownItems = 4;
cmb.Items.Add("True");
cmb.Items.Add("False");
dataGridView1.Columns.Add(cmb);
}
}
}
Post back if you have additional questions.
http://csharp.net-informations.com/datagridview/csharp-datagridview-combobox.htm
Unless you absolutely have to have a List<T> as a DataSource to the grid, I suggest you use a DataTable as a DataSource to the grid. It may make things easier and it appears you may already have the DataTable from db.Tabel_Siswa.ToList() which appears to be making a list FROM a table.
With that said, below is an example of using a combo box to filter the data in a grid using a DataTable and a DataView. It is a simple form with an empty DataGridView and an empty ComboBox. A global DataTable variable gridTable is used as a DataSource to the grid. A global DataView variable dv is used to “filter/unFilter” the gridTable.
Upon form load, columns and some test data is added to the gridTable. Then the gridTable is set as a DataSource to the grid. Lastly the ComboBox is filled with items “Class A, Class B” etc… A “No Filter” selection is added to the combo box to allow the user to remove an applied filter.
When the user changes the combo box selection, a new DataView is created and it’s RowFilter is set to the selected value in the combo box. Finally, the DataView dv is set as a DataSource to the grid to display the filtered results
DataTable gridTable;
DataView dv;
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
gridTable = GetTable();
FillTable(gridTable);
dataGridView1.DataSource = gridTable;
SetComboBox();
}
private void SetComboBox() {
comboBox1.Items.Add("No Filter");
comboBox1.Items.Add("Class A");
comboBox1.Items.Add("Class B");
comboBox1.Items.Add("Class C");
comboBox1.SelectedIndex = 0;
}
private DataTable GetTable() {
DataTable dt = new DataTable();
dt.Columns.Add("FName", typeof(string));
dt.Columns.Add("LName", typeof(string));
dt.Columns.Add("Class", typeof(string));
return dt;
}
private void FillTable(DataTable dt) {
for (int i = 1; i < 5; i++) {
dt.Rows.Add("FName" + i, "LName" + i, "Class A");
dt.Rows.Add("FName" + i, "LName" + i, "Class B");
dt.Rows.Add("Class" + i, "Class" + i, "Class C");
}
}
The RowFilter string… dv.RowFilter = "Class = '" + comboBox1.Text + "'" says to filter such that the cells in the column named “Class” equals the text in the combo box. Example: “Class = Class B”. This implies that the DataSource has a column name that “matches” what’s in the filter string.
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) {
dv = new DataView(gridTable);
if (comboBox1.Text == "No Filter")
dv.RowFilter = "";
else
dv.RowFilter = "Class = '" + comboBox1.Text + "'";
dataGridView1.DataSource = dv;
}
Hope this helps.
I have a Windows Form with two comboBoxes, each one is populated using a DataSet and a TableAdapter. ComboBox1 contains Employee's names and ComboBox2 contains the territories assigned to all employees. I would like to be able to select an employee's name and filter the ComboBox2 to display only the employee's assigned territories.
Here is what I got so far:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Prueba2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void LlenaEmpleados()
{
DataSet1 ds = new DataSet1();
DataSet1TableAdapters.EmployeesTableAdapter Adapter = new DataSet1TableAdapters.EmployeesTableAdapter();
Adapter.FillEmployees(ds.Employees);
ds.Employees.Columns.Add("FullName", typeof(string), "FirstName +' '+ LastName");
cbPrimero.DataSource = ds.Tables["Employees"];
cbPrimero.DisplayMember = "FullName";
cbPrimero.ValueMember = "EmployeeID";
}
public void LlenaTerritorios()
{
DataSet1 ds = new DataSet1();
DataSet1TableAdapters.TerrioriosTableAdapter Adapter = new DataSet1TableAdapters.TerrioriosTableAdapter();
Adapter.FillTerritorios(ds.Territorios);
cbSegundo.DataSource = ds.Tables["Territorios"];
cbSegundo.DisplayMember = "TerritoryDescription";
cbSegundo.ValueMember = "EmployeeID";
}
private void Form1_Load(object sender, EventArgs e)
{
LlenaEmpleados();
cbPrimero.Text = "";
LlenaTerritorios();
cbSegundo.Text = "";
}
private void cbPrimero_SelectedIndexChanged(object sender, EventArgs e)
{
cbPrimero.AutoCompleteSource = AutoCompleteSource.ListItems;
cbPrimero.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
}
}
}
I have searched for the solution, But they do it with a SQL query not using the DataSet.
the easy way is to use DataView as the source for the second ComboBox, and then filter it:
DataTable tab1 = new DataTable();
tab1.Columns.Add("Name",typeof(string));
tab1.Columns.Add("Value", typeof(string));
tab1.Columns.Add("ID", typeof(int));
tab1.Rows.Add("name1", "value1", 1);
tab1.Rows.Add("name2", "value2", 2);
tab1.Rows.Add("name3", "value3", 3);
comboBox1.DataSource = tab1;
comboBox1.ValueMember = "Value";
comboBox1.DisplayMember = "Name";
DataTable tab2 = new DataTable();
tab2.Columns.Add("Name", typeof(string));
tab2.Columns.Add("Value", typeof(string));
tab2.Columns.Add("Tab1_ID", typeof(int));
tab2.Rows.Add("1_name1", "_value1", 1);
tab2.Rows.Add("1_name2", "_value2", 1);
tab2.Rows.Add("1_name3", "_value3", 1);
tab2.Rows.Add("2_name1", "_value1", 2);
tab2.Rows.Add("2_name2", "_value2", 2);
tab2.Rows.Add("2_name3", "_value3", 2);
tab2.Rows.Add("3_name1", "_value1", 3);
tab2.Rows.Add("3_name2", "_value2", 3);
// here use DataView instaed of directly using the table:
comboBox2.DataSource = tab2.DefaultView;
comboBox2.ValueMember = "Value";
comboBox2.DisplayMember = "Name";
comboBox1.SelectedIndexChanged += new EventHandler(comboBox1_SelectedIndexChanged);
comboBox1.SelectedIndex = 0;
and:
void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex != -1 && comboBox1.SelectedItem != null)
{
(comboBox2.DataSource as DataView).RowFilter = "Tab1_ID=" + (comboBox1.SelectedItem as DataRowView).Row["ID"];
}
}
I have 2 listboxes in a window form, one on left and one on right. The
1st listbox have some items while the 2nd listbox is empty. Also there
are 2 buttons between the 2 listboxes which used to move item from/to
the 1st and 2nd listbox
My problem here is that after I bind the data to the 1st listbox (from
a database, using DisplayMember and ValueMember) , and I try to move 1
of the item from this 1st listbox to the 2nd listbox and I want that
the selected item is also removed from the 1st listbox by:
private void btnMoveRight_Click(object sender, EventArgs e)
{
ADD();
}
private void ADD()
{
int c = listJobBox.Items.Count - 1;
` for(int i= c; i>=0; i--)
{
if(listJobBox.GetSelected(i))
{
lstAssignedJobs.Items.Add(listJobBox.Items[i]);
listJobBox.Items.Remove(listJobBox.SelectedItem); ---error line
But the selected item is not removed from the 1st listbox.
it displays error message "Items collection cannot be modified
when the DataSource property is set."
can any one give me the solution to my problem.
Add a boolean column to your DataTable object, something like IsSelected.
Then instead of binding your listbox1 directly to the table, bind it to a BindingSource. Add 2 bindingsources to your form using the designer. And place this code in your code behind file.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.InitializeDataObjects();
}
private void InitializeDataObjects()
{
this.InitData();
this.InitBindingSources();
}
private void InitData()
{
ds = new DataSet();
var dt = new DataTable("Table1");
dt.Columns.Add("Name", typeof(string));
ds.Tables.Add(dt);
}
private void InitBindingSources()
{
bindingSource1 = new BindingSource();
bindingSource2 = new BindingSource();
bindingSource1.DataSource = ds;
bindingSource1.DataMember = "Table1";
bindingSource2.DataSource = ds;
bindingSource2.DataMember = "Table1";
listBox1.DataSource = bindingSource1;
listBox1.DisplayMember = "Name";
listBox2.DataSource = bindingSource2;
listBox2.DisplayMember = "Name";
}
}
Then when you load your data, do the following:
private void btnLoadAndBind_Click(object sender, EventArgs e)
{
this.FetchData(this.ds.Tables["Table1"]);
this.AddSelectedColumn(this.ds.Tables["Table1"]);
this.bindingSource1.Filter = "IsSelected = false";
this.bindingSource2.Filter = "IsSelected = true";
}
private void FetchData(DataTable dataTable)
{
string CS = "your connectionstring";
using (SqlConnection con = new SqlConnection(CS))
{
try
{
SqlDataAdapter da = new SqlDataAdapter();
con.Open();
var sqlcmd = new SqlCommand("SELECT Name FROM sometable", con);
sqlcmd.CommandType = CommandType.Text;
da.SelectCommand = sqlcmd;
da.Fill(dataTable);
}
catch (Exception ex)
{
MessageBox.Show("exception raised");
throw ex;
}
}
}
private void AddSelectedColumn(DataTable suppliersDataTable)
{
var dc = new DataColumn("IsSelected", typeof(bool));
suppliersDataTable.Columns.Add(dc);
foreach (DataRow dr in suppliersDataTable.Rows)
{
dr["IsSelected"] = false;
}
}
Now your listboxes are both connected to the same datatable and filtered based on the IsSelected property / column. Just set this column to true or false and it will flip from box to box. Your eventhandler of a button could look like this:
public void button_Click(object sender, EventArgs e)
{
if (this.bindingSource1.Current!= null)
{
var dr = ((DataRowView)this.bindingSource1.Current).Row;
dr["IsSelected"] = true;
}
}
This works!
Things will be become much simpeler if you use a typed dataset. Most of the bindings then can be done in the designer and your code behind will shrink to 20 lines of code....
Lets say listbox1 is bound to datatable1 (it could be any other collection type) and listbox2 is bound to datatable2. When you click on move button, remove the selected item from the collection i.e datatable1 and add that item to other collection i.e. datatable2 and re-bind the listbox1 and lisbox2.
Here is a rough working example:
public partial class Form1 : Form
{
private DataTable _dataSource1;
private DataTable _dataSource2;
public Form1()
{
InitializeComponent();
_dataSource1 = GetData1();
_dataSource2 = GetData2();
Initialize();
}
private void btnMove_Click(object sender, EventArgs e)
{
MoveItem();
}
void Initialize()
{
listBox1.DataSource = _dataSource1;
listBox1.DisplayMember = "Fruits";
listBox1.ValueMember = "Fruits";
listBox2.DataSource = _dataSource2;
listBox2.DisplayMember = "Fruits";
listBox2.ValueMember = "Fruits";
}
DataTable GetData1()
{
var dt = new DataTable();
dt.Columns.Add("Fruits");
dt.Rows.Add(new object[] {"Apple"});
dt.Rows.Add(new object[] { "Orange" });
dt.Rows.Add(new object[] { "Grapes" });
return dt;
}
DataTable GetData2()
{
var dt = new DataTable();
dt.Columns.Add("Fruits");
return dt;
}
void MoveItem()
{
var index = listBox1.SelectedIndex;
var dataRowToRemove = _dataSource1.Rows[index];
var listItem = dataRowToRemove[0] as string;
_dataSource1.Rows.Remove(dataRowToRemove);
var dataRowToAdd = _dataSource2.NewRow();
dataRowToAdd[0] = listItem;
_dataSource2.Rows.Add(dataRowToAdd);
Initialize();
}
}
I cannot seem to bind to a TextBox. Here is my source:
public partial class formAirFreightLabels : Form
{
int pfDeclarationUID = -1;
BindingNavigator bindingNavigatorMain = new BindingNavigator();
BindingSource bindingSourceMain = new BindingSource();
public formAirFreightLabels(int pvDeclarationUID)
{
InitializeComponent();
pfDeclarationUID = pvDeclarationUID;
this.bindingNavigatorMain.BindingSource = this.bindingSourceMain;
this.bindingNavigatorMain.Dock = DockStyle.Bottom;
this.Controls.Add(this.bindingNavigatorMain);
this.Load += new EventHandler(formAirFreightLabels_Load);
}
void formAirFreightLabels_Load(object sender, EventArgs e)
{
SqlConnection cn = Program.GetSQLConnection();
if (cn != null)
{
SqlCommand cmd = new SqlCommand(String.Format("SELECT ShipperName, ShipperAddress, ConsigneeName, ConsigneeAddress FROM Declarations WHERE DeclarationUID={0}", pfDeclarationUID), cn);
SqlDataReader r = cmd.ExecuteReader(CommandBehavior.CloseConnection);
DataSet ds = new DataSet("Declarations");
ds.Load(r, LoadOption.OverwriteChanges, new string[] { "Declarations" });
bindingSourceMain.DataSource = ds;
textBoxShipperName.DataBindings.Add(new Binding("Text", bindingSourceMain, "ShipperName", true));
}
}
}
I keep getting a run-time error as follows:
Cannot bind to the property or column ShipperName on the DataSource.
Parameter name: dataMember
As msdn says, you need to add TableName.ColumnName in order bind the controls. Windows Form Binding , you may try like this.
textBoxShipperName.DataBindings.Add(new Binding("Text", bindingSourceMain, "Declarations.ShipperName", true))