I have the following code. It's a budget assistant program. User edits a table via textfields and "add row" button, saves it as .csv, then opens it next time to continue his editing. Right now the program saves and opens .csv files perfectly, but the problem is.. it doesn't allow editting after the .csv is loaded. As far as I can see it, the problem is that it creates a new table(dataset) when loading the .csv file, but I am not really sure. I would appreciate if you could give me any advice on how to proceed..
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Data.SqlClient;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;
using CsvHelper;
using ExcelDataReader;
using Microsoft.Office.Interop.Excel;
using DataTable = System.Data.DataTable;
using Excel = Microsoft.Office.Interop.Excel;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
DataSet ds = new DataSet();
DataTable budgetTable = new DataTable();
public Form1()
{
InitializeComponent();
DataTable budgetTable = ds.Tables.Add("MainTable");
budgetTable.Columns.Add("id", typeof(Int32));
budgetTable.Columns.Add("date", typeof(DateTime));
budgetTable.Columns.Add("type", typeof(String));
budgetTable.Columns.Add("name", typeof(String));
budgetTable.Columns.Add("expenses", typeof(Int32));
budgetTable.Columns.Add("income", typeof(Int32));
budgetTable.Columns.Add("saldo", typeof(Int32));
var date = DateTime.ParseExact("29MAR18", "ddMMMyy", CultureInfo.InvariantCulture);
DataRow row = budgetTable.NewRow();
row["id"] = "01";
row["date"] = date;
row["type"] = cbbxType.Text;
row["name"] = nameField.Text;
row["expenses"] = expenseField.Text;
row["income"] = incomeField.Text;
row["saldo"] = 0;
budgetTable.Rows.Add(row);
DtgTable.DataSource = budgetTable;
budgetTable.Rows.Clear();
}
//adds a row to the table
private void button1_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(cbbxType.Text) ||
string.IsNullOrWhiteSpace(expenseField.Text))
{
MessageBox.Show("'Type','Expence','Income' fields cannot be empty!");
}
else
budgetTable.Rows.Add(null, dateTime.Text, cbbxType.Text, nameField.Text, expenseField.Text, incomeField.Text);
}
//deletes everything from the table
private void btnDeleteItem_Click(object sender, EventArgs e)
{
budgetTable.Rows.Clear();
}
//deletes selected row from the table
private void button1_Click_1(object sender, EventArgs e)
{
foreach (DataGridViewRow row in DtgTable.SelectedRows)
{
budgetTable.Rows.RemoveAt(row.Index);
}
}
//enumerates ID values
private void dtgTable_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
}
//calculates saldo cell on a specified row (you have to click the saldo cell)
private void dtgTable_CellValidated(object sender, DataGridViewCellEventArgs e)
{
}
//calculates overall balance
private void btnCalcBalance_Click(object sender, EventArgs e)
{
}
//_______________________MenuStrip__________________________________________//
//Opening file WORKS //
//__________________________________________________________________________//
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
string FileName;
OpenFileDialog dialog = new OpenFileDialog();
dialog.Title = "Open CSV File";
dialog.Filter = "CSV Files (*.csv)|*.csv";
if (dialog.ShowDialog() == DialogResult.OK)
{
FileName = dialog.FileName;
}
else
{
return;
}
OleDbConnection conn = new OleDbConnection
("Provider=Microsoft.Jet.OleDb.4.0; Data Source = " +
Path.GetDirectoryName(FileName) +
"; Extended Properties = \"Text;HDR=YES;FMT=Delimited\"");
conn.Open();
OleDbDataAdapter adapter = new OleDbDataAdapter
("SELECT * FROM " + Path.GetFileName(FileName), conn);
DataSet ds = new DataSet("Temp");
adapter.Fill(ds);
conn.Close();
DtgTable.DataSource = ds;
DtgTable.DataMember = "Table";
}
//__________________________________________________________________________________
//Saving file to .csv WORKS
//___________________________________________________________________________________
public void writeCSV(DataGridView gridIn, string outputFile)
{
//test to see if the DataGridView has any rows
if (gridIn.RowCount > 0)
{
string value = "";
DataGridViewRow dr = new DataGridViewRow();
StreamWriter swOut = new StreamWriter(outputFile);
//write header rows to csv
for (int i = 0; i <= gridIn.Columns.Count - 1; i++)
{
if (i > 0)
{
swOut.Write(",");
}
swOut.Write(gridIn.Columns[i].HeaderText);
}
swOut.WriteLine();
//write DataGridView rows to csv
for (int j = 0; j <= gridIn.Rows.Count - 1; j++)
{
if (j > 0)
{
swOut.WriteLine();
}
dr = gridIn.Rows[j];
for (int i = 0; i <= gridIn.Columns.Count - 1; i++)
{
if (i > 0)
{
swOut.Write(",");
}
value = dr.Cells[i].Value.ToString();
//replace comma's with spaces
value = value.Replace(',', ' ');
//replace embedded newlines with spaces
value = value.Replace(Environment.NewLine, " ");
swOut.Write(value);
}
}
swOut.Close();
}
}
private void saveToolStripMenuItem_Click_1(object sender, EventArgs e)
{
writeCSV(DtgTable, "result.csv");
MessageBox.Show("Converted successfully to *.csv format");
}
//___________________________________________________________________________________
//about
//___________________________________________________________________________________
private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show("");
}
}
}
Your question(s) are fairly wide in scope and SO is not really oriented for this. Example, from your numerous questions it appears you want to:
Read and Write a CSV file without using a special library,
While reading the CSV file you want to store the data into some DATA
Structure like a DataTable or a List<T>,
with the DATA structure filled with data you want to BIND it to a DataGridView,
Then you want to be able to “delete” selected rows from the grid,
Delete ALL the rows from the grid,
Then with some additional text boxes, combo boxes, date picker… you want the user to be able to enter values into those controls then click an “ADD” button to “add” those values as a new row in the grid.
you want to validate the data the user types,
In the grid you want to make a column that “computes” a value based on other cells in that row…
I will try and break this down into manageable pieces that you can modify to fit your requirements.
Read a CSV file
Reading a csv file is fairly simple and as you have obviously done, there are libraries to do this for you, however, it is not difficult to do.
A CSV “implies” the delimiters are commas (,) where each comma separates each field. However, as you have noted, the user may enter a comma (,) into the data and this is going to mess up the csv structure. Therefore, this approach assumes you can define a “delimiter” to be any character. In the examples below I used a TAB (\t) character as a delimiter.
One problem when reading a csv file is noting how many columns (fields) are in the file. This is important to know BEFORE you read the file. If the number of fields on each row is unknown, then you could read the file once, splitting each line on the delimiter, then note the line that has the MOST fields. With this approach you are guaranteed that when you read the file the second time, that you will get ALL the data. This requires reading the file twice and may not be a good option.
Another approach is if the delimited file has a “header” row on the first row, you could use it to define the number of columns (fields) that are in the file. This may work; however, it is no guarantee that the top header row is correct.
Lastly, you can FIX the number of columns if you already know the number of fields. This is the approach the code below uses. In this example we know there are 6 fields in each row in the csv file. I am aware that there are actually 7 fields, however the 7th field is the “Balance” field and is “computed” from the other fields and is not stored in the csv file. Therefore, in this example we will assume each line in the csv file has six (6) fields in this order: ID, Date, Type, Name, Expense and Income. The Saldo (Balance) field will be computed.
Read CSV to DataTable
The code below reads a TAB DELIMITED file and stores the data into a DataTable. This DataTable is a global variable. Exposing this variable is not the best practice however it will suffice for this example. First is the method that “creates” the DataTable. This method simply initializes the DataTable and adds the column definition but NO rows. We will add the rows later.
Note the last “Balance” column is an “Expression” column to compute Income – Expense. When the Income or Expense fields are changed, this value will update automatically.
private void SetTableColumns() {
BudgetTable = new DataTable();
BudgetTable.Columns.Add("Id", typeof(Int32));
BudgetTable.Columns.Add("Date", typeof(DateTime));
BudgetTable.Columns.Add("Type", typeof(String));
BudgetTable.Columns.Add("Name", typeof(String));
BudgetTable.Columns.Add("Expenses", typeof(decimal));
BudgetTable.Columns.Add("Income", typeof(decimal));
BudgetTable.Columns.Add("Balance", typeof(decimal));
BudgetTable.Columns["Balance"].Expression = "Income - Expenses";
}
Next is the method that fills the above DataTable from a tab delimited file. It uses a StreamReader and will require using System.IO;. The variable splitArray is used to “split” each line on the TAB character.
Before we start reading the file, we want to check how many columns the DataTable has. This is the maxColumns variable and is used as a check if the currently read line does not have enough fields in the line. If we neglect to check this for EACH row read, then, when and one of the lines has fewer than 6 fields, then the code will crash at the line newRow[i] = splitArray[i]; since there is no splitArray[5]!... if the line read has MORE that 6 fields, then we will ignore those values and just grab the first 6. Also note that the maxColumns variable is set to the number of columns MINUS 1 since we want to ignore the computed “Balance” column.
Once we have validated the number of fields read, we can safely loop through all the fields/columns and add them to the new DataRow newRow.
private void FillDataTableFromCSV(string file) {
StreamReader sr = new StreamReader(file);
string[] splitArray;
int maxColumns = BudgetTable.Columns.Count - 1; // <- the last column is a computed column we dont want to save
int curColCount = maxColumns;
string curLine = sr.ReadLine();
if (curLine != null) {
DataRow newRow;
while ((curLine = sr.ReadLine()) != null) {
splitArray = curLine.Split('\t');
curColCount = maxColumns;
if (splitArray.Length < maxColumns) {
curColCount = splitArray.Length;
}
newRow = BudgetTable.Rows.Add();
for (int i = 0; i < curColCount; i++) {
newRow[i] = splitArray[i];
}
}
}
sr.Close();
}
The above code will safely work if all the data is good, however there is still a problem if the one of the “numeric” fields read is not a valid number. If an expense value in the csv file is “some string”, then the code will crash on the line newRow[i] = splitArray[i];. It appears we need to do some more validating of the data BEFORE we add it to the DataTable. The same would apply if we used a List<T> data structure to hold the data. The code below again will read a TAB delimited file, but will read the data into a List<T>. In this example the data IS validated for valid numbers before the data is added to the list. You should be able to apply the same validation to the above example.
Read CSV into BindingList<T>
First, we need to define Class T which we will call a BudgetItem. This bare bone BudgetItem Class may look like below. Note the public Date property is a string based on the private DateTime _Date object. This is for display only and is not really necessary. Also, note the public “Balance” property is computed from the Income – Expense properties. Lastly a full constructor.
FYI: the PRIVATE _Date property will NOT show in the grid. When a BindingList<T> is used as a DataSource to a DataGridView ONLY PUBLICLY exposed properties will be displayed. In addition, the grid will NOT display any COLLECTION type properties in the Class. Therefore, in this example Class only the Id, Date, Type, Name Expense, Income and Balance will display in the grid.
public class BudgetItem {
public int Id { get; set; }
private DateTime _Date { get; set; }
public string Date => _Date.ToShortDateString();
public string Type { get; set; }
public string Name { get; set; }
public decimal Expense { get; set; }
public decimal Income { get; set; }
public decimal Balance => Income - Expense;
public BudgetItem(int id, DateTime date, string type, string name, decimal expense, decimal income) {
Id = id;
_Date = date;
Type = type;
Name = name;
Expense = expense;
Income = income;
}
}
Next is the method to read the csv into a BindingList<BudgetItem>. Again, this list is a global variable. Since the validation of the data is needed, a second method GetBudgetItemFromString(curLine)) takes the line read from the file and returns a new BudgetItem we can add to the binding list.
private void FillListFromCSV(string file) {
BudgetList = new BindingList<BudgetItem>();
StreamReader sr = new StreamReader(file);
string curLine = sr.ReadLine(); // <- ignore header row
if (curLine != null) {
BudgetItem BI;
while ((curLine = sr.ReadLine()) != null) {
if ((BI = GetBudgetItemFromString(curLine)) != null) {
BudgetList.Add(BI);
}
else {
MessageBox.Show("Error invalid Budget Item: " + curLine);
}
}
}
sr.Close();
}
For validating the data, the method below follows this logic, if there are less than 6 fields in the line, then a null value is returned. If there are more than 6 values, the extra values are ignored. To validate the numeric values, a TryPase is used and if the string value is NOT a valid number, then the number returned will be zero (0). No error is thrown and the user will simply see zero’s (0) on the numeric fields where the numbers are invalid. You may want to change this.
private BudgetItem GetBudgetItemFromString(string data) {
string[] arr = data.Split('\t');
if (arr.Length >= 6) {
int.TryParse(arr[0], out int id);
DateTime.TryParse(arr[1], out DateTime date);
decimal.TryParse(arr[4], out decimal exp);
decimal.TryParse(arr[5], out decimal inc);
return new BudgetItem(id, date, arr[2], arr[3], exp, inc);
}
return null;
}
Writing the grid data to a csv file
I am guessing you could create a method to write a DataTable to a csv, then another method to write a BindingList<BudgetItem> to a csv… however, if we write a method that writes a DataGridView to a csv, then we would only need one method and it will not matter what “type” the data source is.
The WriteGridToCSV(DtgTable, filePath); method takes a DataGridView and a file path and writes the data in the grid to a TAB DELIMITED file. Here we will use a StreamWriter to write the data to a file. First the headers are written, then a simple loop through the grid’s rows and columns. Note: as previously stated, we do NOT want to write the “Balance” column.
private void WriteGridToCSV(DataGridView dgv, string filename) {
StreamWriter sw = new StreamWriter(filename);
// write headers - we do not want to write the balance column
for (int col = 0; col < dgv.Columns.Count - 1; col++) {
sw.Write(dgv.Columns[col].HeaderText);
if (col < dgv.Columns.Count - 2) {
sw.Write("\t");
}
}
sw.WriteLine();
// Write data - we do not want to save the balance column
for (int row = 0; row < dgv.RowCount; row++) {
for (int col = 0; col < dgv.ColumnCount - 1; col++) {
if (!dgv.Rows[row].IsNewRow) {
sw.Write(dgv.Rows[row].Cells[col].Value);
if (col < dgv.ColumnCount - 2) {
sw.Write("\t");
}
}
}
sw.WriteLine();
}
sw.Close();
MessageBox.Show("Write finished");
}
Once we have the collection of data, we can use it as a DataSource to the DataGridView.
// for the DataTable
DtgTable.DataSource = BudgetTable;
// For the `BindingList<BudgetItem>
DtgTable.DataSource = BudgetList;
Now that the data is displayed in the grid, we want a button that “deletes” the selected rows. This assumes the grids Selection mode is set to some FULL ROW value. We need to grab the selected “row” from the DataTable and remove it. Or we need to grab the selected BudgetItem and remove it from the BindingList<BudgetItem>
When using a DataTable
private void btnDelete_Click(object sender, EventArgs e) {
foreach (DataGridViewRow row in DtgTable.SelectedRows) {
DataRowView dr = (DataRowView)row.DataBoundItem;
dr.Delete();
}
DtgTable.DataSource = null;
DtgTable.DataSource = BudgetTable;
}
}
When using a BindingList<BudgetItem>
private void btnDelete_Click(object sender, EventArgs e) {
BudgetItem target;
foreach (DataGridViewRow row in DtgTable.SelectedRows) {
target = (BudgetItem)row.DataBoundItem;
BudgetList.Remove(target);
}
DtgTable.DataSource = null;
DtgTable.DataSource = BudgetList;
}
To clear the grid of ALL rows…
private void btnClearAll_Click(object sender, EventArgs e) {
BudgetTable.Rows.Clear();
}
private void btnClearAll_Click(object sender, EventArgs e) {
BudgetList = new BindingList<BudgetItem>();
DtgTable.DataSource = null;
DtgTable.DataSource = BudgetList;
}
Next, adding new rows to the grid from the text boxes the user types into.
In the example below, I have added the ID text box and used a DateTimePicker for the date value. The DateTimePicker will eliminate the need to “validate” the date as a user can NOT pick an invalid date. The “type” combo box is a string and needs no validation. This would also apply to the name field. Therefore, we need to make sure the fields are not empty and the ID field is a valid integer and the Expense and Income fields are valid decimal numbers. The int and decimal types have a property called TryParse and is a handy way to validate the numbers.
Both methods below are identical except one adds a row to a DataTable and the other add a new BudgetItem to the BindingList<BudgetItem>.
Adding row to DataTable
private void btnAddItem_Click(object sender, EventArgs e) {
if (!AllFieldsEntered()) {
MessageBox.Show("'ID', 'Type','Name', 'Expense' and 'Income' fields cannot be empty!");
}
else {
StringBuilder errorString = new StringBuilder("Invalid Values: " + Environment.NewLine);
bool noErrors = true;
if (!int.TryParse(txtID.Text, out int id)) {
errorString.AppendLine("ID must be a valid integer");
noErrors = false;
}
if (!decimal.TryParse(expenseField.Text, out decimal exp)) {
errorString.AppendLine("Expense must be a valid decimal");
noErrors = false;
}
if (!decimal.TryParse(incomeField.Text, out decimal inc)) {
errorString.AppendLine("Income must be a valid decimal");
noErrors = false;
}
string date = dtpDate.Value.ToString("MM/dd/yyyy");
if (noErrors) {
BudgetTable.Rows.Add(id, date, cbbxType.Text, nameField.Text, exp, inc);
}
else {
MessageBox.Show(errorString.ToString());
}
}
}
Adding row to BindingList
private void btnAddItem_Click(object sender, EventArgs e) {
if (!AllFieldsEntered()) {
MessageBox.Show("'ID', 'Type','Name', 'Expense' and 'Income' fields cannot be empty!");
}
else {
StringBuilder errorString = new StringBuilder("Invalid Values: " + Environment.NewLine);
bool noErrors = true;
if (!int.TryParse(txtID.Text, out int id)) {
errorString.AppendLine("ID must be a valid integer");
noErrors = false;
}
if (!decimal.TryParse(expenseField.Text, out decimal exp)) {
errorString.AppendLine("Expense must be a valid decimal");
noErrors = false;
}
if (!decimal.TryParse(incomeField.Text, out decimal inc)) {
errorString.AppendLine("Income must be a valid decimal");
noErrors = false;
}
if (noErrors) {
BudgetList.Add(new BudgetItem(id, dtpDate.Value, cbbxType.Text, nameField.Text, exp, inc));
DtgTable.DataSource = null;
DtgTable.DataSource = BudgetList;
}
else {
MessageBox.Show(errorString.ToString());
}
}
}
Check that all fields have data
private bool AllFieldsEntered() {
if (string.IsNullOrWhiteSpace(cbbxType.Text) ||
string.IsNullOrWhiteSpace(expenseField.Text) ||
string.IsNullOrWhiteSpace(txtID.Text) ||
string.IsNullOrWhiteSpace(incomeField.Text) ||
string.IsNullOrWhiteSpace(nameField.Text)) {
return false;
}
return true;
}
Finally, for the text boxes where numbers are needed, we can help the user by allowing only numbers and a possible ONE period (.) for the decimal numbers. To wire this up, you need to register the KeyPressed events for the numeric text boxes. These lines can be placed in the forms constructor AFTER the InitializeComponent(); line.
this.txtID.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.IntegerOnlyField_KeyPress);
this.expenseField.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.DecimalOnlyField_KeyPress);
this.incomeField.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.DecimalOnlyField_KeyPress);
Once these events have been wired up, we need to supply those registered methods in the events above… A check is made to see if the key pressed is a digit, the backspace or a period. If its not one of those characters, then ignore it. NOTE: the user WILL still be able to "PASTE" invalid values into the text box.
private void DecimalOnlyField_KeyPress(object sender, KeyPressEventArgs e) {
if (!(char.IsDigit(e.KeyChar) || e.KeyChar == (char)Keys.Back || e.KeyChar == '.')) {
e.Handled = true;
}
TextBox txtDecimal = sender as TextBox;
if (e.KeyChar == '.' && txtDecimal.Text.Contains(".")) {
e.Handled = true;
}
}
private void IntegerOnlyField_KeyPress(object sender, KeyPressEventArgs e) {
if (!(char.IsDigit(e.KeyChar) || (e.KeyChar == (char)Keys.Back))) {
e.Handled = true;
}
}
Your welcome and I hope it helps.
I am making a Windows Forms App that manages a hotel. It has Client, Room, Occupancy classes. Client and Rooms have an ArrayList that is populated at runtime from a .txt file that is then displayed in a clientListView and a roomDataGridView.
As such, I have this line of code to populate the roomsDGV:
roomsDGV.DataSource = roomsArrayList;
With the roomsDGV, I'm trying to add new Rows by clicking on the roomsDGV, like when it is NOT databound. I am also trying to edit the rows and save it to txt file after editing or as I'm editing. I can post more code as necessary but I'm not sure if showing more code will help at the current moment. In the end, I'm trying for a functionality so that I can highlight a client in the list and click on one of the rows in roomsDGV and assign that clientID to that room or any sort of way like that.
On load, the datagridview is loaded and formatted correctly from the arrayList but I seem to be having this problem of being able to edit the datagridview. It gives me this error when I click on one of the rows:
System.IndexOutOfRangeException: 'Index -1 does not have a value.'
This stems from Application.Run(new HotelManager());
Here is the form:
public partial class HotelManager : Form
{
// VARIABLES
string clientID;
// FILEPATHS
string clientsTxt = "Clients.txt";
string occupanciesTxt = "Occupancies.txt";
string roomsTxt = "Rooms.txt";
string clientsDat = "Clients.dat";
// ARRAYLIST FOR ROOMS and CLIENTS
ArrayList roomsArrayList = new ArrayList();
ArrayList clientsArrayList = new ArrayList();
//STACKS AND QUEUES INIT
// Load occupancies into stack > pop
Stack roomStack = new Stack();
Queue vacancyQueue = new Queue();
// RANDOM for ID
private readonly Random rand = new Random();
public HotelManager()
{
InitializeComponent();
}
private void HotelManager_Load(object sender, EventArgs e)
{
roomsDGV.DataSource = roomsArrayList;
// LOAD clients
// LoadClients();
RefreshClientList();
// LOAD rooms
LoadRooms();
}
private void NewClientButton_Click(object sender, EventArgs e)
{
AddClient();
}
private void checkInButton_Click(object sender, EventArgs e)
{
string clientID = clientList.SelectedItems[0].Text;
string[] text = File.ReadAllLines(occupanciesTxt);
foreach (string s in text)
{
if (s.Contains(clientID))
{
var replace = s;
Console.WriteLine(s);
replace = replace.Replace("false", "true");
}
}
File.WriteAllLines(occupanciesTxt, text);
}
// METHODS
private void AddClient()
{
//COLLECT DATA > CREATE NEW client > SHOW IN **PROGRAM/DataGridView** > add to clients file
// ID GENERATION > CHECKS AGAINST clientsTXT
clientID = rand.Next(0, 999999).ToString();
if (File.ReadLines(clientsTxt).Contains(clientID))
{
clientID = rand.Next(0, 999999).ToString();
}
Client client = new Client(clientID, firstNameBox.Text, lastNameBox.Text);
try
{
if (!string.IsNullOrWhiteSpace(phoneNumBox.Text))
{
client.PhoneNumber = Convert.ToInt64(phoneNumBox.Text);
}
if (!string.IsNullOrWhiteSpace(addressBox.Text))
{
client.Address = addressBox.Text;
}
}
catch (Exception)
{
MessageBox.Show("Please use the correct format!");
throw;
}
clientsArrayList.Add(client);
using (StreamWriter file =
new StreamWriter("Clients.txt", true))
{
file.WriteLine(client.ToString());
}
RefreshClientList();
// TEST CODE // SERIALIZATION TO .DAT
SerializeClientData(client);
}
private void LoadClients()
{
// LOADS arrayList FROM .txt FILE
List<string> clientList = File.ReadAllLines(clientsTxt).ToList();
foreach (var c in clientList)
{
Client client = new Client(c);
clientsArrayList.Add(client);
}
}
private void LoadRooms()
{
List<string> roomsList = File.ReadAllLines(roomsTxt).ToList();
foreach (var r in roomsList)
{
var roomDetails = r.Split('|');
if (r.Contains("BASIC"))
{
BasicRoom basic = new BasicRoom();
basic.RoomNumber = roomDetails[0];
basic.NumberOfBeds = Convert.ToInt32(roomDetails[1]);
basic.Balcony = Convert.ToBoolean(roomDetails[2]);
basic.DownForRepair = Convert.ToBoolean(roomDetails[3]);
basic.Smoking = Convert.ToBoolean(roomDetails[4]);
roomsArrayList.Add(basic);
}
else if (r.Contains("SUITE"))
{
Suite suite = new Suite();
suite.RoomNumber = roomDetails[0];
suite.NumberOfBeds = Convert.ToInt32(roomDetails[1]);
suite.Balcony = Convert.ToBoolean(roomDetails[2]);
suite.DownForRepair = Convert.ToBoolean(roomDetails[3]);
suite.NumberOfRooms = Convert.ToInt32(roomDetails[4]);
roomsArrayList.Add(suite);
}
}
roomStack = new Stack(roomsArrayList);
foreach (var item in roomStack)
{
Console.WriteLine(item);
}
}
private void RoomsDGV_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
}
private void RoomsDGV_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
{
}
}
So far I've looked through all the properties but I can't seem to find the right one. I know I can add/use comboboxes and etc to add a new item into the arrayList instead but I'm trying for datagridview functionality
I expect to edit and add rows to the DGV, but something in the designer is preventing me?
Here is the DGV, and clicking on any of the rows breaks it.
https://imgur.com/a/GG7ZwdV
check this
Insert, Update, Delete with DataGridView Control in C# (Windows Application) | A Rahim Khan's Blog
Insert, Update and Delete Records in a C# DataGridView
Good Luck
I have a form WinForms form window with a DataGridView. It pulls data from a SQL Server Express database using Linq and displays it in the DataGridView columns properly. I have it set in the properties to allow editing. When I edit and change a field during runtime it will only update the database if I specify one column name. This doesn't work well because I need it to update the column I edit, not the one hard coded. It needs to be dynamic. See the comments next to site.??? I can choose a column name manually from site.whatever but that's not what I want. I want to be able to do Site.columnname from the string I set.
I see other examples loading and/or editing a DataGridView with data, but these examples are not using LINQ.
My form load code:
public partial class EditImage : Form
{
SQL sqlLink = new SQL();
CustomMessageBox msg = new CustomMessageBox();
public EditImage()
{
InitializeComponent();
}
private void EditImage_Load(object sender, EventArgs e)
{
dgImageView.DataSource = sqlLink.getImageList();
dgImageView.AutoResizeColumns();
dgImageView.AutoResizeRows();
}
private void dgImageView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
int rid = e.RowIndex;
int dbrid = rid + 1;
int cid = e.ColumnIndex;
string change = dgImageView[cid, rid].Value.ToString();
string columname = dgImageView[cid, rid].OwningColumn.Name;
using (var dc = new DbLink())
{
var site = (from img in dc.images where img.id == dbrid select img).SingleOrDefault();
if (site != null)
{
try
{
site.??? = change;
dc.SubmitChanges();
dgImageView.EndEdit();
}
catch (Exception ex)
{
msg.Text("Error", ex.ToString(), MessageBoxButtons.OK);
}
}
}
}
Code from SQL class to retrieve DataSource:
public IQueryable getImageList()
{
return selectImageList();
}
and
private IQueryable selectImageList()
{
DbLink dbl = new DbLink();
image tl = new image();
var results = from imgs in dbl.images
select imgs;
return results;
}
A screenshot of the dbml file:
You could try with reflection:
var changedColumn = typeof(image).GetProperty(columnName);
changedColumn.SetValue(site,change);
I'm completely new to databases and EF but I made a database with EF and have a DataGridView control on a windows form that I made by dragging my datasource to my form. After the user enters their information and hits the save button it succesfully saves their information in the database using this code
public partial class bsMainPage : Form
{
BSDATAContainer db = new BSDATAContainer();
public bsMainPage()
{
InitializeComponent();
}
private void saveBtn_Click(object sender, EventArgs e)
{
BSRecords breakfastRecord = new BSRecords();
breakfastRecord.BS = brkBS.ToString();
breakfastRecord.Carbs = brkCarb.ToString();
breakfastRecord.Notes = brkftNoteTxt.Text;
breakfastRecord.Date = dateTxt.Text;
BSRecords lunchRecord = new BSRecords();
lunchRecord.BS = lchBS.ToString();
lunchRecord.Carbs = lchCarb.ToString();
lunchRecord.Notes = lnchNoteTxt.Text;
lunchRecord.Date = dateTxt.Text;
BSRecords dinnerRecord = new BSRecords();
dinnerRecord.BS = dnrBS.ToString();
dinnerRecord.Carbs = dnrCarb.ToString();
dinnerRecord.Notes = dnnrNoteTxt.Text;
dinnerRecord.Date = dateTxt.Text;
db.BSRecords.Add(breakfastRecord);
db.BSRecords.Add(lunchRecord);
db.BSRecords.Add(dinnerRecord);
db.SaveChanges();
}
}
But it doesn't show up in the database until I restart the program. When the user selects a row in the DataGridView and hits the delete button which has this code
private void deleteRowsBtn_Click(object sender, EventArgs e)
{
foreach (DataGridViewRow item in this.bSRecordsDataGridView.SelectedRows)
{
bSRecordsDataGridView.Rows.RemoveAt(item.Index);
}
db.SaveChanges();
}
It deletes the data in the DataGridView but doesn't save the changes in my database. I have followed all the answers I found on here and other sites to delete in the database but nothing will save the deleted changes. Does anyone have any idea how to make it work?
You can delete it using remove. You will need to get the key/id field so without seeing the grid and assuming it is say in a hidden first column:
private void deleteRowsBtn_Click(object sender, EventArgs e)
{
string delId;
BSRecords deleteRecord;
foreach (DataGridViewRow item in this.bSRecordsDataGridView.SelectedRows)
{
bSRecordsDataGridView.Rows.RemoveAt(item.Index);
// code to remove record from database
delId = item.Cells[0].Value.ToString(); // column that has id field
deleteRecord = db.BSRecords.First(b => b.Id == delId); // get the record. will throw exception if not found.
db.BSRecords.Remove(deleteRecord);
}
db.SaveChanges();
bSRecordsDataGridView.DataBind(); // this will refresh your grid. Do same in save.
}
Also note you can rewrite this code:
BSRecords breakfastRecord = new BSRecords();
breakfastRecord.BS = brkBS.ToString();
breakfastRecord.Carbs = brkCarb.ToString();
breakfastRecord.Notes = brkftNoteTxt.Text;
breakfastRecord.Date = dateTxt.Text;
with an object initializer:
BSRecords breakfastRecord = new BSRecords { BS = brkBS.ToString(),
Carbs = brkCarb.ToString(),
Notes = brkftNoteTxt.Text,
Date = dateTxt.Text };
First of all:
_ddlOptions is drop down list
_selectedOptions is repeater control
and it's just provisional code of my final control.
What I want to do is to get data for _ddlOption on !IsPostBack. There is Add button that enables user to move selected drop down item to repeater control.
It the following way of updating Repeater.Items correct? I found many solution of adding/removing elements manually using DataSource, but here my DataSource is null, as I set it only on !IsPostBack.
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
_ddlOptions.DataSource = new[] { 1, 2, 3 };
_ddlOptions.DataBind();
}
}
protected void OnAdd(object sender, EventArgs e)
{
var list = new ArrayList(_selectedOptions.Items);
list.Add(_ddlOptions.SelectedItem);
_ddlOptions.Items.RemoveAt(_ddlOptions.SelectedIndex);
_selectedOptions.DataSource = list;
_selectedOptions.DataBind();
}
If you only need to fetch data once and you're going to use viewstate, get the data first time you need it, store it in VS and get it from VS for all future PostBacks.
Example:
public List<int> Data
{
get
{
if (ViewState["Data"] == null)
{
// Get your data, save it and return it.
var data = new List<int> { 1, 2, 3 };
ViewState["Data"] = data;
return data;
}
return (List<int>)ViewState["Data"];
}
set
{
ViewState["Data"] = value;
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
BindData(Data);
}
}
private void BindData(List<int> data)
{
_ddlOptions.DataSource = data;
_ddlOptions.DataBind();
}
protected void OnAdd(object sender, EventArgs e)
{
var existing = Data;
existing.Add(_ddlOptions.SelectedItem);
_ddlOptions.Items.RemoveAt(_ddlOptions.SelectedIndex);
Data = existing;
BindData(existing);
}
I didn't test this - and its only my first thought but you can build on it from here.
Patrick.
Looks good to me. You just may want to move the decalration for your list outside the onAdd method. As you have it I think it will be reinitialized every time the add button is clicked, so you'll never have more than the currently selected item in your repeater.
You can use a DataAdapter to fill a table in a DataSet.
DataSet ds = new DataSet();
using (SqlConnection conn = YourConnectionFactory.GetConnection())
{
SqlCommand objComm = DBHelper.CreateStoredProc("YourStoredProcedure",
conn);
SqlDataAdapter adapt = new SqlDataAdapter(objComm);
adapt.Fill(ds, TableName);
conn.Close();
}
DataTable dt = ds.Tables[0];
for (int a=dt.Rows.Count-1; a>= 0; a--)
{
// check and insert as necessary
}
YourControl.DataSource = ds;
YourControl.DataBind();
You can also do something like this then,
like rebind Taken from: http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.repeater.aspx:
Dim values As New ArrayList()
values.Add(New PositionData("Microsoft", "Msft"))
values.Add(New PositionData("Intel", "Intc"))
values.Add(New PositionData("Dell", "Dell"))
Repeater1.DataSource = values Repeater1.DataBind()
Repeater2.DataSource = values Repeater2.DataBind()