(this is another question, since my original post, I asked too many questions in one)
Let me state this first. I am pretty much completely new to ASP coding. I am working on a little side project that requires me to use ASP instead of PHP that I sort of did before. I have been looking at solutions for past 2 days, and have tried many things, but can't seem to get it to work with my code. I have been part of this site for some time, so I do know how it works. I would not be asking here if I wasn't already trying to do this on my own for some time. I have learned a huge amount of information about SQL on here, so I hope to do the same with ASP.
Question:
Number of Returned Rows display
When the page initially loads, it has only a TextBox1 and a Button. If I don't enter anything in the box and hit the button, it will load my GridView with all the Data rows from the SQL Select. When I do hit the button, I would like to display the amount of rows that has been returned next to the button.
GridView1 :
<asp:TextBox ID="TextBox1" runat="server" Width="265px" Height="22px" CssClass="myBox"></asp:TextBox>
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Search Fields" CssClass="myButton" />
<asp:GridView ID="GridView1" OnPageIndexChanging="GridView1_PageIndexChanging" OnSorting="GridView1_Sorting" runat="server" AutoGenerateColumns="true" CellPadding="4" EnableModelValidation="True" EnableTheming="True" ForeColor="#333333" GridLines="None" Width="100%" style="margin-top: 0px; text-align: center;" AllowPaging="True" AllowSorting="True" >
<AlternatingRowStyle BackColor="White" ForeColor="#284775" />
<EditRowStyle BackColor="#999999" />
<FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
<HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
<PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Center" />
<RowStyle BackColor="#F7F6F3" ForeColor="#333333" />
<SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />
</asp:GridView>
Code Behind (DB name and Password taken out):
SqlConnection vid = new SqlConnection("Data Source=ENF;Initial Catalog=***Database Name***;Persist Security Info=True;User ID=sa;Password=***Password***");
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
GridView1.DataSource = GetData();
GridView1.DataBind();
}
}
protected void Button1_Click(object sender, EventArgs e)
{
String str = "SELECT ab.NAME as [Customer] ,ISNULL(ab.TELEPHONE1,'') as [Phone #] ,ISNULL(pb.NAME,'') as [Product] ,ISNULL(aeb.NEW_PRODUCTVERSION,'') as [Version] ,CASE WHEN ab.STATUSCODE = 1 THEN 'Active' ELSE 'Inactive' END as [Status] ,ISNULL('Sal : ' + c.SALUTATION + ' / ','') + ISNULL('Title : ' + c.JOBTITLE + ' / ','') + ISNULL(a.PRIMARYCONTACTIDNAME,'') as [Primary Contact] ,ISNULL(c.TELEPHONE1,'') as [Contact Phone] FROM ACCOUNTBASE ab LEFT JOIN ACCOUNTEXTENSIONBASE aeb on ab.ACCOUNTID = aeb.ACCOUNTID LEFT JOIN PRODUCTBASE pb on aeb.NEW_PRIMARYPRODUCTID = pb.PRODUCTID LEFT JOIN ACCOUNT a on ab.ACCOUNTID = a.ACCOUNTID LEFT JOIN CONTACT c on a.PRIMARYCONTACTID = c.CONTACTID WHERE ((ab.NAME LIKE '%' + #search + '%') OR (aeb.NEW_PRODUCTVERSION LIKE '%' + #search + '%') OR (pb.NAME LIKE '%' + #search + '%') OR (a.PRIMARYCONTACTIDNAME LIKE '%' + #search + '%')) ORDER BY ab.NAME";
SqlCommand xp = new SqlCommand(str, vid);
xp.Parameters.Add("#search", SqlDbType.NVarChar).Value = TextBox1.Text;
vid.Open();
xp.ExecuteNonQuery();
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = xp;
DataSet ds = new DataSet();
da.Fill(ds, "Name");
GridView1.DataSource = ds;
GridView1.DataBind();
vid.Close();
}
Amount of rows that has been returned will store in instance of dataset. so by taking the count you will get the returned row information.The DataSet contains rows, columns,primary keys, constraints, and relations with other DataTable objects. We can get the number of rows from in a Table inside a Dataset by using its Rows.Count property.
ds.Tables[0].Rows.Count
I am on Linux now and I am coding in RoR.... I cannot test now...
But if you need to know how many items has your GridView, just use:
int qty = GridView1.Rows.Count;
Is it what you need?
Some samples... always find in MSDN abour Microsoft Developer info, There are a lot of stuff there!
http://msdn.microsoft.com/library/system.web.ui.webcontrols.gridviewrowcollection.count%28v=vs.110%29.aspx
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.rows%28v=vs.110%29.aspx
To get the total rows
int TotalRecord = dt.Rows.Count();
You can probably just set everything in your OnClick event but if it doesn't work you may have to set your row count when the gridview is being created and then I would create a label and set the label = TotalRecord.text. make the label.visible=False and in your buttons onclick event set the label.visible = true.
If you don't want to show the count until the user clicks the button, this means that you are getting the count on the post back, and not on the original display of the page. But you populate the gridview on the original display. So at the time of the postback, you no longer have the dataset available, so you can't just say textbox1.text=ds.tables(0).rows.count. You would have to either (a) rerun the query, which seems like a waste of resources, or (b) stash the count somewhere on the initial load.
I'd suggest (b). You could populate the text box on the initial load and hide it (textbox1.visible=false). Or you could create a hidden field to keep the count, or put it in the view state, and then retrieve it from there when you need it.
Two by the ways:
One: I'd use meaningful names for controls rather than "textbox1" and "gridview1". It's a lot easier to read a program that says
if update_order.checked then ...
rather than
if checkbox1.checked then ...
Two: You have some unnecessary steps in your database call. You don't need the xp.ExecuteNonQuery. The fill() will execute the query. You're running it twice: the first time you throw away the results, and then the second time you process them. Also, you don't normally need to specify the data type on your parameters. You can just write
xp.parameters.AddWithValue("#search",textbox1.text)
Related
I have a Gridview in a C# ASPX Page called Gridview1 that pulls a Table from a SQL Server and displays it on PageLoad.
There are 10 columns in the Gridview and I need all the distinct values in column 7 which is Code
Code
A
C
D
A
A
D
B
E
R
A
A
C
B
Basically I need some kind of structure, list or even a datatable to get all the distinct values from the Column "Code". From the example above it would be a list or datatable with 6 entries since there are 6 unique codes in the Gridview column.
Distinct
A
B
C
D
E
R
Any ideas how this can be implemented?
Well, ALWAYS but always try to do such code against the data source and NOT the web page HTML (be it a list view, or grid view, repeater or whatever). The web page is for display of the data, not data base stuff.
Now, I suppose in this case, we could operate against the grid, but it usually a WHOLE lot easier to operate against the data.
So, say this grid:
<div style="float:left;width:40%">
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false"
CssClass="table table-hover table-striped"
DataKeyNames="ID">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:Button ID="cmdDel" runat="server"
Text="Delete"
CssClass="btn"
onclick="cmdDel_Click"
onclientClick="return confirm('really delete this?');"/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
<div style="float:left;margin-left:40px">
<h4>City list</h4>
<asp:ListBox ID="ListBox1" runat="server"
DataTextField="City"
DataValueField="City" Width="163px" Height="159px"
></asp:ListBox>
</div>
</div>
So, right next to the grid, I have a list box we will list out each seperate city.
So, code to load is this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid();
}
void LoadGrid()
{
DataTable rstData = MyRst("SELECT * FROM tblHotelsA ORDER BY HotelName");
GridView1.DataSource = rstData;
GridView1.DataBind();
// now fill out a list box of each city
DataTable rstCity = new DataTable();
rstCity = rstData.DefaultView.ToTable(true, "City");
rstCity.DefaultView.Sort = "City";
ListBox1.DataSource = rstCity;
ListBox1.DataBind();
}
And we get this:
So, NOTE how we went to the data source - NOT the grid (the grid is not a database and in most cases we should not think of the grid as such).
However, we could also pull the values out of the grid (can't see why we would do this).
So, lets drop in listbox 2, and add this code that operates against the grid:
our 2nd listbox - this time NOT data bound.
So, this:
<div style="float:left;margin-left:40px">
<h4>City list 2nd example</h4>
<asp:ListBox ID="ListBox2" runat="server"
DataTextField="Text"
DataValueField="Value"
Width="163px" Height="159px"
></asp:ListBox>
</div>
And say a button click or whatever - we run this code to fill out 2nd listbox from the data grid.
FYI:
If you using templated fields, then you have to use findcontrol to get the control.
If you using data fields, then you use .cells[] array/collection.
but, CAUTION, since empty cells will render as a non breaking space &nsb;
So, we convert from html, just to be save, and we have this code:
protected void Button1_Click1(object sender, EventArgs e)
{
List<string> CityList = new List<string>();
foreach (GridViewRow gRow in GridView1.Rows)
{
string sCity = Server.HtmlDecode(gRow.Cells[2].Text);
if (!CityList.Contains(sCity))
CityList.Add(sCity);
}
// display our list in 2nd listbox
CityList.Sort();
foreach (string sCity in CityList)
{
ListBox2.Items.Add(new ListItem(sCity, sCity));
}
}
So, now we have this:
So try to operatee against the data. I mean, we could even say get a list of distinct citys wiht a query, say this:
string strSQL = "SELECT City from tblHotels GROUP BY City";
Datatable rstCity = MyRst(strSQL);
ListBox1.DataSource = rstCity;
ListBox1.DataBind();
So, in most cases??
Much less effort and better to get this data from the data source, and NOT go to the GV - as it is a display and render system - not a data base, nor is it a data source.
Now, in above, I used a helper function MyRst. All that does is return a table based on sql - (became VERY tired very fast having to type that type of code over and over). So that was this:
DataTable MyRst(string strSQL)
{
DataTable rstData = new DataTable();
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
conn.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
}
return rstData;
}
I'm trying to create a web page with the aps.net framework and I connect to the SQL server successfully and I want to display the data from the database in the Grid View and there are a search box and dropdown list but there is an error when I try to search
this is the error message:
Both DataSource and DataSourceID are defined on 'GridView1'. Remove one definition.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: Both DataSource and DataSourceID are defined on 'GridView1'. Remove one definition.
and my code viewpage1.aspx
the GridView
<asp:GridView ID="GridView1" runat="server" BackColor="White" BorderColor="#999999" BorderStyle="Solid" BorderWidth="1px" CellPadding="3" ForeColor="Black" GridLines="Vertical" OnSelectedIndexChanged="GridView1_SelectedIndexChanged" Format="dd/MM/yyyy" AllowSorting="True" AutoGenerateColumns="False" DataKeyNames="InvoiceID" DataSourceID="SqlDataSource3">
<AlternatingRowStyle BackColor="#CCCCCC" />
-and this the sqldatasource
<asp:SqlDataSource ID="SqlDataSource3" runat="server" ConnectionString="<%$ ConnectionStrings:connStr %>" SelectCommand="Select * from [Portal].[fn_GetInvoices] (#SearchText) where CompanyCode=#CompanyCode and InvoiceDate between #fromDate and #toDate">
<SelectParameters>
viewpage1.aspx.cs
sqlcomm.CommandText = sqlquery;
sqlcomm.Connection = sqlconn;
DataTable dt = new DataTable();
SqlDataAdapter adapter = new SqlDataAdapter(sqlcomm);
adapter.Fill(dt);
GridView1.DataSource = dt;
GridView1.DataBind();
This is a common error message.
I OFTEN use the wizards to build a gridview I use this:
From above, I choose create new data source.
I let the wizard run.
BUT THEN we wind up with some MESSAY sqldatasource in the web page. I do NOT like them, and they NEAR ALWAYS just cause you pain.
So, what I will then do this:
Remove the sqldata source from the web markup - you don't' need that mess anyway.
eg this:
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:CSharpWebApp.Properties.Settings.TEST4 %>"
SelectCommand="SELECT [ID], [Fighter], [Engine], [Thrust], [Description], [ImagePath]
FROM [Fighters]"></asp:SqlDataSource>
DELETE the above!!!!
Now WHEN you WILL use code to load up the Gridview? Then you ALSO must turn off (remove) the fact that you NOT GOING to use the sql data source anymore.
So in your GV, remove this:
So, all that error message is telling you is you are trying to set the data source in "code" but you ALREADY have a sql datasource setup in the markup.
So, get in the habit of blowing out and removing the sqldata source on the page, and ALSO remove the GV data source setting in the markup.
So, now say I will have this GV, and NOT have ANY data source in the markup in the web page. (and I recommend you do this). So, I still VERY often use the wizards. Even for a dropdown list, a gridview, repeaters, and even listview (my favorite).
So, run wizards - build the markup
Then blow out (remove) the SqlDataSource, and the
DataSourceID="SqlDataSource1" from the control (in this case gv).
So, as noted, you cannot have BOTH a DataSourceID="SqlDataSource1" and THEN try to use code. It is one or the other - and that's what the error message is telling you.
So, now we have this markup:
(and the wizards generated most of this for me!!!)
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table" >
<Columns>
<asp:BoundField DataField="Fighter" HeaderText="Fighter" />
<asp:BoundField DataField="Engine" HeaderText="Engine" />
<asp:BoundField DataField="Thrust" HeaderText="Thrust" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField HeaderText="View">
<ItemTemplate>
<asp:ImageButton ID="btnImage" runat="server" Height="68px" Width="149px"
OnClientClick ="popimage(this);return false"
ImageUrl = '<%# Eval("ImagePath") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Just nice clean markup - no Sqldatasource junk.
Now we are free to write normal code like a normal human, and we can fill the grid like this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid();
}
void LoadGrid()
{
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand("SELECT * from Fighters", conn))
{
conn.Open();
DataTable rstData = new DataTable();
rstData.Load(cmdSQL.ExecuteReader());
GridView1.DataSource = rstData;
GridView1.DataBind();
}
}
}
And our results are now this:
Ok, so now lets add a search box above the GV to search the GV.
Say you can type in the first few chars of the Fighter jet name, and we want to filter by that:
So, drop in a text box above the GV, + search button.
We now have say this:
<asp:Label ID="Label1" runat="server" Text="Search for Fighter jet" Font-Size="Large"></asp:Label>
<asp:TextBox ID="txtSearch" runat="server" Style="margin-left:15px" Font-Size="Large"></asp:TextBox>
<asp:Button ID="cmdSearch" runat="server" Text="search"
style="margin-left:15px" CssClass="btn"
/>
<br />
<br />
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table" >
<Columns>
So, now the page with searching looks like this:
Say we search for Lockheed - but even just typing in Lock would be fine.
thus:
And now our code can say be this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid("");
}
void LoadGrid(string MySearch)
{
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand("SELECT * from Fighters ", conn))
{
if (MySearch != "")
{
cmdSQL.CommandText += #" WHERE Fighter LIKE #Fighter + '%'";
cmdSQL.Parameters.Add("Fighter", SqlDbType.NVarChar).Value = MySearch;
}
conn.Open();
DataTable rstData = new DataTable();
rstData.Load(cmdSQL.ExecuteReader());
GridView1.DataSource = rstData;
GridView1.DataBind();
}
}
}
protected void cmdSearch_Click(object sender, EventArgs e)
{
LoadGrid(txtSearch.Text);
}
}
So, I am STRONG suggesting that you DUMP the sqldata source on the web page markup. Such Sqldata soruces on the page can be great for one time load or data display. But the VERY instant you want control, filters, and need to use code?
Quite much the SAME instant it is time to drop and toss out and remove the data source from the web markup. You be glad you did, and as you can see, you now have 100% EASY AND SIMPLE control of the data you shove into the GV, and that includes endless possible use of simple code and buttons to add filters etc. to that data.
I am aiming to get data:
From an sql database
To a gridview
To a local table
So far, I am able to use a stored procedure to display the sql data to a gridvew and I am also able to use checkboxes to send the data to a local table.
Issue:
Only one row's data is being submitted to the table, despite the number of checkboxes checked.
ex. I click 3 checkboxes, wanting to get 3 different rows of data to the table. I hit the submit button and when I check the table, only one of the "checked" rows is submitted to the table 3 times.
EDITED Code:
protected void btnSubmit_Click(object sender, EventArgs e)
{
string connectionString = ConfigurationManager.ConnectionStrings["localDataB"].ConnectionString;
using (var sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
string insertStatement = "INSERT into LocalDB (Item1, Item2, Item3)" + "(#Item1, #Item2, #Item3)";
string Data1, Data2;
float Data3;
foreach (GridViewRow gRow in GridView1.Rows)
{
CheckBox ckSel = (gRow.FindControl("checker") as CheckBox);
if (ckSel.Checked)
{
Data1 = Convert.ToString(gRow.Cells[1].Text);
Data2 = Convert.ToString(gRow.Cells[2].Text);
Data3 = Convert.ToInt32(gRow.Cells[3].Text);
using (var sqlCommand = new SqlCommand(insertStatement, sqlConnection))
{
sqlCommand.Parameters.Add("Item1", SqlDbType.Text).Value = Data1;
sqlCommand.Parameters.Add("Item2", SqlDbType.Text).Value = Data2;
sqlCommand.Parameters.Add("Item3", SqlDbType.Float).Value = Data3;
sqlCommand.ExecuteNonQuery();
}
}
}
}
GVbind();
Code for checkbox inside grid:
<asp:GridView ID="GridView1" runat="server" EmptyDataText="No Data Found" BackColor="#CCCCCC" BorderColor="#999999" BorderStyle="Solid" BorderWidth="3px" CellPadding="4"
AutoGenerateColumns="False" CellSpacing="2" ForeColor="Black" DataKeyNames="Data1" Width="70%" Visible="False" ShowFooter="True">
<Columns>
<asp:TemplateField>
<HeaderTemplate>
<asp:Label ID="SelectBox" runat="server" Text="Select"></asp:Label>
</HeaderTemplate>
<ItemTemplate>
<asp:CheckBox ID="checker" runat="server" OnCheckedChanged="checker_CheckedChanged"/>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Data1" HeaderText="Title1" ReadOnly="True" />
<asp:BoundField DataField="Data2" HeaderText="Title2" ReadOnly="True" />
<asp:BoundField DataField="Data3" HeaderText="Title3" />
</Columns>
</asp:GridView>
Ok, it not at all clear what your routine checker_CheckedChanged() does?
You don't need a post-back or anything for the check boxes - but ONLY the one submit button and code stub
. Those data1, data2 etc. will NOT persist in memory anyway. So, you can't use that routine - but you don't need it either.
Unless the grid has multiple pages etc., then dump that routine. You are free to check box any row in the grid. You then have one submit button code, and that routine just needs a bit of change to check all GV rows, and save the check box values.
That submit button code thus can/will look like this:
protected void btnSubmit_Click(object sender, EventArgs e)
{
string connectionString = ConfigurationManager.ConnectionStrings["localDataB"].ConnectionString;
using (var sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
// insert any row with check boxes into temp db
string insertStatement = "INSERT into LocalDB (Item1, Item2, Item3) " +
"values (#Item1, #Item2, #Item3)";
bool Data1, Data2, Data3;
foreach (GridViewRow gRow in GridView1.Rows)
{
Data1 = Convert.ToBoolean(gRow.Cells[0].ToString());
Data2 = Convert.ToBoolean(gRow.Cells[1].ToString());
Data3 = Convert.ToBoolean(gRow.Cells[2].ToString());
// save data if ANY is checked
if (Data1 | Data2 | Data3)
{
using (var sqlCommand = new SqlCommand(insertStatement, sqlConnection))
{
sqlCommand.Parameters.Add("#Item1", SqlDbType.Bit).Value = Data1;
sqlCommand.Parameters.Add("#Item2", SqlDbType.Bit).Value = Data2;
sqlCommand.Parameters.Add("#Item3", SqlDbType.Bit).Value = Data3;
sqlCommand.ExecuteNonQuery();
}
}
}
}
GVbind();
}
I don't see the need for your first routine. The submit button can loop the GV, get the check box values, and if any one of the 3 is checked, then you do the insert.
However, keep in mind that the cells[] collection ONLY works for datafields if you using a templated control and REAL check box, then you need to use findcontrol, and NOT the cells[] collection
Edit:
Ok, first, information provided suggests that a template field is being used.
HOWEVER, we will first address the CheckBoxField code, since it HARD to google and find that answer. So I going to include that answer.
If a check box is a datafield, then you can't change/edit, but you STILL MAY want to iterate over the GV, and get those values.
So, for data fields, say like this:
<asp:CheckBoxField DataField="Active" HeaderText="Active" />
Then our code has to work like this (you have to dig deeper into cells() colleciton.
So, code becomes this:
foreach (GridViewRow gRow in GridView1.Rows)
{
Data1 =(gRow.Cells[0].Controls[0] as CheckBox).Checked;
Data2 = (gRow.Cells[1].Controls[0] as CheckBox).Checked;
Data3 = (gRow.Cells[2].Controls[0] as CheckBox).Checked;
Note how a CheckBoxField requires us to use controls.
However, as noted, with template field (any kind and ANY of them?).
WE DO NOT use the Cells[] collection and templated columns DO NOT appear in the cells collection.
And thus, in a lot (probably most) cases, then we can expect to have a CheckBox control dropped into the markup as a templated field.
Typical looks like this:
<asp:GridView ID="GridView1" runat="server" class="table borderhide"
AutoGenerateColumns="false" DataKeyNames="ID">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" HeaderStyle-Width="200" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField HeaderText="Smoking" ItemStyle-HorizontalAlign="Center" >
<ItemTemplate>
<asp:CheckBox ID="chkSmoking" runat="server"
Checked='<%# Eval("Smoking") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Balcony" ItemStyle-HorizontalAlign="Center" >
<ItemTemplate>
<asp:CheckBox ID="chkBalcony" runat="server"
Checked='<%# Eval("Balcony") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
So in above, the first values (bound fields) WILL BE found in cells colleciton.
However, the templated fields above? we have to do this:
"WHERE ID = #ID";
foreach (GridViewRow gRow in GridView1.Rows)
{
CheckBox cSmoke = (CheckBox)gRow.FindControl("chkSmoking");
CheckBox cBlacony = (CheckBox)gRow.FindControl("chkBalcony");
int PKID = (int)GridView1.DataKeys[gRow.RowIndex]["ID"];
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
cmdSQL.Parameters.Add("#S", SqlDbType.Bit).Value = cSmoke.Checked;
cmdSQL.Parameters.Add("#B", SqlDbType.Bit).Value = cBlacony.Checked;
cmdSQL.Parameters.Add("#ID", SqlDbType.Int).Value = PKID;
cmdSQL.ExecuteNonQuery();
}
}
So, our code then becomes this:
foreach (GridViewRow gRow in GridView1.Rows)
{
Data1 =(gRow.FindControl("Active") as CheckBox).Checked;
Data2 = (gRow.FindControl("Smoking") as CheckBox).Checked;
Data3 = (gRow.FindControl("Balcony") as CheckBox).Checked;
Now of course, you replace the "Text control" id with YOUR data 1, 2, and 3 id that you used.
The rest of the code should be the same.
BIG LESSON OF THE DAY?
Post a wee bit of markup - not a huge boatload - but just a few lines next time. You save world poverty, and I would have been able to post a better answer next time.
So the rule is:
DataBound fields - use cells[] collection.
Templated fields - you have to using .FindControl("name of control id goes here")
Edit#2
Ok, so far, the question is now this:
We have some data. If the user checks the row 1, or 5 or 10 rows, for each of the checked rows, I want to write out the columns/values I have in 3 other columns item1, item2, item3?
Simple queston!!!!
Ok, so the ONLY missing information is WHAT data type field type is item1, item2, and item3? We only really are missing that part.
So, if check box = true, write out those 3 item columns to the new temp table.
So, the code now should be:
bool Data1, Data2, Data3;
foreach (GridViewRow gRow in GridView1.Rows)
{
// get check box -
CheckBox ckSel = (gRow.FindControl("checker") as CheckBox);
// save data if ANY is checked
if (ckSel.Checked)
{
Data1 = Convert.ToBoolean(gRow.Cells[0].Text);
Data2 = Convert.ToBoolean(gRow.Cells[1].Text);
Data3 = Convert.ToBoolean(gRow.Cells[2].Text);
using (var sqlCommand = new SqlCommand(insertStatement, sqlConnection))
{
sqlCommand.Parameters.Add("#Item1", SqlDbType.Bit).Value = Data1;
sqlCommand.Parameters.Add("#Item2", SqlDbType.Bit).Value = Data2;
sqlCommand.Parameters.Add("#Item3", SqlDbType.Bit).Value = Data3;
sqlCommand.ExecuteNonQuery();
}
}
}
As noted, I stated MULTIPLE TIMES NOW, that non templated columns STILL MUST use cells collection. ONLY templated columns can and need to use FindControl. All others MUST continue to use .Cells[] collection.
It should be like this;
sqlCommand.Parameters.AddWithValue("#Item1", data1);
sqlCommand.Parameters.AddWithValue("#Item2", data2);
sqlCommand.Parameters.AddWithValue("#Item3", data3);
Using GridView I am trying to insert all the records, including the edited column into the database, by clicking the submit button. However, it is always inserting the initial data which was fetched from the database after the page load.
I want user to edit the column 'id', and after clicking on submit button, all the records should be inserted into a table. With the current code, the insertion is happening, however in id column, I always get "MG_US-FCWI1.05.30" and "MG_US-FCWI1.05.10" in DB. But I want "MG_US-FCWI1" and "MG_US-FCW" to be inserted. I am using VS2013.
Code used -
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:BoundField DataField="column_name" HeaderText ="Material Name" />
<asp:BoundField DataField="part number" HeaderText ="Material Number" />
<asp:TemplateField HeaderText="Id">
<ItemTemplate>
<asp:TextBox ID="txtedit" runat="server" Text='<%# Eval("id") %>' ReadOnly="false"></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="GL_account" HeaderText ="GL Account" />
</Columns>
</asp:GridView>
<asp:Button ID="btnUpload" Text="Submit" runat="server" OnClick="Submit_Data" Height="21px" style="margin-left: 260px; margin-top: 14px;" />
protected void Submit_Data(object sender, EventArgs e)
{
foreach (GridViewRow row in GridView1.Rows)
{
SqlConnection con = new SqlConnection(getConn());
SqlCommand com = new SqlCommand("insert into test ([Sales Order Id],[input data],Material_Name,[Part Number],id,GL_Account) values (" + sales_order_id + "," + input_id + ",'" + row.Cells[0].Text + "','" + row.Cells[1].Text + "','" + ((TextBox)row.Cells[2].FindControl("txtedit")).Text + "','" + row.Cells[3].Text + "')", con);
con.Open();
com.ExecuteNonQuery();
con.Close();
}
}
This is a REALLY good example as to why you don't want to concatenate that long string (reduces SQL injection issues - but ALSO allows for easier to read and easier to write code).
it beyond hard to read, and thus cleaning this up?
Then the error(s) in the code become clear.
You have this:
((TextBox)row.Cells[2].FindControl("txtedit")).Text
but, it should be this:
((TextBox)row.FindControl("txtedit")).Text
Lesson: Make your code readable, and you find your bugs and issue a lot faster.
Say something like this:
foreach (GridViewRow row in GridView1.Rows)
{
SqlCommand com = new SqlCommand("insert into test " +
"([Sales Order Id],[input data],Material_Name,[Part Number],id,GL_Account)" +
"(#SalesID,#InputData,#MatName,#PartNum,#id,#GLAccount)",
new SqlConnection(getConn()));
com.Connection.Open();
com.Parameters.Add("#SalesID", SqlDbType.Int).Value = sales_order_id;
com.Parameters.Add("#InputData", SqlDbType.Int).Value = input_id;
com.Parameters.Add("#MatName", SqlDbType.NVarChar).Value = row.Cells[0].Text;
com.Parameters.Add("#PartNum", SqlDbType.Int).Value = row.Cells[1].Text;
com.Parameters.Add("#GlAccount", SqlDbType.NVarChar).Value = ((TextBox)row.FindControl("txtedit")).Text;
com.ExecuteNonQuery();
com.Connection.Close();
}
I don't think you need a Convert.ToInt32() around the int values.
But, by just re-writing the code as readable, then the findcontrol error was easy to spot. Also, note how we don't have to guess if we need extra quotes around strings, or not for numbers - so we strong casting here, and that also works better.
Now above is air code, but after I had typed the first parameter, then I started hitting ctr-d to duplicate the line - so the amount of typing I had to do was actually quite small - between intellisense and Ctrl-d, then typing was reduced, but the code is not only easier to read, but we could for example add another row/parameter set over time, and again modifying this code would be less taxed on your carbon based computer (your brain) as opposed to the computer based silicon brain.
I tried looking this one up online, but I guess I'm not sure how I should phrase the question without a full explanation.
I've got a webform with a search bar. When you enter in a search term, it queries a database and pulls information and populates a gridview. I'm using the following code:
string find = "SELECT tblShipments.ShipmentID as [Shipment ID], tblShipmentsAssets.DateShip as [Date Shipped], FROM tblShipments INNER JOIN(tblAssets INNER JOIN tblShipmentsAssets ON tblAssets.AssetID = tblShipmentsAssets.AssetIDFK) ON tblShipments.ShipmentID = tblShipmentsAssets.ShipmentIDFK where(AssetIDFK like '%' + #assetidfk + '%' )";
OleDbCommand comm = new OleDbCommand(find, con);
comm.Parameters.Add("#assetidfk", OleDbType.Char).Value = TxtSearch.Text;
con.Open();
comm.ExecuteNonQuery();
OleDbDataAdapter da = new OleDbDataAdapter();
da.SelectCommand = comm;
DataSet ds = new DataSet();
da.Fill(ds, "AssetIDFK");
SearchGridView.DataSource = ds;
SearchGridView.DataBind();
In the past, I've simply populated a gridview solely with aspx code using <asp:SqlDataSource> and there I was able to get at the columns that held dates withing the <columns> tag and add in a DataFormatString="{0:d}" to get the format I wanted:
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" HorizontalAlign="Center">
<Columns>
<asp:BoundField DataField="NextCal" HeaderText="Calibration Due" DataFormatString="{0:d}" >
</asp:BoundField>
</Columns>
</asp:GridView>
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:Database %>" ProviderName="<%$ ConnectionStrings:Database.ProviderName %>" SelectCommand="SELECT , [NextCal] FROM [tblAssets]"></asp:SqlDataSource>
With my C# method of populating the gridview, I have no <Columns> section to change the data format. I would then assume I need to do this in the code behind, however I'm not sure how to do that.
Figured out what I was doing wrong:
By having <AutoGenerateColumns ="true">, my columns were (big surprise) being automatically generated and then me adding in columns under the tag in .aspx was generating duplicate columns.
I changed it to <AutoGenerateColumns ="false">, manually entered my columns in under the tag and used the DataFormatString="{0:MM/dd/yyyy hh:mm tt}"to change the date format.
Sorry for the dumb question. Hopefully this will help someone avoid the same mistake..