How to bind Access database to DataTable? - c#

I'm new to C# and Visual Studio.
I'm working on a project that is searching through a database of information.
I want to bind a database (Microsoft access file) to my datagridview
but I want it to work with my preexisting code which utilizes a datatable converted into a dataview.
My database has a lot of information and I don't want to put it in manually. I've tried binding the information directly to the datagridview (through datasource in the properties) but then searching doesn't work**. I've looked into sql but im trying to avoid learning 2 languages at the same time.
My projects basic functionality contains: 1 combobox (idCbo) containing the search query's 1 datagridview for displaying the information
this setup is for searching one column only, im going to duplicate the code for the oher columns
The name of the column in the datagridview selects the column(id) for filtering then the combo box(idCbo) searches that column for matching characters in the datagridview and comboBox list.
the combo box contains the values 1-100 for searching the column
public partial class Form1 : Form
{
DataTable dt = new DataTable();
DataView dataView;
public Form1()
{
InitializeComponent();
dt.Columns.Add("id", typeof(int));
for (int i = 0; i < 100; i++)
dt.Rows.Add(i);
dataView = new DataView(dt);
this.dataGridView1.DataSource = dataView;
}
private void idCbo_SelectedIndexChanged(object sender, EventArgs e)
{
string query = idCbo.Text;
dataView.RowFilter = $"convert(id,'System.String') LIKE '%{query}%'";
}
}
**
Binding the database to the datagridview while using this code renders column titles but not the information and the code cannot access the database, columns or the rows System.Data.EvaluateException: 'Cannot find column ...
Big thanks to Johng for assisting me with the code :)
CURRENT WORKING CODE
public Form1()
{
InitializeComponent();
}
public static BindingSource gridBindingSource;
private void idCbo_SelectedIndexChanged(object sender, EventArgs e)
{
string query = idCbo.Text;
gridBindingSource = (BindingSource)dataGridView1.DataSource;
if (gridBindingSource != null)
{
if (query == "All")
{
gridBindingSource.Filter = "";
}
else
{
gridBindingSource.Filter = "convert(id,'System.String') LIKE '%" + query + "%'";
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the '_InfoFinalv_2___CopyDataSet.Info' table. You can move, or remove it, as needed.
infoTableAdapter.Fill(this._InfoFinalv_2___CopyDataSet.Info);
idCbo.Items.Add("All");
for (int i = 1; i < 100; i++)
{
idCbo.Items.Add(i);
}
idCbo.SelectedIndex = -1;
}
private void idReset_Click(object sender, EventArgs e)
{
idCbo.SelectedIndex = -1;
}

If you have set up the grids data source in the designer “correctly” then using the DataView as you want can be simplified by using the existing BindingSource that is usually created when you set up the grid’s data source in the designer.
We can use the existing grid’s BindingSource and then use it’s Filter property as opposed to converting the BindingSource to a DataView to filter. This will allow us to set the filter in the grid WITHOUT having to “change” the grids data source.
Remove all the code you have in the form constructor obviously leaving the InitializeComponent(); and add the code below to the forms Load event. In the load event all we do is set up the combo box with the proper values. I added an “All” option to allow the user to “un-filter” the data in the grid.
private void Form1_Load(object sender, EventArgs e) {
// TODO: This line of code loads data into the 'database1DataSet.EmployeeDT' table. You can move, or remove it, as needed.
employeeDTTableAdapter.Fill(this.database1DataSet.EmployeeDT); // <- created by the designer
idCbo.Items.Add("All");
for (int i = 1; i < 100; i++) {
idCbo.Items.Add(i);
}
idCbo.SelectedIndex = 0;
}
Then in the combo boxes SelectedIndexChanged event... change the code as shown below. Cast the grids DataSource to a BindingSource and then use its Filter property.
private void idCbo_SelectedIndexChanged(object sender, EventArgs e) {
string query = idCbo.Text;
BindingSource GridBS = (BindingSource)dataGridView1.DataSource;
if (GridBS != null) {
if (query == "All") {
GridBS.Filter = "";
}
else {
GridBS.Filter = "EmpID LIKE '%" + query + "%'";
}
}
}

Here's the tip:
On the form load, make an ajax call to the database and fetch only the required data columns. Return data will be in JSON that can be used as data for DataTable.
I used it in an MVC project recently and it works fine. If you would like I can share the detailed code and logic.
Not sharing the code since I'm not sure if you are on .Net MVC.

Related

How to dynamically filter DataGridView using TextBox data?

I created a simple DataGridViewa with a single column and added a TextBox above.
Currently the text actually a DataTable (I though this would make things easier for filtering) with 2 columns, number and text (I hide the number in the DataGridView ). I can change it to any other class if required.
When the user enters a letter in the TextBox, I want to dynamically filter and show only the lines containing this text.
I load the data to the DataGridView like this:
private void PhrasesForm_Load(object sender, EventArgs e)
{
phrasesDataGridView.ReadOnly = true;
phrasesDataGridView.DataSource = _phrases.Phrases;
phrasesDataGridView.Columns[0].Visible = false;
this.phrasesDataGridView.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}
When add another letter the filter will be readjusted.
What do I write here...
private void filterBox_TextChanged(object sender, EventArgs e)
{
}
If you have a DataGridView, or any Control with a DataSource, consider using Nuget Package BindingListView. It has functionality to sort the binding list by any column on a mouse click, but it also has functionality to filter the data.
List<Customer> customers = ...
BindingListView<Customer> customerView = new BindingListView<Customer>(customers);
dataGridView1.DataSource = customerView;
And presto, if shows all Customers. The columns depend on you column definition.
To filter the customers, for instance, show only customers with born before a certain date:
void ShowCustomers(DateTime limitDate)
{
customerView.ApplyFilter( customer => customer.BirthDay < limitDate));
}
Result: only the older Customers are shown.
You can do it similarly:
void ShowItemsWithPhraseStart(string phraseStart)
{
myView.ApplyFilter(row => row.Phrase.StartsWith(phraseStart));
}
And bingo: the datagrid shows only the row with a value for property Phrase that starts with phraseStart.
private void OnTextBocChanged(object sender, ...)
{
var text = this.TextBox1.Text;
ShowItemsWithPhraseStart(text);
}

Pushing values from DataView built on one Windows Form to another Windows Form

I have 2 windows forms. One form with datagridview and button, and the other form with labels. More or less like Master-Detail design. I have 2 related tables in database. I can fill the datagridview nicely with data from the main table. First I select data on datagridview, and I want to use the button to display the values on the labels located on another form.
Once data is loaded on datagridview; I use the following code for the methods to filter the underlying tables based on selection made from datagridview:
[form 2]
public DataView EnterpriseView()
{
foreach (DataGridViewRow row in Viewer.SelectedRows)
identifier = row.Cells["BusinessName"].Value.ToString();
var EnterpriseVw = new DataView(EnterpriseDT)
{
RowFilter = "BusinessName = '" + identifier + "'"
};
return EnterpriseVw;
}
After returning the view, I want to use the button to push the information to another form that has the labels. I'm not sure about how to get this working. I tried different codes of my own, and it doesn't work
My issue how to code the button_click event. And is there another event I need to call for this to work? How does the button know if I have selected something on the datagridview? How does the datagridview know I have clicked the button? I tied something like this for the button:
[form 2]
private void button1_Click(object sender, EventArgs e)
{
index = Viewer.SelectedRows[0].Index;
Viewer.Rows[index].Selected = true;
//EnterpriseView();
//DetailsView();
//this.Click += new EventHandler(Viewer_SelectionChanged);
if (Viewer.Rows[index].Selected == true)
{
var frm1 = new form1(); //form with labels
//foreach(DataGridViewRow row in Viewer.SelectedRows)
frm1.Publish(); //method that assigns data to labels
}
It doesn't work
I tried using somthing like this for the labels:
[form 1]
public void Publish()
{
var frm2 = new form2();
var vEnterprise = frm2.EnterpriseView();
Email.DataBindings.Add("Text", vEnterprise, "EmailAddress");
}
To Get values from Selected Row of the DataGridView you need to change some properties :
First You need to set SelectionMode to FullRowSelect.
Second You need to set MultiSelect to False.
You can do it through the properties tab.
Then we can use dataGridView1.SelectedRows it will return DataGridViewSelectedRowCollection a list of the selected rows, but since we disabled multiSelect there will always be just one so we can use [0].
Now in the Button_click event Handler
private void button1_Click(object sender, EventArgs e)
{
// To Get The Selected Row
var dr = dataGridView1.SelectedRows[0];
// The Cells Property is going to return DataGridViewCellCollection a list again
// Basically the columns so the first one will be the first column and so on
string item1 = dr.Cells[0].Value.ToString();
string item2 = dr.Cells[1].Value.ToString();
string item3 = dr.Cells[2].Value.ToString();
}
In the Form with the labels Define a second constructor.
Say we Have 3 Labels
We gonna define a second constructor that takes 3 Strings, Like this
public TheSecondForm(String S1, String S2,String S3)
{
label1.Text = S1;
label2.Text = S2;
label3.Text = S3;
}
Then The button Event Handle will become like this :
private void button1_Click(object sender, EventArgs e)
{
var dr = dataGridView1.SelectedRows[0];
string item1 = dr.Cells[0].Value.ToString();
string item2 = dr.Cells[1].Value.ToString();
string item3 = dr.Cells[2].Value.ToString();
// What was added
TheSecondForm frm2 = new TheSecondForm(item1, item2, item3);
frm2.Show();
}
By Now the labels will be populated.

Datatable gets reset unexpectedly

I have a datagrid which refreshes every 3 seconds and while refresh happens Datatable gets reset everytime unexpectedly. What i do is adding values from one datatable to another.
However if use 'DefaultView.ToTable' it stores the data.
Basically, I have 2 datatables dtTopSQL and dtCurTopSQL.
dtTopSQL getting new data and adding to the datatable dtCurTopSQL and I want to store the rows in dtCurTopSQL;
// Initial Load the Datatable Structure
private void Main_Load(object sender, EventArgs e)
{
dtTopSQL.Columns.Add("SQL_ID", typeof(string));
dtTopSQL.Columns.Add("Count", typeof(Int16));
dtTopSQL.Columns.Add("CurTime", typeof(DateTime));
}
// Timer start refreshing the datagrid
private void timer_TimerTopSQL(object sender, EventArgs e)
{
dtTopSQL.Clear(); // Clear before the Fill
odaTopSQL = new OracleDataAdapter(getTopSQLString, oradb);
odaTopSQL.Fill(dtTopSQL);
getTopSQL();
}
// Merging datatable starts here.
public void getTopSQL()
{
for (int i = 0; i < dtTopSQL.Rows.Count; i++)
{
bool isDupe = false;
for (j = 0; j < dtCurTopSQL.Rows.Count; j++)
{
if (dtTopSQL.Rows[i][0].ToString() == dtCurTopSQL.Rows[j][0].ToString())
{
dtCurTopSQL.Rows[j][1] = int.Parse(dtCurTopSQL.Rows[j][1].ToString()) + int.Parse(dtTopSQL.Rows[i][1].ToString());
dtCurTopSQL.Rows[j][2] = CurDate;
isDupe = true;
break;
}
}
if (!isDupe)
{
dtCurTopSQL.ImportRow(dtTopSQL.Rows[i]);
dtCurTopSQL.Rows[j][2] = CurDate;
}
}
ugTopSQL.DataSource = dtCurTopSQL; // Bind the merged Datatable.
}
Above code works if i use below before just binding data;
dtCurTopSQL = dtCurTopSQL.DefaultView.ToTable();
However i don't understand why? I want to store data without using DefaultView.ToTable
Could you explain please?
Above code works if i use below before just binding data;
dtCurTopSQL = dtCurTopSQL.DefaultView.ToTable();
However i don't understand why? I want to store data without using
DefaultView.ToTable
ugTopSQL.DataSource = dtCurTopSQL; // Bind the merged Datatable.
The first time this statement is executed, the DataSource changes from null to dtCurTopSQL. Each subsequent time it is executed, the DataSource setter checks if the new value is the same as its existing value and if so it does nothing.
By assigning a new table to dtCurTopSQL before assigning the DataSource you are assigning a new object to the DataSource, so it acts according with the new source.
A simple solution would be to add
ugTopSQL.DataSource = null;
as the first statement in the getTopSQL method.
What you are doing is absolutely unnecessary. When UltraGrid is bound to DataTable each time data table's rows change the grid will automatically show this change. So change your Load event like this:
// Initial Load the Datatable Structure
private void Main_Load(object sender, EventArgs e)
{
dtTopSQL.Columns.Add("SQL_ID", typeof(string));
dtTopSQL.Columns.Add("Count", typeof(Int16));
dtTopSQL.Columns.Add("CurTime", typeof(DateTime));
ugTopSQL.DataSource = dtCurTopSQL; // Bind the merged Datatable.
}
Then remove this row from getTopSQL method:
ugTopSQL.DataSource = dtCurTopSQL; // Bind the merged Datatable.
This way whatever changes you made to dtCurTopSQL will be automatically shown in the grid without setting the grid's DataSource, which by the way is expensive operation and should not be done each three seconds.

add the new row client has entered to data source on radgirdview

I am new to using the telerik tools for winforms and I was wondering if anyone could help me to find the best way so that the client can add a new row on the radgrid and for it to show this change on the data source.
so far I have the radgrid set up so that the client can add a new row. I just need to bind it to the data source.
private void radGridView1_Click(object sender, EventArgs e)
{
this.radGridView1.AllowEditRow = false;
this.radGridView1.AllowAddNewRow = true;
this.radGridView1.AllowDeleteRow = false;
this.radGridView1.AddNewRowPosition = Telerik.WinControls.UI.SystemRowPosition.Top;
this.radGridView1.MasterTemplate.AddNewBoundRowBeforeEdit = true;
radGridView1.EnableAlternatingRowColor = true;
}
Have a look at the UserAddedRow event for RadGridView. This is fired after the user added a new row to the grid. You could then add the new entries to a source list or data table.
List<Foo> _lSource = new List<Foo>();
DataTable _tSource = new DataTable();
private void radGridView1_UserAddedRow(object sender, GridViewRowEventArgs e)
{
Foo foo = new Foo();
foo.Col1 = e.Row.Cells["col1"].Value.ToString();
foo.Col2 = e.Row.Cells["col2"].Value.ToString();
_lSource.Add(foo);
_tSource.Rows.Add(e.Row.Cells["col1"].Value.ToString(), e.Row.Cells["col2"].Value.ToString());
}
I added both possibilities in the same code snippet. Just choose one (list or table). I just created a random class to show you an example. You can create your own classes and name the properties as you want. Just note that the column (col1 and col2 in my example) must exist, otherwise you'll get a NullReferenceException. If you're using DataTable you have to add the columns once before adding rows.
_tSource.Columns.Add("col1");
_tSource.Columns.Add("col2");
I also recommend not to update the RadGridView properties on a click event of RadGridView. Because it is enough to set those properties once. Now you set them every time you click in your grid view. Either move them to the Load event of your form or set it directly in designer properties.
private void Form_Load(object sender, EventArgs e)
{
radGridView1.AllowEditRow = false;
radGridView1.AllowAddNewRow = true;
radGridView1.AllowDeleteRow = false;
radGridView1.AddNewRowPosition = Telerik.WinControls.UI.SystemRowPosition.Top;
radGridView1.MasterTemplate.AddNewBoundRowBeforeEdit = true;
radGridView1.EnableAlternatingRowColor = true;
}
RadGridView supports data binding to variety of data sources and CRUD operation will be handled for you automatically. Here you can find information on the supported data sources: Telerik UI for WinForms Documentation
And here is how to bind to a DataTable, where all CRUD operations work out of the box
RadGridView radGridView1 = new RadGridView();
this.Controls.Add(radGridView1);
radGridView1.Dock = DockStyle.Fill;
DataTable table = new DataTable();
table.Columns.Add("col1");
table.Columns.Add("col2");
table.Rows.Add("value1", "value1");
table.Rows.Add("value2", "value2");
radGridView1.DataSource = table;
Here is also a design time tutorial.

What is the most efficient way to sync two datagridviews?

Here is what I am trying to accomplish:
I have two datagriddviews. Datagridview1 holds a list of urls. Datagridview2 is basically a bookmarking system. A user can right-click on a url in datagridview1 and select to favorite or "bookmark" it. That row will then be copied to datagridview2. The font on the row on datagridview1 will then become bolded to show that it has been favorited and added to datagridview2. How do I keep these two synced? For example if a user removes a favorite on datagridview2 it should become unbolded on datagridview1 as it is no longer a favorite. What is an efficient way to keep these 2 synced to each other since the indexes will be different? I Had through about just iterating through each row to see if the cell content strings were equal and then unbolding it based off that but that seems very inefficient. Any suggestions on how to accomplish this?
As a good option you can have a data source like a DataTable having and string Url and a bool Favorite column.
Then, bind the first grid to the data table as its DataSource.
Bind the second grid to a DataView that you created from your data table and set Favorite=true as its Filter.
Then handle RowPrePaint event of first DataGridView and set the font to be bold if the row has favorite=true, else set the font to be regular.
For add button and remove button, set the value of favorite column on data row behind the current row and then call EndEdit on data row.
Initialization
private void Form1_Load(object sender, EventArgs e)
{
//Create DataTable
var data = new DataTable();
data.Columns.Add("Url", typeof(string));
data.Columns.Add("Favorite", typeof(bool));
//Fill Data
data.Rows.Add("http://stackoverflow.com", true);
data.Rows.Add("http://bing.com", false);
data.Rows.Add("http://google.com", false);
//Set DataBidnings to allUrlsDGV
this.allUrlsDGV.DataSource = data;
//Set DataBidnings to favoriteUrlsDGV
var favoriteData = new DataView(data);
favoriteData.RowFilter = "Favorite=true";
this.favoriteUrlsDGV.DataSource = favoriteData;
}
RowPrePaint
private void allUrlsDGV_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
var f = e.InheritedRowStyle.Font;
var drv = (DataRowView)allUrlsDGV.Rows[e.RowIndex].DataBoundItem;
if (drv.Row.Field<bool>("Favorite") == true)
allUrlsDGV.Rows[e.RowIndex].DefaultCellStyle.Font = new Font(f, FontStyle.Bold);
else
allUrlsDGV.Rows[e.RowIndex].DefaultCellStyle.Font = new Font(f, FontStyle.Regular);
}
Add and Remove Buttons
private void AddButton_Click(object sender, EventArgs e)
{
if (allUrlsDGV.CurrentRow == null)
return;
var drv = (DataRowView)allUrlsDGV.CurrentRow.DataBoundItem;
drv.Row["Favorite"] = true;
drv.Row.EndEdit();
}
private void RemoveButton_Click(object sender, EventArgs e)
{
if (favoriteUrlsDGV.CurrentRow == null)
return;
var drv = (DataRowView)favoriteUrlsDGV.CurrentRow.DataBoundItem;
drv.Row["Favorite"] = false;
drv.Row.EndEdit();
}

Categories