I'm developing a basic app in Windows OS using VS C# Express and MySQL as my database. Now, the purpose of my app is to just list all the profiles in a data grid in a form. Now my question is what data grid control in C# .net that I can use to list my profiles in my mysql database? And how to populate that grid control with the data from the database in the code?
I already have a working database. I can also query the database in the C# code.
Please advise.
Many thanks.
you can use a DataGridView to display you're data
string sql = "SELECT ProfilCode, ProfilName FROM Profils";
DataTable myTable = new DataTable();
using(MySqlConnection connection = new MySqlConnection(connectionString))
{
using(MySqlDataAdapter adapter = new MySqlDataAdapter(sql, connection))
{
adapter.Fill(myTable);
}
}
dataGridView.DataSource = myDataTable;
Good luck
DataGridView is the Data Grid you want
There are several ways to fill it with data.
The simplest way would be just to bind the data to DataGridView. See this article on it.
Binding is really easy. Lets say you fill your MySql results to a some enumerable of class Cat (this is a scenario of using some ORM). Binding is easy:
class Cat
{
[DisplayName("Cats Name")]
public string Name {get;set;}
public string Likes {get; set;}
}
...
public void BindCatsToGrid(List<Cat> cats)
{
bindingDataSource = new BindingDataSource();
bindingDataSource.DataSource = cats;
grid.DataSource = bindingDataSource;
}
But you can control rows and columns manually. Or, even, switch to so called 'virtual mode' for displaying the huge sets of data.
There is a good article about using DataGridView in different scenarios.
Related
When trying to change the active column in the following table 1.
I get the error
Error insufficient parameters supplied to the command
and I cannot figure out for the life of me what is wrong with the code. Please help.
private void dataGridView1_SelectionChanged_1(object sender, EventArgs e)
{
SQLiteConnection sqlConnection = new SQLiteConnection();
sqlConnection.ConnectionString = "datasource = SubjectTable.db";
if (dataGridView1.SelectedRows.Count > 0)
{
ID = dataGridView1.SelectedRows[0].Cells[1].Value.ToString();
//Define SELECT Statement
string commandText = "SELECT * FROM SubjectTable WHERE ID=" + ID;
//Create a datatable to save data in memory
var datatable = new DataTable();
SQLiteDataAdapter myDataAdapter = new SQLiteDataAdapter(commandText, sqlConnection);
sqlConnection.Open();
//Fill data from database into datatable
myDataAdapter.Fill(datatable);
//Fill data from datatable into form controls
CMBactive.Text = datatable.Rows[0]["Active"].ToString();
TBsubjectBUD.Text = datatable.Rows[0]["Budget"].ToString();
sqlConnection.Close();
}
}
You are getting these problems, because you are trying to do too much things in one procedure. You should separate your concerns
Separate the fetching of the data from the database from displaying this data; separate also that you do this of event selection changed.
This has the advantage that you can reuse your code more easily: if you want to do the same because of a Button press, you can reuse the code. After that it is a one-liner code if you want to add a menu item doing the same.
It is easier to test your code if you have a separate method to query the database, or a separate method to fill your controls CmbActive and TbSubjectBud.
It is easier to change your code, for instance, if you will not use SQLite anymore, but entity framework to fetch your data, the displaying and the button handling won't notice this. Only the procedure to fetch the data needs to be changed.
This makes unit testing easier: instead of a real database, you can mock the database with a Dictionary for the tests.
Finally: when using Winforms, don't fiddle with the Cells directly, use the DataSource of the DataGridView to fill and read the data. Again: separate the data from how it is displayed.
First Your actual problem: query the data
So you have an Id, and you want to fetch the values of columns Active and Budget from all Subjects from the database that have this Id. Don't fetch properties that you won't use!
Database handling
First we need a class Subject to put the data that you fetch from table SubjectTable. If you put all columns of this table in it, you can reuse the class for other queries. However. you don't have to fill in all fields. It depends how often you will call this method whether it is wise to fill all properties or only some.
Some people don't like this. Consider to fetch always all columns (inefficient), or to create classes for different queries (a lot of work).
class Subject
{
public int Id {set; set;}
public string Name {get; set;}
public DateTime StartDate {get; set;}
public string Active {get; set;}
public Decimal Budget {get; set;}
}
Create a method to fetch the Active and Budget from tableSubjects with Id, or null if there is not subject with this Id.
Put all your database queries in a separate class. For instance class Repository. You hide that it is in a database, if in future you want to save it in a CSV-file, or JSON format, no one will notice (nice if you want to use it in a unit test!)
private Subject FetchBudgetOrDefault(int id)
{
const string sqlText = #"SELECT Active, Budget FROM SubjectTable WHERE ID = #Id";
using (var dbConnection = new SQLiteConnection(this.dbConnectionString))
{
using (var dbCommand = dbConnection.CreateCommand()
{
dbCommand.Commandtext = sqlText;
dbCommand.Parameters.AddWithValue("#Id", id);
using (var dbReader = dbCommand.ExecuteReader())
{
if (dbReader.Read())
{
// There is a Subject with this id:
return new Subject()
{
Id = id,
Active = dbReader.GetString(0),
Budget = (decimal)dbReader.GetInt64(1) / 100.0D,
};
}
else
{
// no subject with this Id
return null;
}
}
}
}
}
I assumed that the decimal Budget is saved as long * 100 on purpose, to show you that by separating your concerns it is fairly easy to change the database layout without having to change all users: if you want to save this decimal in SQLite as a REAL, then the queries are the only place where you have to change the data.
By the way: this method also solved your problem: ID can't be an empty string!
If you won't do this query 1000 times a second, consider to fetch all columns of Subject. This is a bit less efficient, but easier to test, reuse, and maintain.
Display the fetched data in your form
Currently you display the data in a ComboBox and a TextBox. If you separate your concerns, there will only be one place where you do this. If you want to display the data in a Table, or do something else with it, you only have to change one place:
public void Display(Subject subject)
{
this.comboBoxActive.Text = subject.Active;
this.textBoxBudget.Text = subject.Budget.ToString(...);
}
Bonus points: if you want to change the format of the displayed budget, you'll only have to do this here.
Read and Write the DataGridView
It is seldom a good idea to read and write the cells of a DataGridView directly. It is way to much work. You'll have to do all type checking yourself. A lot of work to test and implement small changes in the displayed data.
It is way easier to use the DataSource.
In the DataSource of the DataGridView you put a sequence of similar items. If you only want to Display once, an ICollection<TSource> will be enough (Array, List). If you want to update changes automatically, use a BindingList<TSource>
In the DataGridView add columns. User property DataGridViewColumn.DataPropertyName to indicate which property should be displayed in that column.
Usually it is enough to use visual studio designer to add the columns.
If your datagridview displays Subjects, code will be like:
DataGridView dgv1 = new DataGridView();
DataGridViewColumn columnId = new DataGridViewColumn
{
DataPropertyName = nameof(Subject.Id),
...
};
DataGridView columnName = new DataGridViewColumn
{
DataPropertyName = nameof(Subject.Name),
...
};
... // other columns
dgv.Columns.Add(columnId);
dgv.Columns.Add(columnName);
...
In your forms class:
private BindingList<Subject> DisplayedSubjects {get; set;} = new BindingList<Subject>();
// Constructor:
public MyForm()
{
InitializeComponent();
this.dgv1.DataSource = this.DisplayedSubjects();
}
void FillDataGridView()
{
using (var repository = new Repository())
{
IEnumerable<Subject> fetchedSubjects = repository.FetchAllSubjects();
this.DisplayedSubjects = new BindingList<Subject>(fetchedSubjects.ToList();
}
}
This is all that is needed to display all fetched subjects. If the operator changes any cell value, the corresponding value in this.DislayedSubjects is automatically updated.
This works both ways: if you change any value in this.DisplayedSubjects, the displayed value in the DataGridView is automatically updated.
No need to read the cells directly. If you allow column reordering, or if you implement row sorting, then everything still works two ways. Because you separated the fetched data from the displayed data, you can change the display without having to change the fetched data.
Put it all together
When you get the event that the selection is changed from the datagridview you want to update Active and Budget. Let's do the from the Selected item:
void OnSelectionChanged(object sender, EventHandler e)
{
// Get the selected Subject
var selectedSubject = this.SelectedSubject;
this.Display(selectedSubject); // described above
}
Subject SelectedSubject => this.Dgv.SelectedRows.Cast<DataGridViewRow>()
.Select(row => (Subject)row.DataBoundItem)
.FirstOrDefault();
Because you separated concerns, each method is easy to understance, easy to test, easy to reuse and to change slightly: if you want to update after a Button Press or a menu item, the code will be a one liner. If you want to display other items than just Active / Budget: small changes; if you want to fetch by Name instead of Id: only limited changes needed.
I have a column with encrypted name(all other columns are not encrypted) in SQL Database table. And I have to decrypt the column with encrypted name to show in DataGrid to users of my application but the actual table of SQL database should not be changed.(has to remain as encrypted name).
I think UpdateCommand works to update the actual table and I have to find an alternative than below UpdateCommand.
Or is there alternative way to decrypt only 1 column on DataTable which is not influencing the actual table of database?
My simple code is,
SqlCommand gridcomm = new SqlCommand();
gridcomm.Connection = Conn;
gridcomm.CommandText = "SELECT Id, customername, phonenumber FROM customers";
SqlDataAdapter gridda = new SqlDataAdapter(gridcomm);
SqlDataReader gridreader = gridcomm.ExecuteReader();
while (gridreader.Read())
{
}
gridreader.Close();
DataTable griddt = new DataTable("customers");
gridda.Fill(griddt);
foreach (DataRow row in griddt.Rows)
{
string strcustomername = (string) row["customername"].ToString();
bytecustomername = Convert.FromBase64String(strcustomername);
string decryptedcustomername = DecryptStringFromBytes_Aes(bytecustomername, byteAESKey, byteAESIV);
row["customername"] = decryptedcustomername;
}
gridda.UpdateCommand = new SqlCommandBuilder(gridda).GetUpdateCommand();
dataGrid_Totalcustomerlist.ItemsSource = griddt.DefaultView;
gridda.Update(griddt);
Hello Kay Lee: I think that if you look at implementing a Coverter in your View you will get exactly what you are looking for. In your IValueConverter implementation you can Implement the Decrypt routine. A Converter is the extended syntax in a WPF Binding Statement. If this is not clear then I will flesh out some more. Here is a great reference for Converters: http://www.wpf-tutorial.com/data-binding/value-conversion-with-ivalueconverter/
Kind Regards,
Mark Wardell
I've read many posts but there were no solution for me as this case is unusual. However, I just thought logically and finally found solution by myself.
We just need to delete 2 line of Update related code because we don't need to update.
gridda.UpdateCommand = new SqlCommandBuilder(gridda).GetUpdateCommand();
gridda.Update(griddt);
Hope this helps someone..
I am working on an application in which data is retrieved from four SQL tables using joins and the retrieved data is populated to a datagridview on a windows form.
I have two Radio Buttons ALL and DrawDate , by default ALL Radio Button is selected and once the application is opened it populates all the data on to the datagridview and when I select DrawDate Radio Button, only the data related to that draw date is populated on the datagridview. Everything looks fine until then but after selecting DrawDate Radio Button and if a user want to get all the data again by selecting ALL Radio Button, it again loads all data from the database server which is not preferred.
Is there any better way I can cache the data populated once the application is opened and populate it when a user selects ALL Radio Button later when he needs it?
C# Code
sqlcon = GetConnectionString();
try
{
sqlcon.Open();
//var sw = Stopwatch.StartNew();
for (int i = 0; i < dgvPaymentsReceived_Collections.RowCount; i++)
{
int trademonth = Convert.ToInt32(dgvPaymentsReceived_Collections.Rows[i].HeaderCell.Value);
for (int j = 0; j < dgvPaymentsReceived_Collections.ColumnCount; j++)
{
int paymentmonth = Convert.ToInt32(dgvPaymentsReceived_Collections.Columns[j].HeaderCell.Value);
//var sw = Stopwatch.StartNew();
SqlCommand cmd_PaymentsReceived = new SqlCommand();
cmd_PaymentsReceived.Connection = sqlcon;
cmd_PaymentsReceived.CommandType = CommandType.StoredProcedure;
cmd_PaymentsReceived.CommandText = sp_PaymentsReceved_Collections;
cmd_PaymentsReceived.Parameters.Add(new SqlParameter("#trademonth", trademonth));
cmd_PaymentsReceived.Parameters.Add(new SqlParameter("#paymentmonth", paymentmonth));
SqlDataAdapter da_PaymentsReceived_Collections = new SqlDataAdapter();
DataTable dt_PaymentsReceived_Colletions = new DataTable();
da_PaymentsReceived_Collections.SelectCommand = cmd_PaymentsReceived;
da_PaymentsReceived_Collections.Fill(dt_PaymentsReceived_Colletions);
//sw.Stop();
//MessageBox.Show(sw.ElapsedMilliseconds.ToString());
dgvPaymentsReceived_Collections.Rows[i].Cells[j].Value = dt_PaymentsReceived_Colletions.Rows[0][0].ToString();
}
}
sqlcon.Close();
}
You haven't shown us any code. What kind of connection did you use? Don't bind your database source to a datagridview - use either an Enumerable/IList collection or DataSet as your data holder and then assign only the part of it (filtering it with LINQ) to your datagridview. This way you don't have to create a new collection every time in memory, just iterate over the items with given condition.
Assuming you don't have tons and tons of data, you could store the SQL results in a DataTable and then bind that to the DataGridView
You could consider storing the SQL data in a IEnumerable data structure when All button is clicked, and bind that collection to the gridview, and during the DrawDate button click, sort the collection appropriately.
You can hang on to your original data table and create a separate DataTable to bind to your DataGridView if you want to be able to change what is displayed.
Just fill your data table once, and then create a new DataTable to hide what you don't need.
This might use slightly more memory depending on the size of your DataTable, but it's probably the easiest solution to implement.
DataTable viewedData = dt_PaymentsReceived_Colletions;
if(hideIncome.Checked) {
viewedData.Columns.Remove("Income");
viewedData.AcceptChanges();
}
DataGridView1.DataSource = viewedData;
I have a ComboBoxColumn in my datagridview and the data that's bound to them is from another table. The data can be updated at any point so I want to update the data in the ComboBoxColumn automatically when the data is added/updated from another table.
I've tried using -
ScripterCombo.DataSource = updatedUserList;
updatedUserListis a list of the data that i want to apply to the ComboBoxColumn, I already set the DataSource of ComboBoxColumn. Unfortunately the ComboBoxColumn never update. I need to reload the entire application to view the changes.
UPDATE -
Sorry i'm not using a list to store the data, i'm using a string array. Basically i connect to the backend server and loop through all the names in the 'User' Column:
MySqlConnection(Constants.serverInfo))
{
getNumberOfUsers.Open();
using (MySqlCommand requestCommand = new MySqlCommand("SELECT COUNT(*) FROM userlist", getNumberOfUsers))
{
MySqlDataReader userReader = requestCommand.ExecuteReader();
while (userReader.Read())
{
iNumberOfUsers = userReader.GetInt32(0); // Pull the number of Owners from the backend.
strListOfUsers = new string[iNumberOfUsers]; // Make sure the owner list can hold the value pulled from the backend.
}
userReader.Close();
}
getNumberOfUsers.Close();
}
// Get the names of the users
using (MySqlConnection getUsersNames = new MySqlConnection(Constants.serverInfo))
{
getUsersNames.Open();
using (MySqlCommand requestCommand = new MySqlCommand("SELECT username FROM userlist", getUsersNames))
{
MySqlDataReader userReader = requestCommand.ExecuteReader();
for (int i = 0; i < iNumberOfUsers; i++)
{
while (userReader.Read())
{
strListOfUsers[i] = userReader.GetString(0); ; // Add the user to the list of users.
i++;
}
}
userReader.Close();
}
getUsersNames.Close();
}
I have another class that grabs data from a different table and displays it in a datagridview. I then feed the array of users through to the above class and create a datagridcomboboxcolumn that displays all the usernames. This works as expected, as it displays the user list and then updates the main table when a user has been changed.
However, if i add a user to the list I call the above code again to get an updated list and then feed the list to the comboboxcolumn using this -
ScripterCombo.DataSource = updatedUserList;
This unfortunatly doesn't update the comboboxes, but when I check the DataSource after running this it's showing the newly added user. It just doesn't want to display it.
Hope this makes sense.
Thanks
try to add ToList()
ScripterCombo.DataSource = updatedUserList.ToList();
I am using VS2005 C# and SQL Server Database 2005.
I am tying to compare values between 2 databases.
I am able to retrieve the variable [StudentName] from tStudent Table via a SELECT WHERE sql statement, as follow:
Now, I have another table named StudentDetails. It has 3 columns, StudentName,Address and ContactNum:
The situation is that I want to grep the result from the first SQL query on tStudent, which returns me a list of Students whose [Status]=DELETED.
And from the list of Students queried , I want to take one Student at a time, and search through my [StudentDetails] table.
If it exist in [StudentDetails], I wan to use a way to store the variable [StudentName] from StudentDetails table and display it in GridView on my webpage.
(open to many solutions here. store in database; display result in GridView; store in array; etc)
May I know what the ways and steps I can take to achieve the result?
Step by step guide and code snippets are very much appreciated, because I am quite weak in C# programming.
you can do like this:
Use Visual Studio to create a DataSet name StudentDS, create table name "Student" in this DataSet, this table will contain 3 table columns: String StudentName; String Address; String ContactNum;
Fill deleted students into this DataSet:
DataSet dset = new StudentDS();
String connectionString = "";// depends on your database system, refer to http://www.connectionstrings.com
using (connection = new OdbcConnection(connectionString))
{
connection.Open();
command.Connection = connection;
command.CommandText = "select StudentName, Address, ContactNum from tStudent WHERE status = 'DELETE'";
OdbcDataAdapter da = new OdbcDataAdapter();
da.SelectCommand = command;
da.Fill(dset, "Student");
}
- After you get this DataSet, you can iterate on its row to do what you want.
if(dset.Tables[0].Rows != null) {
for (int i = 0; i < dset.Tables[0].Rows.Count, i++){
if(!ExistInStudentDetail(dset.Tables[0].Rows[i]["StudentName"]))
{
dset.Tables[0].Rows.remove(i);
i--;
}
}
}
//here, boolean ExistInStudentDetail(String StudentName) is a method, you can create sql for this as same in above.
In your form, add a new DataGridView name "StudentForm",add 1 column for this DataGridView name "StudentName", and set its binding property to "StudentName" (same column name in DataSet), and then set DataSource of this grid.
StudentForm.DataSource = dSet;
HTH.
This is a fairly simple issues but the scope is pretty large. So here goes:
First you should really make sure you have unique columns in the tables you are searching this allows you to modify those individual rows and make sure that you are modifying the correct one. I didn't see any ID columns in the screenshot so I just wanted to cover this.
Second I would create a class for students. In here I would create fields or properties of all the information that I wanted.
class Student
{
public string Name { get; private set; }
public string Address { get; private set; }
public string ContactNum { get; private set; }
}
you can either use a constructor in the above class and fill the properties with that or you can fill in each through your select your choice.
Third I would create a List<Student> students; this will be used as your reference list
List<Student> deletedStudents = SQL Select Statement;
Fourth I would then create another List<Student> detailedStudents;
Finally I would compare the two lists and then do something when a match is found.