I have a GridView which gets the data from a SQL Server database.
The GridView binds when the user select the date from an CalendarExtender, because the data is different one day to another, also the rows quantity.
E. G., in Saturdays the GridView is filled with 18 rows. In Tuesdays, with 58.
Concern:
What I need to do is to split the GridView in 2 parts (2 GridViews). E. G., In tuesdays, 29 rows each GridView, in saturdays, 9 rows each.
I have tried:
To bring the daily data into a GridView, called "GVTotal":
if (Weekday.Value == "Saturday")
{
GVTotal.DataSourceID = SaturdayData.ID;
GVTotal.DataBind();
}
To count the rows from GVTotal, and divide by 2.
int everything = GVTotal.Rows.Count;
int half = everything / 2;
What I want to do now, is to "Copy" the rows from 0 to half to GVPart1, and from half to everything to GVPart2, in the exactly same order than in GVTotal.
I have read that maybe using a DataTable will made this possible.
I am not pretty sure how to do that. Could someone help me please?
You could have a Repeater with two items, one for each GridView. In the example below, the Repeater is rendered as a table with a single row. Each GridView is in a cell of that row, with an empty cell between them.
<asp:Repeater ID="repeater1" runat="server" OnItemDataBound="repeater1_ItemDataBound">
<HeaderTemplate>
<table cellspacing="0" cellpadding="0">
<tr>
</HeaderTemplate>
<ItemTemplate>
<td>
<asp:GridView ID="gvHalf" runat="server" >
...
</asp:GridView>
</td>
</ItemTemplate>
<SeparatorTemplate>
<td style="width: 32px;" />
</SeparatorTemplate>
<FooterTemplate>
</tr>
</table>
</FooterTemplate>
</asp:Repeater>
In order to get the two data sources, you declare two DataTables in your class:
private DataTable dtTopHalf;
private DataTable dtBottomHalf;
In Page_Load, the two DataTables are populated by splitting the full DataTable in two parts, and the data source of the Repeater is set to two boolean values:
void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
using (SqlConnection conn = new SqlConnection("Data Source=(local); Integrated Security = True; Initial Catalog=TestMP"))
using (SqlCommand cmd = new SqlCommand("SELECT * FROM Clients ORDER BY ClientID ASC", conn))
{
cmd.CommandType = CommandType.Text;
SqlDataAdapter dataAdapter = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
dataAdapter.Fill(dt);
int halfCount = dt.Rows.Count / 2;
dtTopHalf = dt.AsEnumerable().Select(x => x).Take(halfCount).CopyToDataTable();
dtBottomHalf = dt.AsEnumerable().Select(x => x).Skip(halfCount).CopyToDataTable();
}
// Each value in the Repeater indicates if it is the top half or not
repeater1.DataSource = new List<bool>() { true, false };
repeater1.DataBind();
}
}
The specific data source can then be set for each GridView in the ItemDataBound event handler of the Repeater:
protected void repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
bool isTopHalf = (bool)e.Item.DataItem;
GridView gvHalf = e.Item.FindControl("gvHalf") as GridView;
gvHalf.DataSource = isTopHalf ? dtTopHalf : dtBottomHalf;
gvHalf.DataBind();
}
}
Note 1: The Repeater allows to share the same markup for both GridViews. If you prefer, you can declare two separate GridViews in the markup and apply the specific data source to each one in Page_Load.
Note 2: You need a reference to System.Data.DataSetExtensions in your project to use some of the LINQ methods mentioned above.
Thanks to ConnorsFan for the assistance. His answer is the right way to do what I wanted.
As I need to select the date before the data comes in, I wrote Connor's code into my date Textbox OnTextChanged event, into the if (Weekday.Value == "<day of the week>") statement.
This is the solution:
ASPX:
Updated: Only with one GridView, as reccomended:
<asp:Repeater ID="repeater1" runat="server" OnItemDataBound="repeater1_ItemDataBound">
<HeaderTemplate>
<table cellspacing="200px" cellpadding="0">
<tr style="vertical-align: top;">
</HeaderTemplate>
<ItemTemplate>
<td>
<asp:GridView ID="gvHalf" runat="server" BackColor="White" AutoGenerateColumns="False" HeaderStyle-CssClass="tituloshandoff" RowStyle-CssClass="contenidohandoffbatch">
<Columns>
<asp:BoundField HeaderText="IDBATCH" DataField="IDBatch" SortExpression="IDBatch" HeaderStyle-CssClass="TituloInvisible" ItemStyle-CssClass="TituloInvisible" />
<asp:BoundField HeaderText="BATCH" DataField="Nombre" SortExpression="Nombre" />
<asp:BoundField HeaderText="DEALER" DataField="DealerCodigo" SortExpression="DealerCodigo" />
<asp:BoundField HeaderText="CT TIME" DataField="CTStart" SortExpression="CTStart" />
<asp:BoundField HeaderText="STATUS" DataField="Estado" SortExpression="Estado" />
<asp:TemplateField HeaderText="CONTROL">
<ItemTemplate>
<asp:Button ID="Button1" CssClass="botongrid" runat="server" Text="Select" Width="100px" /></ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</td>
</ItemTemplate>
<SeparatorTemplate>
<td style="width: 100px;" />
</SeparatorTemplate>
<FooterTemplate>
</tr>
</table>
</FooterTemplate>
</asp:Repeater>
C#:
I will show only one day in example:
string LaConexion = #"<My Connection String>";
private DataTable dtTopHalf;
private DataTable dtBottomHalf;
protected void TextDate_TextChanged(object sender, EventArgs e)
{
if (diasemana.Value == "Monday")
{
using (SqlConnection conexion = new SqlConnection(LaConexion))
using (SqlCommand comando = new SqlCommand("SELECT [1Monday].IDBatch, Batch.Nombre, Dealer.DealerCodigo, Batch.CTStart, BatchDatos.Estado FROM [1Monday] INNER JOIN Batch ON [1Monday].IDBatch = Batch.IDBatch INNER JOIN Dealer ON Batch.IDDealer = Dealer.IDDealer LEFT OUTER JOIN BatchDatos ON [1Monday].ID = BatchDatos.ID ORDER BY Batch.CTStart", conexion))
{
comando.CommandType = CommandType.Text;
SqlDataAdapter dataAdapter = new SqlDataAdapter(comando);
DataTable dt = new DataTable();
dataAdapter.Fill(dt);
int halfCount = dt.Rows.Count / 2;
dtTopHalf = dt.AsEnumerable().Select(x => x).Take(halfCount).CopyToDataTable();
dtBottomHalf = dt.AsEnumerable().Select(x => x).Skip(halfCount).CopyToDataTable();
}
// Each value in the Repeater indicates if it is the top half or not
repeater1.DataSource = new List<bool>() { true, false };
repeater1.DataBind();
}
}
A Picture:
Related
I have 3 different textboxes for serials. And when I click the button I want to save them for each row in database table.
Textbox1.Text="HP" ==> STOCKID
Textbox2.Text="İ5" ==> MODEL
Textbox3.Text="3" ==> QUANTITY
Textbox4.Text="11231231"; ==> SERIAL-1
Textbox5.Text="11231231"; ==> SERIAL-2
Textbox6.Text="11231231"; ==> SERIAL-2
Then Button click event.
Result should be as below.
FIRST GRIDVIEW
STOKID
MODEL
QUANTİTY
HP
I5
3
SECOND GRIDVIEW
STOKID
MODEL
SERIALS
DELETEBUTTON
HP
I5
32165161
BUTTON
HP
I5
12313223
BUTTON
HP
I5
16516516
BUTTON
When I delete from serials one bye one, the first Gridview QTY should decrease one for each serials. Is it possible?
I use store procedure during insert to data for first gridview. But for second one I don't know how to use a loop for textboxes and add to database different serials(textboxes values).
Ok, so say this markup:
We have Stokkid, model, and then have 3 optional serial num boxes
(if they are left blank - we don't add).
So, this markup:
The boxes, and add button:
<div style="float: left">
<h4>Stokid</h4>
<asp:TextBox ID="txtStokID" runat="server"></asp:TextBox>
</div>
<div style="float: left;margin-left:20px">
<h4>Model</h4>
<asp:TextBox ID="txtModel" runat="server"></asp:TextBox>
</div>
<div style="float: left;margin-left:20px">
<h4>Serial 1</h4>
<asp:TextBox ID="txtS1" runat="server"></asp:TextBox>
</div>
<div style="float: left;margin-left:20px">
<h4>Serial 2</h4>
<asp:TextBox ID="txtS2" runat="server"></asp:TextBox>
</div>
<div style="float: left;margin-left:20px">
<h4>Serial 3</h4>
<asp:TextBox ID="txtS3" runat="server"></asp:TextBox>
</div>
<div style="float: left;margin-left:20px;margin-top:20px">
<asp:Button ID="cmdSave" runat="server" Text="Save/Add" CssClass="btn"
OnClick="cmdSave_Click"
/>
</div>
<div style="clear:both" ></div>
And right below that, 2 grids, the first totals gv, and then the data we have gv.
So, this:
<h3>Counts</h3>
<asp:GridView ID="GVCounts" runat="server" CssClass="table"
width="40%" ShowHeaderWhenEmpty="True" >
</asp:GridView>
<h3>Items</h3>
<asp:GridView ID="GridView1" runat="server" CssClass="table"
width="40%" ShowHeaderWhenEmpty="True" >
</asp:GridView>
And now our code to load up the above:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadData();
}
void LoadData()
{
// load up "counts" grid
string strSQL =
#"SELECT Stokid, Model, count(*) as QTY FROM tblSerials
GROUP BY Stokid, Model";
GVCounts.DataSource = MyRst(strSQL);
GVCounts.DataBind();
// Load up existing data grid
strSQL =
#"SELECT Stokid, Model, Serials FROM tblSerials ORDER BY ID";
GridView1.DataSource = MyRst(strSQL);
GridView1.DataBind();
}
So, only part left is the "add new data button"
That code is this:
protected void cmdSave_Click(object sender, EventArgs e)
{
// save (add) one row for each serial number,
// if no serial - then don't add
DataTable dt = MyRst("SELECT * FROM tblSerials WHERE ID = 0");
DataRow MyAddRow = dt.NewRow();
MyAddRow["Stokid"] = txtStokID.Text;
MyAddRow["Model"] = txtModel.Text;
MyAddRow["Serials"] = txtS1.Text;
dt.Rows.Add(MyAddRow);
if (txtS2.Text != "")
{
MyAddRow = dt.NewRow();
MyAddRow["Stokid"] = txtStokID.Text;
MyAddRow["Model"] = txtModel.Text;
MyAddRow["Serials"] = txtS2.Text;
dt.Rows.Add(MyAddRow);
}
if (txtS3.Text != "")
{
MyAddRow = dt.NewRow();
MyAddRow["Stokid"] = txtStokID.Text;
MyAddRow["Model"] = txtModel.Text;
MyAddRow["Serials"] = txtS3.Text;
dt.Rows.Add(MyAddRow);
}
// send/save all rows to database
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST5))
{
using (SqlCommand cmdSQL = new SqlCommand("SELECT * FROM tblSerials", conn))
{
conn.Open();
SqlDataAdapter da = new SqlDataAdapter(cmdSQL);
SqlCommandBuilder daU = new SqlCommandBuilder(da);
da.Update(dt);
}
}
// Update counts and display
LoadData();
}
so, the result is now this:
Now, we do probably want to setup a delete button.
so, that means our "cheat" life and easy GV of data?
Well, we have to give it a bit more effort, love and care.
We need the database PK id, and we want that delete button.
And since the delete button can be a danger, then it should confirm.
So, we have to update our GV a bit, bite the bullet and use some templating. but, it is not much.
And lets use a boot-strap icon - they should be part of any most recent project by defualt. But BE warned, after boottrap 4, the later versions (due to some copyright/lawsuit, bootstrap does not include the glyph-icons).
Anyway, I COULD use a plain jane asp.net button in the gv for delete, but lets use standard html button - I ONLY use the html one, since I wanted the cute icon. Probably better for you to use a image button.
(code is much the same either way).
So, our 2nd gv now looks like this:
<h3>Items</h3>
<asp:GridView ID="GridView1" runat="server" CssClass="table table-hover"
width="40%" ShowHeaderWhenEmpty="True" DataKeyNames="ID"
AutoGenerateColumns="False" >
<Columns>
<asp:BoundField DataField="STOKID" HeaderText="STOKID" />
<asp:BoundField DataField="MODEL" HeaderText="MODEL" />
<asp:BoundField DataField="serials" HeaderText="serials" />
<asp:TemplateField HeaderText="Delete" ItemStyle-Width="80px" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<button id="cmdDelete" runat="server" class="btn"
onclick="if (!confirm('Delete')) return false;"
onserverclick="cmdDel_ServerClick" >
<span aria-hidden="true" class="glyphicon glyphicon-trash"></span>
</button>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
And the code to load up this gv is now:
void LoadData()
{
// load up "counts" grid
string strSQL =
#"SELECT Stokid, Model, count(*) as QTY FROM tblSerials
GROUP BY Stokid, Model";
GVCounts.DataSource = MyRst(strSQL);
GVCounts.DataBind();
// Load up existing data grid
strSQL = #"SELECT * FROM tblSerials ORDER BY ID";
GridView1.DataSource = MyRst(strSQL);
GridView1.DataBind();
}
And our delete code is this:
protected void cmdDel_ServerClick(object sender, EventArgs e)
{
HtmlButton btn = (HtmlButton)sender;
GridViewRow gRow = (GridViewRow)btn.NamingContainer;
int PKID = (int)GridView1.DataKeys[gRow.RowIndex]["ID"];
string strSQL = "DELETE FROM tblSerial WHERE ID = {PKID}";
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST5))
{
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
conn.Open();
cmdSQL.ExecuteNonQuery();
}
}
// update count and items gv
LoadData();
}
So, now we see, get this for a delete:
Edit2: The Myrst routine
In a few places in above, I also used this helper routine, since I get VERY tired VERY fast having to write code each and every time I want some data, so, I wrote and use this routine:
DataTable MyRst(string strSQL)
{
DataTable rstData = new DataTable();
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST5))
{
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
conn.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
}
return rstData;
}
It handy for filling out a gridview, dropdown list etc.
I have a datalist which is populated with values from a database. When the user clicks a checkbox, I want to loop through all of the datalist items and hide all of the items that have the property isActive = false (which is displayed as "Disabled" in a text label). The item template consists of a table that contains multiple other items, buttons etc, which have not been included below. I therefore want to hide all elements that are found under the itemtemplate of a specific item.
My idea was to just hide the entire table by accessing its id, and then setting the table's visible property to false. This is currently not doing anything when I run the code. Any help will
appreciated!
<asp:CheckBox runat="server" Text="Filter by active postings" OnCheckedChanged="filterByActive"/>
<asp:DataList ID="postingsDataList" runat="server" OnItemCommand="itemCommand" >
<ItemTemplate>
<div class="postingRow">
<table class="postingTable" id="posting">
<tr>
<td>
<asp:Label ID="lblActive" runat="server" Text=<%#Eval("isActive").ToString().Equals("True") ? "Active ✅" : "Disabled ❌"%>/>
</td>
</tr>
</table>
</div>
</ItemTemplate>
</asp:DataList>
Code Behind:
protected void filterByActive (object sender, EventArgs e)
{
int count = postingsDataList.Items.Count;
CheckBox check = (CheckBox)sender;
if (check.Checked == true)
{
for (int i = 0; i < count; i++)
{
Label lblActive = postingsDataList.Items[i].FindControl("lblActive") as Label;
string isActive = lblActive.Text.ToString();
if (isActive.Equals("Disabled ❌"))
{
Table tbl = postingsDataList.Items[i].FindControl("posting") as Table;
tbl.Visible = false;
}
}
}
else
{
for (int i = 0; i < count; i++)
{
Label lblActive = postingsDataList.Items[i].FindControl("lblActive") as Label;
string isActive = lblActive.Text.ToString();
if (isActive.Equals("Active ✅"))
{
Table tbl = postingsDataList.Items[i].FindControl("posting") as Table;
tbl.Visible = true;
}
}
}
}
}
Ok, I suggest you do this:
Persist your table - thus you don't have to hit sql server again. (but, you could - not the end of the world).
I don't have your data, but I have a simple list of hotels - and there is a Active column for the Hotel. So, lets filter the data based on checking the check box, not have to hit the database server again. And EVEN BETTER is if we un-check the box, we can display all records we had.
We assume of course this is not a lot of data.
Ok, so we have this for our markup:
(really does not matter a whole lot)
<asp:DataList ID="DataList1" runat="server" DataKeyField="ID" RepeatColumns="4" RepeatDirection="Horizontal" >
<ItemTemplate>
<div style="border-style:solid;color:black;width:300px;">
<div style="padding:5px;text-align:right">
<p>Hotel Name: <asp:TextBox ID="HotelName" runat="server" Text ='<%# Eval("HotelName") %>' /></p>
<p>First Name: <asp:TextBox ID="FirstName" runat="server" Text ='<%# Eval("FirstName") %>' /></p>
<p>Last Name: <asp:TextBox ID="LastName" runat="server" Text ='<%# Eval("LastName") %>' /></p>
<p>City: <asp:TextBox ID="City" runat="server" Text ='<%# Eval("City") %>' /></p>
<p>Province: <asp:TextBox ID="Province" runat="server" Text ='<%# Eval("Province") %>' /></p>
Active: <asp:CheckBox ID="Active" runat="server" Checked = '<%# Eval("Active") %>'/>
</div>
</div>
</ItemTemplate>
</asp:DataList>
Now, our code to load could be this:
DataTable rstData = new DataTable();
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
LoadData();
ViewState["MyData"] = rstData;
}
else
rstData = (DataTable)ViewState["MyData"];
}
void LoadData()
{
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
string strSQL = "SELECT top 10 * from tblHotels ORDER BY HotelName";
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
conn.Open();
rstData.Load(cmdSQL.ExecuteReader());
DataList1.DataSource = rstData;
DataList1.DataBind();
}
}
}
And now we have this:
Now, our filter show/hide becomes JUST this:
protected void ckFilter_CheckedChanged(object sender, EventArgs e)
{
// filter based on check box
if (ckFilter.Checked)
rstData.DefaultView.RowFilter = "Active = 1";
else
rstData.DefaultView.RowFilter = "";
DataList1.DataSource = rstData;
DataList1.DataBind();
}
And BETTER is if I un-check the check box, all the data is re-displayed. And we don't have to re-hit the database to do this!!!
Now, we COULD also just hide/show each row, and then un-hide if for some reason you don't want to persist the data table as I did per above.
Checking on the box filters to this:
As noted, if I un-check the box, then I get all the data back - all without hitting the database again.
Edit:=========================================
Note that if you NOT using the above horizontal across, but just rows down?
Then you can just as well format to hide/show the rows, and you do NOT have to persist the data table.
You can apply formatting say by doing this:
protected void ckFilter_CheckedChanged(object sender, EventArgs e)
{
foreach (DataListItem gRow in DataList1.Items)
{
// get checkbox
CheckBox ckActive = (CheckBox)gRow.FindControl("Active");
// get div
HtmlGenericControl Myiv = (HtmlGenericControl)gRow.FindControl("myrow");
string MyFormat = "normal";
if (ckFilter2.Checked)
{
// only show active ones
if (ckActive.Checked)
MyFormat = "normal";
else
MyFormat = "none";
}
Myiv.Style["display"] = MyFormat;
}
}
So the above does work fine. BUT the restriction is that you can't have a set of horizontal across data items like my original screen shot
(it actually still works but in fact hide each panel with a blank space).
So, if your as noted using vertical layout (and it looks to be that you are).
Note for above, I added a simple ID to the "div", and runat server like this:
<ItemTemplate>
<div id="myrow" runat="server"
style="border-style:solid;color:black;width:300px;">
Then skip my first example. You can loop the data list rows, and hide, or un-hide. And even more amazing is that if you un-hide - then the rows all do re-appear and persist correct.
I had pulled a working example - but I was trying to figure out why I had to persist the data - the reason was the "horizontail" across settings.
But, as above shows, no real need to persist the data source table - you can process the data list rows, and hide/show each one conditionals, and you can do this without a data re-bind.
I have a repeater that I populate from a database:
using (SqlConnection conn = new SqlConnection(connString))
{
SqlCommand cmd = new SqlCommand(#"SELECT CommunityName, CID, Budget FROM Donation WHERE Year = year(getdate()) ORDER BY CommunityName", conn);
conn.Open();
SqlDataAdapter adp = new SqlDataAdapter(cmd);
DataSet myDataSet = new DataSet();
adp.Fill(myDataSet);
myRep.ItemDataBound += new RepeaterItemEventHandler(myRep_ItemDataBound);
myRep.DataSource = myDataSet;
myRep.DataBind();
}
void myRep_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
var textbox = e.Item.FindControl("community");
textbox.ClientIDMode = ClientIDMode.Static;
textbox.ID = "community" + (e.Item.ItemIndex + 1);
}
Repeater:
<asp:UpdatePanel ID="UpdatePanel" runat="server" UpdateMode="Always">
<ContentTemplate>
<asp:Repeater ID="myRep" runat="server">
<ItemTemplate>
<div class="form-group">
<asp:Label ID='thisLbl' runat="server" Text='<%# Eval("CommunityName") %>' />
<asp:TextBox runat="server" ID="community" Text='<%# Eval("Budget") %>' CssClass="form-control" />
</div>
</ItemTemplate>
</asp:Repeater>
</ContentTemplate>
</asp:UpdatePanel>
This creates 6 textboxes with labels and values, now my question is how do I detect which of these boxes belongs to the record it was initially pulled from in the database? I want to be able to modify the value in these boxes and hit a button to save them back to the database but I can't seem to wrap my head around getting them to the proper records.
Should I set the ID of the textbox to something I can parse through and match with the proper record? In the ItemDataBound?
You have to put a hidden field inside the repeater item template that takes the value from budget, and another hidden field to keep the CID value that has to be read in the post back request. Of course you need also a button and its click event handler.
<asp:Repeater ID="myRep" runat="server">
<ItemTemplate>
<div class="form-group">
<asp:Label ID='thisLbl' runat="server" Text='<%# Eval("CommunityName") %>' />
<asp:TextBox runat="server" ID="txtBudget" Text='<%# Eval("Budget") %>' CssClass="form-control" />
<asp:HiddenField runat="server" ID="hdOriginalBudget" Value='<%# Eval("Budget") %>' />
<asp:HiddenField runat="server" ID="hdCID" Value='<%# Eval("CID") %>' />
</div>
</ItemTemplate>
</asp:Repeater>
<br />
<asp:Button ID="btn" runat="server" OnClick="btn_Click" />
In your code behind you need to loop inside the repeater to check whether the text box has been changed by comparing its value to the hidden field. After you save the budget value in the database you need to realign the hidden field value to the the new value entered by the user, otherwise you will always save that value after each post back:
protected void btn_Click(object sender, EventArgs e)
{
foreach (RepeaterItem item in myRep.Items)
{
var txtBudget = item.FindControl("txtBudget") as TextBox;
var hdOriginalBudget = item.FindControl("hdOriginalBudget") as HiddenField;
var hdCID = item.FindControl("hdCID") as HiddenField;
if (txtBudget.Text != hdOriginalBudget.Value)
{
//If you enter here means the user changed the value of the text box
using (SqlConnection conn = new SqlConnection(connString))
{
SqlCommand cmd = new SqlCommand(#"UPDATE Donation SET Budget = #Budget WHERE CID = #CID", conn);
cmd.Parameters.Add(new SqlParameter("#Budget", int.Parse(txtBudget.Text)));
cmd.Parameters.Add(new SqlParameter("#CID", int.Parse(hdCID.Value)));
conn.Open();
cmd.ExecuteNonQuery();
}
//After you write in the database realign the values
hdOriginalBudget.Value = txtBudget.Text;
}
}
}
Take care that my code is missing the most basic validation, so if the user writes an invalid value in the textbox (for example "yyy") it breaks. So please don't put it in production as it is!
I a newbie to asp.net. I have created a DB table in sqlserver with 3 columns; ImageID, Filename & Score.
FileName is C:\pics\beads.png or C:\pics\moreimages\scenary.jpeg.
Using C# asp.net, I have created a GridView. This Gridview should populate
(column 1)ImageID and get the image from the Filename (column2) and I have 2 Checkboxlist created as shown below which should accept score for the images from user and save it into the DB table.
Question 1 .
I am able to populate Image ID, but Images are not getting populated.
Question 2.
I do not know how to access the checkboxlist since it is in template. The checked is in the default select and doesn't change even if I click on it. Which method/property should be used to do save the selection to the database. Here's my code
<form id="form1" runat="server">
<p style="height: 391px">
<asp:GridView ID="GridView1" runat="server" Caption="Logos" Height="299px" Width="577px" AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="ImageID" HeaderText="ImageID" />
<asp:ImageField DataImageUrlField="FileName" ControlStyle-Width="100"
ControlStyle-Height="100" HeaderText="Image" AlternateText="No image">
<ControlStyle Height="100px" Width="100px"></ControlStyle>
</asp:ImageField>
<asp:TemplateField HeaderText="Score" AccessibleHeaderText="CheckBoxList" ValidateRequestMode="Enabled">
<ItemTemplate>
<asp:CheckBoxList runat="server" AutoPostBack="true" RepeatColumns="3" ID="test">
<asp:ListItem Selected="True" Value="5">Good</asp:ListItem>
<asp:ListItem Value="0">Not Good </asp:ListItem>
<asp:ListItem Value="3">OK</asp:ListItem>
</asp:CheckBoxList>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Save" Width="130px" />
<asp:Button ID="Button2" runat="server" OnClientClick="javaScript:window.close(); return false;" Text="Exit" Width="102px" />
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
</p>
</form>
public string sqlSel = #"SELECT TOP 3 [ImageID],FileName FROM [db1].[ImagesTest] where [Score] is null";
protected void Page_Load(object sender, EventArgs e)
{
using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["SQLConnectionString"].ConnectionString))
{
connection.Open();
SqlCommand cmdSel = new SqlCommand(sqlSel, connection);
SqlDataReader reader1 = cmdSel.ExecuteReader();
while (reader1.Read())
{
DataSet ds = GetData(sqlSel);
if (ds.Tables.Count > 0)
{
GridView1.DataSource = ds;
GridView1.DataBind();
}
else
{
Response.Write("Unable to connect to the database.");
}
}
}
}
private DataSet GetData(string cmdSel)
{
String strConnString = System.Configuration.ConfigurationManager.ConnectionStrings["SQLConnectionString"].ConnectionString;
DataSet ds = new DataSet();
try
{
SqlConnection con = new SqlConnection(strConnString);
SqlDataAdapter sda = new SqlDataAdapter(cmdSel,con);
sda.Fill(ds);
}
catch (Exception ex)
{
Response.Write("IN EXCEPTION "+ex.Message);
return null;
}
return ds;
}
Thanks for ur time
Rashmi
There are several issues with your code, here is what it should be fixed:
How to fix the images (Question 1)
As #Guilherme said in the comments, for images use URLs instead of disk paths.
Replace the images disk paths C:\pics\beads.png or C:\pics\moreimages\scenary.jpeg to something like Images/beads.png and Images/scenary.jpeg.
For this to work, you will need to have a folder named Images that contains these two files on the same directory level as your .aspx file.
Adjust your GridView1 declaration in the ASPX file
On your GridView1 declaration you should:
attach a handler to the OnRowDataBound event. This will allow you to properly bind the Score dataset column to the Score gridview column
set the DataKeyNames property on the grid to what should be the primary key for your images table (in this case DataKeyNames="ImageID")
use a RadioButtonList instead of a CheckboxList, because according to your dataset you can only set one value, which is what the RadioButtonList does. The CheckboxList is normally used when multiple selection is needed.
attach a handler to the SelectedIndexChanged event on the RadioButtonList. This will allow you to save the new values to the database (Question 2)
Here's how your GridView declaration should look like:
<asp:GridView ID="GridView1" runat="server" Caption="Logos" Height="299px" Width="577px" AutoGenerateColumns="False" OnRowDataBound="GridView1_RowDataBound" DataKeyNames="ImageID">
<Columns>
<asp:BoundField DataField="ImageID" HeaderText="ImageID" />
<asp:ImageField DataImageUrlField="FileName" ControlStyle-Width="100"
ControlStyle-Height="100" HeaderText="Image" AlternateText="No image">
<ControlStyle Height="100px" Width="100px"></ControlStyle>
</asp:ImageField>
<asp:TemplateField HeaderText="Score" AccessibleHeaderText="RadioButtonList" ValidateRequestMode="Enabled">
<ItemTemplate>
<asp:RadioButtonList runat="server" AutoPostBack="true" RepeatColumns="3" ID="test" OnSelectedIndexChanged="test_SelectedIndexChanged">
<asp:ListItem Value="5">Good</asp:ListItem>
<asp:ListItem Value="0">Not Good </asp:ListItem>
<asp:ListItem Value="3">OK</asp:ListItem>
</asp:RadioButtonList>
<!-- UPDATED! - keep the old values in a hidden field -->
<asp:HiddenField runat="server" ID="hfOldScore" Value='<%# Eval("Score") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Adjust your code behind value in your .ASPX.CS file (Question 2)
On the code behind, you will also need to:
Make sure you are binding to the GridView only once in the Page_Load event, by checking the IsPostBack property
(Optional, but recommended) Move the logic of getting data (with the SQLConnection code) in a separate method that will handle only data retrieval
Implement the handler for the OnRowDataBound event. This will bind any values from the Score column to the RadioButtonList control
Implement the handler for the SelectedIndexChecked event of the RadioButtonList control. This will allow you to save to the database the new values
Finally, here's the entire code-behind code:
protected void Page_Load(object sender, EventArgs e)
{
//bind only the first time the page loads
if (!IsPostBack)
{
DataSet ds = GetData(sqlSel);
if (ds.Tables.Count > 0)
{
GridView1.DataSource = ds;
GridView1.DataBind();
}
else
{
Response.Write("Unable to connect to the database.");
}
}
}
private DataSet GetData(string cmdSel)
{
//normally you should query the data from the DB
//I've manually constructed a DataSet for simplification purposes
DataSet ds = new DataSet();
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("ImageID", typeof(int)));
dt.Columns.Add(new DataColumn("FileName", typeof(string)));
dt.Columns.Add(new DataColumn("Score", typeof(int)));
dt.Rows.Add(100, #"Images/beads.png", 0);
dt.Rows.Add(200, #"Images/moreimages/scenary.jpeg", 3);
dt.Rows.Add(300, #"Images/moreimages/scenary.jpeg", 5);
ds.Tables.Add(dt);
return ds;
}
protected void Button1_Click(object sender, EventArgs e)
{
//UPDATED - iterate through all the data rows and build a dictionary of items
//to be saved
Dictionary<int, int> dataToUpdate = new Dictionary<int, int>();
foreach (GridViewRow row in GridView1.Rows)
{
if (row.RowType == DataControlRowType.DataRow)
{
int imageID = (int)GridView1.DataKeys[row.RowIndex].Value;
int oldScore;
int newScore;
int.TryParse((row.FindControl("hfOldScore") as HiddenField).Value, out oldScore);
int.TryParse((row.FindControl("test") as RadioButtonList).SelectedValue, out newScore);
if (oldScore != newScore)
{
dataToUpdate.Add(imageID, newScore);
}
}
}
//update only the images that were changed
foreach (var keyValuePair in dataToUpdate)
{
SaveToDB(keyValuePair.Key, keyValuePair.Value);
}
}
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
//we are only interested in the data Rows
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataRow dataRow = (e.Row.DataItem as DataRowView).Row;
//manually bind the Score column to the RadioButtonlist
int? scoreId = dataRow["Score"] == DBNull.Value ? (int?)null : (int)dataRow["Score"];
if (scoreId.HasValue)
{
RadioButtonList test = e.Row.FindControl("test") as RadioButtonList;
test.ClearSelection();
test.SelectedValue = scoreId.Value.ToString();
}
}
}
protected void test_SelectedIndexChanged(object sender, EventArgs e)
{
RadioButtonList test = sender as RadioButtonList;
GridViewRow gridRow = test.NamingContainer as GridViewRow;
//obtain the current image Id
int imageId = (int)GridView1.DataKeys[gridRow.RowIndex].Value;
//obtain the current selection (we will take the first selected checkbox
int selectedValue = int.Parse(test.SelectedValue);
//UPDATED! - saves are now handled on the Save button click
//SaveToDB(imageId, selectedValue);
}
private void SaveToDB(int imageId, int score)
{
//UPDATED!
using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["SQLConnectionString"].ConnectionString))
{
connection.Open();
using (SqlCommand command = connection.CreateCommand())
{
command.Parameters.Add("#ImageID", imageId);
command.Parameters.Add("#Score", score);
command.CommandText = #"update [db1].[ImagesTest] set Score = #Score where [ImageID] = #ImageID";
command.ExecuteNonQuery();
}
}
}
UPDATED!
The declaration of the GridView1 now contains a hidden field containing the Score value. You can use this hidden field to determine which row has changed, so you can trigger a save only on the changed ones
Save is now done on the click of the Save button, by iterating through the rows of the grid an building a Dictionary<int,string> to keep only the changes.
SaveToDB method should work, but I cannot test this myself.
Question 2:
To access your checkbox you can use.
GridViewRow row = GridView1.Rows[i];
CheckBox Ckbox = (CheckBox)row.FindControl("test");
For Question 1
Instead of ImageField you can use use ASP:Image in Templatefield like this
<asp:TemplateField HeaderText="Score" AccessibleHeaderText="CheckBoxList" ValidateRequestMode="Enabled">
<ItemTemplate>
<asp:Image ID="imageControl" runat="server" ImageUrl='<%# Eval("Filename") %>'></asp:Image>
</ItemTemplate>
</asp:TemplateField>
For Question 2
This is one of the example for how to access each Checkbox list inside gridview
For Each gvr As GridViewRow In Gridview1.Rows
If (CType(gvr.FindControl("CheckBox1"), CheckBox)).Checked = True Then
ReDim uPrimaryid(iCount)
uPrimaryid(iCount) = New Integer
uPrimaryid(iCount) = gvr.Cells("uPrimaryID").Text
iCount += 1
End If
Next
This question to related to a similar Data list question I posted some days ago. I have a datalist which displays Categories, and then documents within categories. What needs to happen is that documents under each category gets displayed in the order based on some numbers in file names. Documents are in format like '001-filename.pdf', '002-filename.pdf' ... '00x-filename.pdf'. I can use the first dash as some kind of 'split' function then grab the numbers like '001' etc to make the sorting to work. I think this could be done on either itemdatabound or in the sql syntax. I am posting the relevant code here. Any idea as to how I can make this to work? It is possible that there could be more than one document with shared number prefix: '001-filename.pdf', '001-filenameversion2.pdf' etc. Thanks!
ASPX:
<asp:DataList ID="DataList1" runat="server" RepeatDirection="Vertical" DataKeyField="docid"
EnableViewState="True" OnItemDataBound="DataList1_ItemDataBound">
<ItemTemplate>
<table cellpadding="0" cellspacing="0" id="tbl_data">
<tr runat="server" id="tr_category">
<td>
<asp:Label ID="lblHeader" runat="server" Font-Bold="True" Text='<%# Eval("categoryname") %>'
Font-Underline="True"></asp:Label>
<asp:Label runat="server" ID="lbl_cb_all">Select All
<asp:CheckBox runat="server" OnCheckedChanged="CheckAllChanged" AutoPostBack="true"
ID="cb_selectall" />
</asp:Label>
<asp:HiddenField ID="HiddenCatID" runat="server" Value='<%# Eval("CatID") %>' />
<asp:HiddenField ID="HiddenDocID" runat="server" Value='<%# Eval("docid") %>' />
</td>
</tr>
<tr runat="server" id="tr_data">
<td>
<asp:CheckBox runat="server" ID="cb_docid" Value='<%# Eval("docid") %>' OnCheckedChanged="displayselectedinit"
AutoPostBack="true" />
<asp:HyperLink ID="hpl_docfileencr" Text='<%# Eval("docfileencr") %>' NavigateUrl='<%# "~/PDFEncr/" + DataBinder.Eval(Container.DataItem, "docfileencr") %>'
Target="_blank" runat="server" />
<br />
</td>
</tr>
</table>
</ItemTemplate>
</asp:DataList>
C# CodeBehind:
sqlsyntax = #"SELECT dbo.projectsdocuments.docfileencr,dbo.categories.catid, dbo.categories.categoryname, dbo.projectsdocuments.docid
FROM dbo.Projects INNER JOIN dbo.projectsdocuments ON (dbo.Projects.projectid = dbo.projectsdocuments.projectid)
INNER JOIN dbo.categories ON (dbo.projectsdocuments.categoryid = dbo.categories.catid)
WHERE Projects.projectid = " + projectid + " ORDER BY dbo.categories.sortorder ASC";
protected void DataList1_ItemDataBound(Object sender, DataListItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
var row = (DataRowView)e.Item.DataItem;
var view = row.DataView;
var lastRow = e.Item.ItemIndex == 0 ? null : view[e.Item.ItemIndex - 1];
var tr_category = (System.Web.UI.HtmlControls.HtmlTableRow)e.Item.FindControl("tr_category");
var sameCategory = lastRow != null && (int)row["catid"] == (int)lastRow["catid"];
tr_category.Visible = !sameCategory;
}
}
Change your ORDER BY to include both columns.
SELECT
dbo.projectsdocuments.docfileencr,
dbo.categories.catid,
dbo.categories.categoryname,
dbo.projectsdocuments.docid
FROM dbo.Projects
INNER JOIN dbo.projectsdocuments
ON (dbo.Projects.projectid = dbo.projectsdocuments.projectid)
INNER JOIN dbo.categories
ON (dbo.projectsdocuments.categoryid = dbo.categories.catid)
WHERE Projects.projectid = " + projectid + "
ORDER BY dbo.categories.sortorder, dbo.projectsdocuments.docfileencr
The order by precedence is left to right, the default is "ASCENDING" but you can change a specific column sort by adding "ASC or DESC" directly after it.
** IMPORTANT NOTE **
Your code is susceptible to SQL Injection because your doing string concatenation. If this is a concern to you, change the SQL statement to use a named parameter that you assign using a command parameter (see example below).
Step 1: Change the inline string parameter, to a named parameter "#projectid".
SELECT
dbo.projectsdocuments.docfileencr,
dbo.categories.catid,
dbo.categories.categoryname,
dbo.projectsdocuments.docid
FROM dbo.Projects
INNER JOIN dbo.projectsdocuments
ON (dbo.Projects.projectid = dbo.projectsdocuments.projectid)
INNER JOIN dbo.categories
ON (dbo.projectsdocuments.categoryid = dbo.categories.catid)
WHERE Projects.projectid = #projectid
ORDER BY dbo.categories.sortorder, dbo.projectsdocuments.docfileencr
Step 2: Assign the parameter inline (example code)
using(SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
SqlCommand command = new SqlCommand(sql, conn);
command.CommandType = CommandType.Text;
// Assign the value projectid to the parameter #projectid
command.Parameters.Add(new SqlParameter("#projectid", projectid));
// Execute The Command (fill dataset, create datareader, etc...)
SqlDataReader reader = command.ExecuteReader();
}