I am working on a Windows application where I get an input from a TextBox and add it to the DataGridView when user clicks on a Button (Add).
My problem is that when I add the text for the first time, it works fine and its added to the grid.
When I add new text, it's not added to the DataGridView. Once the Form is closed and reopened with the same object then I am able to see it.
Code:
private void btnAddInput_Click(object sender, EventArgs e)
{
if (Data == null)
Data = new List<Inputs>();
if (!string.IsNullOrWhiteSpace(txtInput.Text))
{
Data.Insert(Data.Count, new Inputs()
{
Name = txtInput.Text,
Value = string.Empty
});
}
else
{
MessageBox.Show("Please enter parameter value", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
txtInput.Text = "";
gridViewInputs.DataSource = Data;
}
I am not sure what is causing the record not to be added to grid on second add button click.
You could set the DataGridView.DataSource to null before setting a new one.
This would cause the DataGridView to refresh its content with the new data in the source List<Inputs>:
the underlying DataGridViewDataConnection is reset only when the DataSource reference is different from the current or is set to null.
Note that when you reset the DataSource, the RowsRemoved event is raised multiple times (once for each row removed).
I suggest to change the List to a BindingList, because any change to the List will be reflected automatically and because it will allow to remove rows from the DataGridView if/when required: using a List<T> as DataSource will not allow to remove a row.
BindingList<Inputs> InputData = new BindingList<Inputs>();
You can always set the AllowUserToDeleteRows and AllowUserToAddRows properties to false if you don't want your Users to tamper with the grid content.
For example:
public class Inputs
{
public string Name { get; set; }
public string Value { get; set; }
}
internal BindingList<Inputs> InputData = new BindingList<Inputs>();
private void Form1_Load(object sender, EventArgs e)
{
dataGridView1.DataSource = InputData;
}
private void btnAddInput_Click(object sender, EventArgs e)
{
string textValue = txtInput.Text.Trim();
if (!string.IsNullOrEmpty(textValue))
{
InputData.Add(new Inputs() {
Name = textValue,
Value = "[Whatever this is]"
});
txtInput.Text = "";
}
else
{
MessageBox.Show("Not a valid value");
}
}
If you want to keep using a List<T>, add the code required to reset the DataGridView.DataSource:
private void btnAddInput_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(textValue))
{
//(...)
dataGridView1.DataSource = null;
dataGridView1.DataSource = InputData;
txtInput.Text = "";
}
//(...)
Related
I have a form application where multiple DataGridView objects are to be displayed (but not at once). They should be created on top of each other and it should then be possible to toggle the displayed DataGridView using a ComboBox.
I have a function which should create new DataGridView every time its called and then adds the name to the ComboBox:
private void readCSV(string DBname)
{
DataGridView tagDBname = new DataGridView();
tagDBname.Location = new System.Drawing.Point(24, 260);
tagDBname.Name = DBname;
tagDBname.Size = new System.Drawing.Size(551, 217);
tagDBname.TabIndex = 6;
tagDBname.Columns.Add("Column1", "Col1");
tagDBname.Columns.Add("Column2", "Col2");
tagDBname.Visible = false;
comboBoxTag.Items.Add(DBname);
}
Then I would like to change the visibility state of a DataGridView given the selected name from the ComboBox. This should be done in the function called when the index changes:
private void comboBoxTag_SelectedIndexChanged(object sender, EventArgs e)
{
// Get the name of the DataGridView which should be visible:
string selectedTagDB = comboBoxTagDatabases.SelectedItem.ToString();
DataGridView tagDatabase = ? // Here the DataGridView should be selected given the name "selectedTagDB"
tagDatabase.Visible = true;
}
In the above, I do not know how to assign the DataGridView only given its name. Any help would be appreciated - even if it means that the selected approach is inappropriate of what I am trying to achieve. If the question is answered elsewhere, feel free to guide me in the right direction :)
I would store the gridviews in a dictionary by using the DB name as key;
private readonly Dictionary<string, DataGridView> _tagDBs =
new Dictionary<string, DataGridView>();
private void readCSV(string DBname)
{
DataGridView tagDBname = new DataGridView();
// Add the gridview to the dictionary.
_tagDBs.Add(DBname, tagDBname);
tagDBname.Name = DBname;
tagDBname.Location = new System.Drawing.Point(24, 260);
tagDBname.Size = new System.Drawing.Size(551, 217);
tagDBname.TabIndex = 6;
tagDBname.Columns.Add("Column1", "Col1");
tagDBname.Columns.Add("Column2", "Col2");
tagDBname.Visible = false;
this.Controls.Add(tagDBname); // Add the gridview to the form ot to a control.
comboBoxTag.Items.Add(DBname);
}
private void comboBoxTag_SelectedIndexChanged(object sender, EventArgs e)
{
// Get the name of the DataGridView which should be visible:
string selectedTagDB = comboBoxTagDatabases.SelectedItem.ToString();
foreach (DataGridView dgv in _tagDBs.Values) {
dgv.Visible = dgv.Name == selectedTagDB; // Hide all gridviews except the selected one.
}
}
If you need to do something with the selected gridview, you can get it with:
if (_tagDBs.TryGetValue(selectedTagDB, out DataGridView tagDatabase)) {
// do something with tagDatabase.
}
Note: you must add the gridview to the form or to a container control on the form. E.g.
this.Controls.Add(tagDBname);
You can loop through all DataGridViews of the form to display the expected one using its name, while hidding the others ones.
This solution isn't pretty but works
private void ShowOneDataGridViewAndHideOthers(string name)
{
foreach (var DGV in this.Controls.OfType<DataGridView>())
{
DGV.Visible = DGV.Name == name;
}
}
And call it this way :
private void comboBoxTag_SelectedIndexChanged(object sender, EventArgs e)
{
// Get the name of the DataGridView which should be visible:
string selectedTagDB = comboBoxTagDatabases.SelectedItem.ToString();
ShowOneDataGridViewAndHideOthers(selectedTagDB);
}
The method can be made a bit more generic this way :
private void ShowOneControlAndHideOthers<T>(string name, Control controls) where T : Control
{
foreach (var control in controls.Controls.OfType<T>())
{
control.Visible = control.Name == name;
}
}
private void comboBoxTag_SelectedIndexChanged(object sender, EventArgs e)
{
// Get the name of the DataGridView which should be visible:
string selectedTagDB = comboBoxTagDatabases.SelectedItem.ToString();
ShowOneControlAndHideOthers<DataGridView>(selectedTagDB, this);
}
I am attempting to write code that will display data in a datagridview based off of the size of car selected in a combo box. When this code initially runs, it defaults to economy sized, and displays the correct information in the datagridview. However, when a different size is selected in the combo box, the text boxes update correctly while the datagridview remains the same. What can I do to make it update every time the combo box is changed? I thought the code in "private void cboSize_selectionChangeCommitted()" would accomplish this, but there was no change in the output.
namespace carForm
{
public partial class Form1 : Form
{
_Cars_1_DataSet cDataSet;
BindingSource sizeBindingSource;
BindingSource vehicleBindingSource;
CarsDataClass clsCarsData;
Boolean gridInitialized;
public Form1()
{
InitializeComponent();
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
private void Form1_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the '_Cars_1_DataSet.Reservations' table. You can move, or remove it, as needed.
this.reservationsTableAdapter.Fill(this._Cars_1_DataSet.Reservations);
// TODO: This line of code loads data into the '_Cars_1_DataSet.Vehicle' table. You can move, or remove it, as needed.
this.vehicleTableAdapter.Fill(this._Cars_1_DataSet.Vehicle);
// TODO: This line of code loads data into the '_Cars_1_DataSet.CarSize' table. You can move, or remove it, as needed.
this.carSizeTableAdapter.Fill(this._Cars_1_DataSet.CarSize);
clsCarsData = new CarsDataClass();
cDataSet = clsCarsData.GetDataSet();
//Binding source sizes
sizeBindingSource = new BindingSource();
sizeBindingSource.DataSource = cDataSet;
sizeBindingSource.DataMember = "CarSize";
//Binding source vehicles
vehicleBindingSource = new BindingSource();
vehicleBindingSource.DataSource = cDataSet;
vehicleBindingSource.DataMember = "Vehicle";
//Combo box
cboSize.DataSource = sizeBindingSource;
cboSize.DisplayMember = "Size";
cboSize.ValueMember = "SizeCode";
//bind other controls
txtDaily.DataBindings.Add("text", sizeBindingSource, "DailyRate");
txtMileage.DataBindings.Add("text", sizeBindingSource, "MileageRate");
//execute combo box
cboSize_SelectionChangeCommitted(cboSize, e);
}
private void cboSize_SelectionChangeCommitted(object sender, EventArgs e)
{
string carSelected;
carSelected = Convert.ToString(cboSize.SelectedValue);
if (!gridInitialized)
{
dgvVehicles.DataSource = vehicleBindingSource;
gridInitialized = true;
ChangeGridColumns();
}
vehicleBindingSource.Filter = "CarSize = '" + carSelected + "'";
}
private void ChangeGridColumns()
{
//Change column headers
//dgvVehicles.Columns["Inv_ID"].Visible = false;
}
}
}
Try using SelectedIndexChanged event from the events menu after clicking on the combobox in the design view.
This should populate in your code:
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Console.WriteLine("test!");
}
In there you can put this logic from your code:
string carSelected;
carSelected = Convert.ToString(cboSize.SelectedValue);
if (!gridInitialized)
{
dgvVehicles.DataSource = vehicleBindingSource;
gridInitialized = true;
ChangeGridColumns();
}
vehicleBindingSource.Filter = "CarSize = '" + carSelected + "'";
Use SelectedIndexChanged instead of SelectionChangeCommitted.
In my Windows application I had a job code combobox and when user selects a jobcode from the combobox it will get the corresponding data from database and will display it in a datagridview below the combobox. All is fine and I am able to load data corresponding to selected jobcode.
I used the this code
public void loadcompljobcodecombobox()
{
completedcobcodeadapterTableAdapter cmpltjbcd = new completedcobcodeadapterTableAdapter();
cmpltjbcd.Connection = new OleDbConnection(Program.ConnStr);
DataTable dt= cmpltjbcd.GetData(int.Parse(cmbcutcode.SelectedValue.ToString()));
if (dt.Rows.Count > 0)
{
cmbjobcode.ValueMember = "jobpk";
cmbjobcode.DisplayMember = "jobcode";
txtcompanyname.Text = "companyname";
cmbjobcode.DataSource = dt;
}
else
{
MessageBox.Show("NO JobCode to be invoiced");
}
}
private void cmbjobcode_SelectedValueChanged(object sender, EventArgs e)
{
tbltoinvoicedtableTableAdapter tbltoinvce = new tbltoinvoicedtableTableAdapter();
tbltoinvce.Connection = new OleDbConnection(Program.ConnStr);
if (cmbjobcode.SelectedValue != null)
{
DataTable dt = tbltoinvce.GetDataBy(int.Parse(cmbjobcode.SelectedValue.ToString()));
dataGridView1.DataSource = dt;
}
}
Now my requirement is user must be able to select more than one jobcode details at a time for invoicing i.e. if he selects one value from jobcode corresponding data should be added in datagridview and when he select another jobcode its corresponding data should be added as next row in the Datagridview.
I had tried very much and find no way can anyone suggest an idea or example
If I understand you correctly, I would try something like this. This isn't tested but it's an idea.
At Form Level:
private BindingList<DataRow> jobList;
Then to add to your current code...
private void cmbjobcode_SelectedValueChanged(object sender, EventArgs e)
{
tbltoinvoicedtableTableAdapter tbltoinvce = new tbltoinvoicedtableTableAdapter();
tbltoinvce.Connection = new OleDbConnection(Program.ConnStr);
if (cmbjobcode.SelectedValue != null)
{
DataRow job = tbltoinvce.GetDataBy(int.Parse(cmbjobcode.SelectedValue.ToString())).Rows[0];
if (jobList == null)
{
jobList = new BindingList<DataRow>();
jobList.Add(job);
dataGridView1.DataSource = jobList;
}
else
{
if (!jobList.Contains(job));
jobList.Add(job);
}
}
}
**Edit: This is assuming your job data contains only one row of data since your question asked for how to add "a row".
I need help to get a response when I click on an "Item" from a list view. Know that there is selectedindexchanged, but when I try to display a MessageBox so nothing happens, have tried lots of other things but have not managed to come up with something.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
...
while (reader.Read())
{
string alio = reader["fornamn"].ToString();
string efternamn = reader["efternamn"].ToString();
ListViewItem lvi = new ListViewItem(alio);
listView1.Items.Add(lvi);
lvi.SubItems.Add(efternamn);
}
}
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
}
}
Assuming that 81.private void listView1_SelectedIndexChanged is properly linked to the listview, you will need to query the listview to find out what's selected:
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
if(this.listView1.SelectedItems.Count == 0)
return;
string namn = this.listView1.SelectedItems[0].Text;
// Create the sql statement to retrieve details for the user
string sql = string.Format("select * from kunder where fornamn = '{0}', namn);
// do the same as you do to create a reader and update the controls.
}
Going by the term "when I try to display a MessageBox so nothing happens"\, I assume that you simply put MessageBox.Show("blah"); inside the event handler and never got it shown.
If that's the case, your event handler is not hooked properly to your form's list view. go back and see the text listView1_SelectedIndexChanged is anywhere to be found inside your Form1.Designer.cs file.
If not (or anyway), start over on a new form. That's the easiest way out. :)
private void lstView_KQ_SelectedIndexChanged(object sender, EventArgs e)
{
if (lstView_KQ.SelectedItems.Count > 0)
{
ListViewItem itiem = stView_KQ.SelectedItems[lstView_KQ.SelectedItems.Count - 1];
if (itiem != null)
foreach (ListViewItem lv in lstView_KQ.SelectedItems)
{
txtMaNV.Text = lv.SubItems[0].Text;
cmbCV.Text = lv.SubItems[1].Text;
txtHoNV.Text = lv.SubItems[2].Text;
txtTenNV.Text = lv.SubItems[3].Text;
txtNgaysinh.Text = lv.SubItems[4].Text;
txtGioiTinh.Text = lv.SubItems[5].Text;
txtDiaChi.Text = lv.SubItems[6].Text;
txtSDT.Text = lv.SubItems[7].Text;
txtCMND.Text = lv.SubItems[8].Text;
}
}
}
At my user-control I populate listbox with collection and want save data in viewstate\controlstate for further autopostback using.
protected void btFind_Click(object sender, EventArgs e)
{
var accounts = new AccountWrapper[2];
accounts[0] = new AccountWrapper { Id = 1, Name = "1" };
accounts[1] = new AccountWrapper { Id = 2, Name = "2" };
lbUsers.DataSource = accounts;
lbUsers.DataBind();
ViewState["data"] = accounts;
}
ListBox is populated at button click. When I save accounts to ViewState listBox is empty, when not it displays collection good. What's reasonn of this behaviour?
After your button is being clicked, PostBack occurs and ListBox loses it's state.
void lbUsers_DataBinding(object sender, EventArgs e)
{
if (this.IsPostBack &&)
{
AccountWrapper[] accounts = this.ViewState["data"] as AccountWrapper[];
if (accounts!= null)
{
lbUsers.DataSource = accounts;
lbUsers.DataBind();
}
}
}
(don't forget to subscribe to DataBinding event of your ListBox in markup)
Also I recommend you to encapsulate your access to ViewState:
private AccountWrapper[] Accounts
{
get { return this.ViewState["data"] as AccountWrapper[]; }
set { this.ViewState["data"] = value;
}