Can't bind a TextBox to a BindingSource - c#

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))

Related

How to get ValueMember value from ComboBox C#?

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;
}
}
}

listbox error: Items collection cannot be modified when the DataSource property is set

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();
}
}

save usercontrol textbox content back to its bindingsource

I am working in Winforms and now got stuck; I have created a custom usercontrol for textbox as shown in the code below:
public partial class TextBox : UserControl
{
public TextBox()
{
InitializeComponent();
txtTEXT.Text = "";
}
[DefaultValue("Text")]
public string Caption
{
get { return lblCAPTION.Text; }
set { lblCAPTION.Text = value;}
}
[Browsable(true)]
public new string Text
{
get { return txtTEXT.Text; }
set { txtTEXT.Text = value; }
}
[Browsable(true),DefaultValue(100)]
public int Caption_Width
{
get { return lblCAPTION.Width; }
set { lblCAPTION.Width = value; }
}
private void txtTEXT_TextChanged(object sender, EventArgs e)
{
if (this.txtTEXT.Text.StartsWith("textBox"))
this.txtTEXT.Text = "";
}
public new BorderStyle BorderStyle
{
get { return txtTEXT.BorderStyle; }
set { txtTEXT.BorderStyle = value; }
}
public bool PasswordChar
{
get { return txtTEXT.UseSystemPasswordChar; }
set {txtTEXT.UseSystemPasswordChar = value ;}
}
public bool m_Mandatory = false;
public bool Mandatory
{
get { return m_Mandatory; }
set
{
m_Mandatory= value;
lblAMENDATORY.Visible = m_Mandatory;
}
}
private void TextBox_Resize(object sender, EventArgs e)
{
this.Height = txtTEXT.Height;
}
}
Then in another form, I have binded this textbox as in the code below:
BindingSource bs = new BindingSource();
BindingSource bsDet = new BindingSource();
DataSet data;
SqlDataAdapter masterDataAdapter;
SqlDataAdapter detailsDataAdapter;
SqlConnection connection = new SqlConnection(connectionString);
// Create a DataSet.
data = new DataSet();
// Add data from the Customers table to the DataSet.
masterDataAdapter = new SqlDataAdapter("select * from RFID_MAS", connection);
masterDataAdapter.Fill(data, "RFID_MAS");
// Add data from the Orders table to the DataSet.
detailsDataAdapter = new SqlDataAdapter("select * from RFID_DET", connection);
detailsDataAdapter.Fill(data, "RFID_DET");
// Establish a relationship between the two tables.
DataRelation relation = new DataRelation("MyRelation", data.Tables["RFID_MAS"].Columns["SEQ_NO"],data.Tables["RFID_DET"].Columns["MAS_SEQ"]);
data.Relations.Add(relation);
// Bind the master data connector to the Customers table.
bs.DataSource = data;
bs.DataMember = "RFID_MAS";
// Bind the details data connector to the master data connector,
// using the DataRelation name to filter the information in the
// details table based on the current row in the master table.
bsDet.DataSource = bs;
bsDet.DataMember = "MyRelation";
// textBox1 is default Windows TextBox
textBox1.DataBindings.Add(new Binding("Text", this.bs, "RFID_NO", true, DataSourceUpdateMode.OnValidation));
// textBox2 is my ouwn usercontrol textbox
textBox2.DataBindings.Add(new Binding("Text", this.bs, "SEQ_NO",true, DataSourceUpdateMode.OnValidation));
dataGridView2.DataSource = bsDet;
dataGridView1.AutoResizeColumns();
dataGridView2.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
bindingNavigator1.BindingSource = bs;
//bs.DataSource = dset.Tables[0];
//dataGridView1.DataSource = bs;
Then I am saving my textbox data as:
this.Validate();
this.bs.EndEdit();
SqlCommandBuilder cm = new SqlCommandBuilder(masterDataAdapter);
this.masterDataAdapter.Update(data.Tables[0]);
Now my problem here is that when I add a new record,
the data from TextBox1 (ie windows default textbox) get updated in the BindingSource,
but data from TextBox2 (ie my own usercontrol textbox) does not get updated in the Bindingsource.
Please Help me if I am missing out something.
Very thanks to you.

Dynamically update combobox based on user selection

I am not sure how postback works on WinForms, but I want to allow the ComboBox to update based on the user selection.
Currently when I change the selection of my first ComboBox, it doesn't change the items in the second dropdown. (only showing the first item by default)
In what ways can O alter this?
Code to what I have:
public ContentUploader()
{
InitializeComponent();
using (SqlConnection con = new SqlConnection(conString))
{
con.Open();
using (SqlDataAdapter sda = new SqlDataAdapter("SELECT ... re.OverallID = 1", conString))
{
DataTable dt = new DataTable();
sda.Fill(dt);
sections_drp.ValueMember = "ID";
sections_drp.DisplayMember = "DisplayName";
sections_drp.DataSource = dt;
}
}
sections_drp.SelectedIndexChanged += (o, e) => FillFirstChildren();
}
public void FillFirstChildren()
{
firstChild_drp.Items.Add("Select Item");
firstChild_drp.SelectedIndex = 0;
string sectionId = sections_drp.SelectedValue.ToString();
using (SqlConnection con = new SqlConnection(conString))
{
con.Open();
using (SqlDataAdapter sda = new SqlDataAdapter("SELECT ... em.ItemID = ("+ sectionId +")", conString))
{
DataTable dt = new DataTable();
sda.Fill(dt);
firstChild_drp.ValueMember = "ID";
firstChild_drp.DisplayMember = "DisplayName";
firstChild_drp.DataSource = dt;
}
}
FillSecondChildren();
}
Winforms does not contain a post back. You will need to tie to the SelectedIndexChanged (or Item or Value) event to filter your second dropdown.
Example:
public void FillFirstChildren()
{
//Your Fill Logic Here
...
//Call FillSecondChildren on selection change
firstChild_drop.SelectedIndexChanged += (o, e) => FillSecondChildren();
}

How to add an EventHandler for Child MenuItems click event to the following Dynamic Menu?

The below Dynamic menu shows data from database but I want to create an event handler for the child menu items created for handling the click event raised. Also please help me with the event handler code to track down which child menu is click (for example, based on the value of the child menu item).
public partial class _Default : System.Web.UI.Page {
string connect = WebConfigurationManager.ConnectionStrings["con"].ToString();
SqlConnection con1;
protected void Page_Load(object sender, EventArgs e) {
con1 = new SqlConnection(connect);
con1.Open();
if (!IsPostBack) {
PopulateMenuItems();
} else {
Label1.Text = Request.QueryString["str"].ToString();
}
}
private void PopulateMenuItems() {
DataTable dt_GetParentData = GetMenuData_Parent();
AddTopMenuItems(dt_GetParentData);
}
private DataTable GetMenuData_Child() {
using (SqlCommand cmd = new SqlCommand("SELECT Department, FirstName + ' ' + LastName as Name FROM Employees", con1)) {
SqlDataAdapter da_menuchild = new SqlDataAdapter(cmd);
DataTable dt_menuchild = new DataTable();
da_menuchild.Fill(dt_menuchild);
return dt_menuchild;
}
}
private DataTable GetMenuData_Parent() {
using(SqlCommand cmd1 = new SqlCommand("SELECT DISTINCT Department FROM Employees", con1)) {
SqlDataAdapter da_menuparent = new SqlDataAdapter(cmd1);
DataTable dt_menuparent = new DataTable();
da_menuparent.Fill(dt_menuparent);
return dt_menuparent;
}
}
private void AddTopMenuItems(DataTable dt_Parent) {
DataTable dtchild = GetMenuData_Child();
DataView parent_view = new DataView(dt_Parent);
foreach (DataRowView row in parent_view) {
MenuItem newParentItem = new MenuItem(row["Department"].ToString());
Menu2.Items.Add(newParentItem);
AddChildMenuItems(dtchild,newParentItem);
}
}
private void AddChildMenuItems(DataTable dtchild, MenuItem parentitem) {
string parent = parentitem.Value.ToString();
DataView childview = new DataView(dtchild);
childview.RowFilter = String.Format("Department='{0}'", parent);
foreach(DataRowView row1 in childview) {
MenuItem newChildItem = new MenuItem(row1["Name"].ToString());
parentitem.ChildItems.Add(newChildItem);
newChildItem.NavigateUrl = "Default2.aspx?str=" + newChildItem.Value.ToString();
}
}
Wouldn't you just handle Menu2.MenuItemClick event? http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.menu.menuitemclick.aspx
In the event args, you'd have access to the Item clicked so you could determine which one the user clicked and react accordingly.

Categories

Resources