c# windows forms / basic data grid - c#

One of the problems I am having with c# is that there seems to be so much information online that I am having trouble finding the right answer to the most basic of questions.
I am trying to do something simple:
I have a button, I click it, it queries the database and populates a datagrid on my windows form.
private void button1_Click(object sender, EventArgs e)
{
SqlConnection c = new SqlConnection("Data Source = (local); Integrated Security = true; Initial Catalog = pubs; ");
c.Open();
// 2
// Create new DataAdapter
SqlCommand cmd = c.CreateCommand();
cmd.CommandText = #" SELECT * FROM Authors ";
SqlDataReader reader = cmd.ExecuteReader();
dataGridView1.DataSource = reader;
dataGridView1.DataBind();
}
Error 1 'System.Windows.Forms.DataGridView' does not contain a definition for 'DataBind' and no extension method 'DataBind' accepting a first argument of type 'System.Windows.Forms.DataGridView' could be found.....
I am probably missing a "using directive" but which one? Multiple Google searches tell me how to bind a Yahoo RSS Feed to a gridview or provide various obscure details on "using directives".
Maybe I am using the SqlDataReader incorrectly. Should I be using SqlAdapter instead? What happened to all the good basic tutorials online for windows c# forms? A few months ago I found a couple great tutorials, but they seem to have lost their pageranking and I cannot find them anymore using basic google searches.

You're not missing a using directive; it's just that the WinForms DataGridView doesn't have a DataBind method. Just assigning DataSource is enough to get the binding to happen; you don't need to call a method as well.
However, I don't think you can assign a SqlDataReader as the DataSource. According to the DataSource property documentation in MSDN, the DataSource must be an IList, an IListSource, an IBindingList or an IBindingListView. You will probably instead need to load the data into a DataTable or DataSet (or an object data source populated using an object-relational mapper), and use that as the DataSource.

Try this instead:
using (SqlConnection conn = new SqlConnection("your connection string"))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(#"SELECT * FROM Authors", conn))
{
using (SqlDataAdapter adap = new SqlDataAdapter(cmd))
{
DataTable dt = new DataTable();
adap.Fill(dt);
dataGridView1.DataSource = dt;
}
}
}
The DataGridView does not have a DataBind() method because it doesn't need one. Setting the DataSource property handles the binding for you. The using() blocks will automatically close and dispose of everything for you as well.
Note: you should replace "your connection string" with a valid connection string. I left yours out of my sample to avoid the horizontal scrollbars, and I'm not sure yours is valid anyway. You may get a runtime error when you run the code using your connection string. www.connectionstrings.com is a great resource for figuring out a valid connection string.
Update: instead of the nested using() blocks, you can also do it like this:
using (SqlConnection conn = new SqlConnection("..."))
using (SqlCommand cmd = new SqlCommand(#" SELECT * FROM Authors", conn))
using (SqlDataAdapter adap = new SqlDataAdapter(cmd))
{
conn.Open();
DataTable dt = new DataTable();
adap.Fill(dt);
dataGridView1.DataSource = dt;
}
I prefer the nested style, but it's "half of one, six dozen of the other" to me. Typically, I would encapsulate code like this into a class (called "DataGetter" or whatever) with a static method like:
public static DataTable GetData(string query)
{
// do all the connecting and adapting and filling and so forth
}
so that the code in your button click would be as simple as:
dataGridView1.DataSource = DataGetter.GetData("SELECT * FROM AUTHORS");
However, I would not do this in any performance-critical section of my code, since you sometimes want to keep a SqlCommand object (and its SqlParameter collection) around between calls. You do not need to keep SqlConnection objects around between calls, thanks to connection pooling (in fact, you don't want to keep them around under any circumstances).

Related

How to load the datagridview using OOP C#

I am a beginner at C# and .NET oop concepts. I want to load the datagridview. I don't know how to pass the data. What I tried so far I attached below.
I created a class std
public void get()
{
SqlConnection con = new SqlConnection("server =.; initial catalog=testdb; User ID=sa; Password=123");
string sql = "select * from std";
con.Open();
SqlCommand cm = new SqlCommand(sql, con);
SqlDataReader dr = cm.ExecuteReader();
while ( dr.Read())
{
string stname = dr["st_name"].ToString();
string nicnum = dr["nic"].ToString();
}
con.Close();
}
Form: I am getting data like this way
std ss = new std();
ss.get();
dataGridView1.Rows.Clear();
If I wrote like this way how to pass data into the datagridview columns? I am stuck in this area
It's easier like this:
public void FillGrid()
{
var dt = new DataTable();
var da = new SqlDataAdapter("select * from std", "server =.; initial catalog=testdb; User ID=sa; Password=123");
da.Fill(dt);
dataGridView1.DataSource = dt;
}
but if you're going to use such a low level method of database access you should consider adding a DataSet type of file to your project; visual studio will write all this code and more for you with a few mouse clicks, and it makes a good job of creating tables and adapters that are a lot easier to work with
you have made multiple mistakes. First you read data wirh dataraeader and in every iteration define two stname and nimnum variables like. So when loop ends variables are destroyed. You have to define data table and read data by dataraeader and and add them to it row by row. Or read by sqldataadapter and read it immediately and pass to datatable object.Finnaly you pass datatable as return object of function. Use this vala as datasource of datagridview property if I'm not wrong.

Populating a datagrid using a OleDbCommand and maintaining the items source once connection is closed

I've got the code below to populate a datagrid in a C# WPF application. I've been told that I should "Close" the connection as soon as I have retrieved the data I need. However once the connection has been closed so does the reader and I lose the itemsource for my datagrid. How would I maintain the contents of this datagrid once I close the connection and reader? To clarify, once the data has been retrieved the datagrid contents won't change.
Code:
OleDbConnection conn = new OleDbConnection { ConnectionString = DataBase.ConnectionString() };
conn.Open();
OleDbDataReader reader = null;
OleDbCommand command = new OleDbCommand("SELECT [ID],[ClassName] FROM Class WHERE TeacherID = #TeacherID", conn);
command.Parameters.AddWithValue("#TeacherID", Properties.Settings.Default.UserID);
reader = command.ExecuteReader();
_ClassGrid.ItemsSource = reader; //_ClassGrid is my DataGrid
conn.Close();
The code will function fine, if I remove the conn.close();
Thanks for any help in advance, it's much appreciated.
So when you are displaying data, you need to "bind" or set your item source to a disconnected object, and not the data source directly. You should also ensure that you dispose of all disposable objects in your code. If you do not have a lot of data, the simplest thing to do would be use fill a datatable. If you do have a lot of data, then you should use POCO's in a collection. The following code shows for a datatable:
DataTable stuffToDisplay = new DataTable();
using (var conn = new OleDbConnection { ConnectionString = DataBase.ConnectionString() })
{
conn.Open();
using(var command = new OleDbCommand("SELECT [ID],[ClassName] FROM Class WHERE TeacherID = #TeacherID", conn))
{
command.Parameters.AddWithValue("#TeacherID", Properties.Settings.Default.UserID);
using(var adapter = new OleDbDataAdapter(command))
{
adapter.Fill(stuffToDisplay);
}
}
}
// Looks like you need to set to default view in WPF
_ClassGrid.ItemsSource = stuffToDisplay.DefaultView;
That is kind of a rough cut, so take that with a grain of debugging salt. But give something like that a shot. Here is a link to the data adapter class on MSDN, it should give you some idea of how to use it.
https://msdn.microsoft.com/en-us/library/system.data.oledb.oledbdataadapter(v=vs.110).aspx
If you do have a lot of data, then you should consider using objects instead of a data table (I think that is more excepted anyways). I would do some reading on binding and observable collections to get your WPF right for that situation, using a data reader to fill objects (or entity framework). Here are some related questions to that, which would be faster than me posting a ton of code on it.
WPF - Binding to collection in object
Bind collection of objects to ListBox
https://social.msdn.microsoft.com/Forums/vstudio/en-US/1f211699-5764-47ae-9ad9-d9ff2875e9c0/how-to-set-listboxitemssource-to-a-collection-of-objects-in-xaml?forum=wpf
https://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.itemssource%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396

How to display data in gridview from MS Access?

I want to display information of user stored in a MS Access database. The user enters his userid and on clicking a button following function is called. But no data is being displayed. What am I doing wrong ?
System.Data.OleDb.OleDbConnection con;
System.Data.OleDb.OleDbDataAdapter da;
protected void Button1_Click(object sender, EventArgs e)
{
con = new System.Data.OleDb.OleDbConnection();
con.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;"
+ "Data Source=C:\\Users\\sam\\Desktop\\mydb.mdb";
con.Open();
string sql = "SELECT * From Leave where userid="+Textbox1.Text;
da = new System.Data.OleDb.OleDbDataAdapter(sql, con);
DataTable t = new DataTable();
da.Fill(t);
GridView1.DataSource = t;
con.Close();
}
You need to call GridView1.DataBind()
GridView1.DataSource = t;
GridView1.DataBind();
Just a side-note, it is good practice to wrap your connection with using
using(con = new System.Data.OleDb.OleDbConnection())
{
con = new System.Data.OleDb.OleDbConnection();
con.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;"
+ "Data Source=C:\\Users\\sam\\Desktop\\mydb.mdb";
con.Open();
...
...
}
This ensures your connection is properly disposed after use
You should use bind function:
protected void Button1_Click(object sender, EventArgs e)
{
con = new System.Data.OleDb.OleDbConnection();
con.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;"
+ "Data Source=C:\\Users\\sam\\Desktop\\mydb.mdb";
con.Open();
string sql = "SELECT * From Leave where userid="+Textbox1.Text;
da = new System.Data.OleDb.OleDbDataAdapter(sql, con);
DataTable t = new DataTable();
da.Fill(t);
GridView1.DataSource = t;
GridView1.DataBind();
con.Close();
}
First off, please, please please don't concatenate your WHERE parameters in your SQL. Use Parameters. Second, Add a "using System.Data.OleDb" statement at the top of your module, so that you are not having to type things like:
System.Data.OleDb.OleDbDataAdapter
Over and over again.
Try the following code. Personally, when I have to work with data tables and such, I prefer to avoid all the DataAdapter nonsense, and keep it as simple as possible.
Note in the code below:
the "using" blocks. These place the variables created within them inside their own scope, and take care of disposal and such for you.
I used an OleDb Parameter instead of concatenating criteria. This is a much safer way to do things, and creates much cleaner and more readable code as well, especially in cases where you have several criteria in your WHERE clause.
I assume your UserID input is a string, since you are grabbing the value from a Textbox. If it is in fact an int value (such as an auto-incrementing id in MS Access) you will need to use an int data type instead. You may have to mess with it a little. When you are still figuring this stuff out, it can be a bit painful. However, using parameters increases security and maintainability.
Once you have obtained a data table as the return from the MyUsers method, you should be able to simply set the data source of your Gridview. If you have difficulties still, do as Steve suggests and check the Autogenerate columns property in the designer, or set it in code.
Not that I have moved the connection string to the project Properties/Settings. You should find this in the solution designer. Place your connection string there, in one spot, and you can obtain it from anywhere in your code. If you later change the connection string (such as moving your Db to another computer, server share, etc) you need only change it in one place.
SAMPLE CODE:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data.OleDb; // put this here, and stop writing long namespaces inline
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Where possible, move code out of specific event handlers
// into methods which can be re-used from other client code.
// Here, I pulled the actual data access out into separate methods,
// and simply call it from the event handler:
this.LoadGridView(textBox1.Text);
}
private void LoadGridView(string UserID)
{
// Now we can load the gridview from other places in our
// code if needed:
this.dataGridView1.DataSource = this.MyUsers(UserID);
}
private DataTable MyUsers(string UserID)
{
var dt = new DataTable();
// Use a SQL Paramenter instead of concatenating criteria:
string SQL = "SELECT * FROM Leave WHERE userid = #UserID";
// The "using" statement limits the scope of the connection and command variables, and handles disposal
// of resources. Also note, the connection string is obtained from the project properties file:
using(OleDbConnection cn = new OleDbConnection(Properties.Settings.Default.MyConnectionString))
{
using (var cmd = new OleDbCommand(SQL, cn))
{
// For simpler things, you can use the "AddWithValue" method to initialize a new parameter,
// add it to the Parameters collection of the OleDBCommand object, and set the value:
cmd.Parameters.AddWithValue("#UserID", UserID);
// Get in, get out, get done:
cn.Open();
dt.Load(cmd.ExecuteReader());
cn.Close();
}
}
return dt;
}
}
}
Hope that helps. It's not how everyone might do it, but I have found it provides maximum flexibility, when you must work with MS Access.

Why does my application hang while trying to close a SqlConnection object?

I am trying to get column information in C# from a SQL table on SQL Server. I am following the example in this link: http://support.microsoft.com/kb/310107 My program strangely gets hung up when it tries to close the connection. If the connection is not closed, the program exits without any Exceptions. Here's my code:
SqlConnection connection = new SqlConnection(#"MyConnectionString");
connection.Open();
SqlCommand command = new SqlCommand("SELECT * FROM MyTable", connection);
SqlDataReader reader = command.ExecuteReader(CommandBehavior.KeyInfo); // If this is changed to CommandBehavior.SchemaOnly, the program runs fast.
DataTable table = reader.GetSchemaTable();
Console.WriteLine(table.Rows.Count);
connection.Close(); // Alternatively If this line is commented out, the program runs fast.
Putting the SqlConnection inside a using block also causes the application to hang unless CommandBehavior.KeyInfo is changed to CommandBehavior.SchemaOnly.
using (SqlConnection connection = new SqlConnection(#"MyConnectionString"))
{
connection.Open();
SqlCommand command = new SqlCommand("SELECT * FROM MyTable", connection);
SqlDataReader reader = command.ExecuteReader(CommandBehavior.KeyInfo); // If this is changed to CommandBehavior.SchemaOnly, the program runs fast even here in the using
DataTable table = reader.GetSchemaTable();
Console.WriteLine(table.Rows.Count);
}
The table in question has over 3 million rows, but since I am only obtaining the Schema information, I would think this wouldn't be an issue. My question is: Why does my application get stuck while trying to close a connection?
SOLUTION: Maybe this isn't optimal, but it does work; I inserted a command.Cancel(); statement right before Close is called on connection:
SqlConnection connection = new SqlConnection(#"MyConnectionString");
connection.Open();
SqlCommand command = new SqlCommand("SELECT * FROM MyTable", connection);
SqlDataReader reader = command.ExecuteReader(CommandBehavior.KeyInfo); // If this is changed to CommandBehavior.SchemaOnly, the program runs fast.
DataTable table = reader.GetSchemaTable();
Console.WriteLine(table.Rows.Count);
command.Cancel(); // <-- This is it.
connection.Close(); // Alternatively If this line is commented out, the program runs fast.
I saw something like this, long ago. For me, it was because I did something like:
SqlCommand command = new SqlCommand("SELECT * FROM MyTable", connection);
SqlDataReader reader = command.ExecuteReader();
// here, I started looping, reading one record at a time
// and after reading, say, 100 records, I'd break out of the loop
connection.Close(); // this would hang
The problem is that the command appears to want to complete. That is, go through the entire result set. And my result set had millions of records. It would finish ... eventually.
I solved the problem by adding a call to command.Cancel() before calling connection.Close().
See http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=610 for more information.
It looks right to me overall and I think you need a little optimization. In addition to the above suggestion regarding avoiding DataReader, I will recommend to use connection pooling. You can get the details from here :
http://www.techrepublic.com/article/take-advantage-of-adonet-connection-pooling/6107854
Could you try this?
DataTable dt = new DataTable();
using(SqlConnection conn = new SqlConnection("yourConnectionString"))
{
SqlCommand cmd = new SqlCommand("SET FMTONLY ON; " + yourQueryString + "; SET FMTONLY OFF;",conn);
conn.Open();
dt.Load(cmd.ExecuteReader());
}
SET FMTONLY ON/OFF from MSDN seems the way to go
There is an specific way to do this, using SMO (SQL Server management objects)
You can get the collection of tables in the database, and then read the properties of the table you're interested in (columns, keys, and all imaginable properties)
This is what SSMS uses to get and set properties of all database objects.
Look at this references:
Database.Tables Property
Table class
This is a full example of how to get table properties:
Retrieving SQL Server 2005 Database Info Using SMO: Database Info, Table Info
This will allow you to get all the possible information from the database in a very easy way. there are plenty of samples in VB.NET and C#.
I would try something like this. This ensures all items are cleaned up - and avoids using DataReader. You don't need this unless you have unusually large amounts of data that would cause memory issues.
public void DoWork(string connectionstring)
{
DataTable dt = new DataTable("MyData");
using (var connection = new SqlConnection(connectionstring))
{
connection.Open();
string commandtext = "SELECT * FROM MyTable";
using(var adapter = new SqlDataAdapter(commandtext, connection))
{
adapter.Fill(dt);
}
connection.Close();
}
Console.WriteLine(dt.Rows.Count);
}

Issues querying Access '07 database in C#

I'm doing a .NET unit as part of my studies. I've only just started, with a lecturer that as kinda failed to give me the most solid foundation with .NET, so excuse the noobishness.
I'm making a pretty simple and generic database-driven application. I'm using C# and I'm accessing a Microsoft Access 2007 database.
I've put the database-ish stuff in its own class with the methods just spitting out OleDbDataAdapters that I use for committing. I feed any methods which preform a query a DataSet object from the main program, which is where I'm keeping the data (multiple tables in the db).
I've made a very generic private method that I use to perform SQL SELECT queries and have some public methods wrapping that method to get products, orders.etc (it's a generic retail database).
The generic method uses a separate Connect method to actually make the connection, and it is as follows:
private static OleDbConnection Connect()
{
OleDbConnection conn = new OleDbConnection(
#"Provider=Microsoft.ACE.OLEDB.12.0; Data Source=C:\Temp\db.accdb");
return conn;
}
The generic method is as follows:
private static OleDbDataAdapter GenericSelectQuery(
DataSet ds, string namedTable, String selectString)
{
OleDbCommand oleCommand = new OleDbCommand();
OleDbConnection conn = Connect();
oleCommand.CommandText = selectString;
oleCommand.Connection = conn;
oleCommand.CommandType = CommandType.Text;
OleDbDataAdapter adapter = new OleDbDataAdapter();
adapter.SelectCommand = oleCommand;
adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
adapter.Fill(ds, namedTable);
return adapter;
}
The wrapper methods just pass along the DataSet that they received from the main program, the namedtable string is the name of the table in the dataset, and you pass in the query you wish to make.
It doesn't matter which query I give it (even something simple like SELECT * FROM TableName) I still get thrown an OleDbException, stating that there was en error with the FROM clause of the query. I've just resorted to building the queries with Access, but there's still no use. Obviously there's something wrong with my code, which wouldn't actually surprise me.
Here are some wrapper methods I'm using.
public static OleDbDataAdapter GetOrderLines(DataSet ds)
{
OleDbDataAdapter adapter = GenericSelectQuery(
ds, "orderlines", "SELECT OrderLine.* FROM OrderLine;");
return adapter;
}
They all look the same, it's just the SQL that changes.
Have you tried something more simple to see if you have connectivity to the table you are looking for. Something like
DataSet ds = new DataSet();
using (OleDbConnection myConnection = new OleDbConnection
(#"Provider=Microsoft.ACE.OLEDB.12.0; Data Source=C:\Temp\db.accdb"))
{
myConnection.Open();
OleDbDataAdapter myAdapter = new OleDbDataAdapter("SELECT OrderLine.* FROM OrderLine;, myConnection);
myAdapter.TableMappings.Add("Table", "TestTable");
myAdapter.Fill(ds);
}
Then from there check to see if stuff is in ds with
ds.Tables[0].Rows.Count()
This will actually show you if you are hitting the DB and getting results. From there you can make it more elegant
Square brackets seem to have fixed the problem. Turns out I was using a keyword. Hmph.

Categories