I have a DataList in ASP.NET that brings me the products from the "Products" table, so with the "Eval" statement I assign the product ID:
<asp:TextBox ID="idProductoText" runat="server" type="hidden" value='<%# Eval("PRO_ID") %>'></asp:TextBox>
So in my C# code I need to get the value of that TextBox by its ID, for example an idProductText.Text.Trim(); , but for some reason it doesn't work, any solution? I leave the complete DataList below.
Code to fill the DataList:
public void loadStockProducts()
{
OracleConnection connection = new OracleConnection(with);
OracleCommand command = new OracleCommand("SHOW_PRODUCTS_BUY", connection);
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.Add("registers", OracleDbType.RefCursor).Direction = ParameterDirection.Output;
OracleDataAdapter d = new OracleDataAdapter();
d.SelectCommand = command;
DataTable dt = new DataTable();
d.Fill(dt);
DataList1.DataSource = dt;
DataList1.DataBind();
connection.Close();
}
Full DataList in ASP.NET
<asp:DataList ID="DataList1" runat="server">
<ItemTemplate>
<div class="card mb-6" style="max-width: 1400px">
<div class="row g-0">
<div class="col-md-4">
<img
src="../../img/armchair.jpg"
class="img-fluid rounded-start"
alt="product" />
</div>
<div class="col-lg-5 m-4 form-floating">
<div class="card-body">
<!-- THE PRODUCT ID IS HIDDEN, IT WILL ONLY BE USED TO ADD TO CART -->
<asp:TextBox ID="idProductoText" runat="server" type="hidden" value='<%# Eval("PRO_ID") %>'></asp:TextBox>
<asp:Label ID="PRO_NAMELabel" class="card-title" runat="server" Text='<%# Eval("PRO_NAME") %>' Font-Bold="true" Font-Size="Large" Visible="True" />
<br />
<br />
Q<asp:Label ID="PRO_PRICELabel" class="card-text" runat="server" Text='<%# Eval("PRO_PRICE") %>' Font-Size="Large" />
<br />
<br />
<div class="input-group">
<asp:Button ID="moreInformation" runat="server" Text="More Information" class="btn btn-dark m-2" />
<asp:TextBox ID="quantidadBuy" runat="server" type="number" class="form-control m-2" placeholder="Quantity to Buy"></asp:TextBox>
<asp:Button ID="addCart" runat="server" Text="Add to Cart" class="btn btn-success m-2"/ OnClick="addCart_Click"/>
</div>
</div>
</div>
</div>
</div>
<br />
</ItemTemplate>
</asp:DataList>
Ok, while there are some events of the data list, you can just grab and get all the information you need/want from that button click you have.
However, before we write that code, I see you need a database row PK id, so, I suggest you remove this:
<!-- THE PRODUCT ID IS HIDDEN, IT WILL ONLY BE USED TO ADD TO CART -->
<asp:TextBox ID="idProductoText" runat="server"
type="hidden" value='<%# Eval("PRO_ID") %>'></asp:TextBox>
Assuming PRO_ID is the PK database id, then we really don't want to have that information in the markup - (even hidden).
So, use the so called "data keys" feature. You COULD leave the above, but remove it - we really don't need it.
So, in datalist, add this setting:
<asp:DataList ID="DataList1" runat="server" DataKeyField = "PRO_ID" >
Ok, so now the click event for the button.
Say we have this button:
<asp:Button ID="cmdView" runat="server" Text="View" CssClass="btn"
OnClick="cmdView_Click" />
Code behind:
protected void cmdView_Click(object sender, EventArgs e)
{
Button cmdView = sender as Button;
DataListItem gRow = cmdView.NamingContainer as DataListItem;
// get row index
Debug.Print("row click = " + gRow.ItemIndex.ToString());
// get database primary key (data keys)
int? PkID = DataList1.DataKeys[gRow.ItemIndex] as int?;
Debug.Print("Database PK id = " + PkID);
// get value of single control - say hotel label called
Label lHotelName = gRow.FindControl("HotelNameLabel") as Label;
Debug.Print("Hotel name = " + lHotelName.Text);
}
Output:
So note several things:
We grab/get current row - naming container. This works for Gridview, repeater, listview - quite much all of the data bound types of controls.
From that row, then we can get:
Row index -
From Row index, we reference into data keys. Note that datalist ONLY supports ONE data key, so in MOST cases, we have to do this .DataKeys[ some index]["SOME KEY"]
But datalist is a exception - only one key, so .DataKeys[some index] is all you require.
And to pull controls out, use .FindControl as I show above.
FYI: debug.print - this requires using System.Diagnostics;
(and I have my outputs re-directed to Immediate window).
Related
I would like to have the color of the 'CheckIn' button appear as green depending on the value of some other data in my code but I am unable to access that button outside of its onClick method. I should be able to access it via its ID but for some reason am unable to
<asp:ListView
ID="lvInstructors"
runat="server"
itemwDataBound="lvDataBound"
itemCommand="lvCommand"
Visible="true">
<LayoutTemplate>
<div class="container" id="mainContent">
<asp:PlaceHolder ID="itemPlaceHolder" runat="server" />
</div>
</LayoutTemplate>
<ItemTemplate>
<div class="row instructorItem" id="instructorItem">
<asp:HiddenField ID="sessionID" runat="server" Value='<%#Eval("SessionID")%>' />
<asp:HiddenField ID="hasChckedIn" runat="server" Value='<%#Eval("hasCheckedIn")%>' />
<div class="col-2 sessionStartTimeDiv">
<p class="sessionStartTime"><%#Eval("SessionStartTime")%></p>
</div>
<div class="col-2 instructorHeadshotDiv">
<asp:Image class="instructorHeadshot" runat="server" src='<%#Eval("InstructorHeadshot")%>' />
</div>
<div class="col-5 sessionInfoDiv">
<h3 class="instructorName"><%#Eval("InstructorName")%></h3>
<p class="sessionInfo"><%#Eval("SessionInfo")%></p>
</div>
<div class="col-3 checkInBtnDiv">
<asp:Button class="checkInBtn" ID="checkInBtn" runat="server" OnClick="CheckInBtn_Click" Text="Check-In"></asp:Button>
</div>
</div>
<hr />
</ItemTemplate>
<EmptyDataTemplate>
<br />
<br />
No Sessions to Display.
</EmptyDataTemplate>
</asp:ListView>
How I access it in onClick():
protected void CheckInBtn_Click(object sender, EventArgs e)
{
Button checkInBtn = (Button)sender;
checkInBtn.Text = "Check-Out";
checkInBtn.BackColor = Color.Green;
...
}
(Side Question: Why does SelectedIndex return -1 when I click that button ?)
In your itemwDataBound event, look for the value you need then set the button color there. Also you can set the value in the buttons command argument then grab it in the click event.
You need to Access your Button on ItemDataBound event of the ListView to change the color of the Button conditionally.
Here is the link which can help you with this:
datalist itemdatabound event having issues changing item bg color on condition
I am doing a little school project with ASP.NET, and I'm trying to get all products from the Database, show it and have "Add to card" button for every one of them. I managed to do all this, except adding the button. I have a function that gets all the products and I'm binding it with ASP Repeater. The problem is, when I try to add a button, it says that I need to use the form tag first, when I add the form tag it says it needs to have runat="server", but when I add it, it says I can't have more than one runat="server". The Repeater tag is using it also, and I'm stuck. Can anyone suggest something? Thank you!
private void bindPopularProducts()
{
String CS = ConfigurationManager.ConnectionStrings["DatabaseConnectionString1"].ConnectionString;
using (SqlConnection con = new SqlConnection(CS))
{
using (SqlCommand cmd = new SqlCommand("SELECT * FROM Food", con))
{
using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
{
DataTable fFood = new DataTable();
sda.Fill(fFood);
popularFood.DataSource = fFood;
popularFood.DataBind();
}
}
}
}
and this
<asp:Repeater ID="popularFood" runat="server">
<ItemTemplate>
<div class="col-md-4">
<div><%# Eval("name") %></div>
<div><asp:Image ID="foodImage" runat="server" ImageUrl='<%# "~/images/food/" + Eval("image").ToString() %>' Visible="true" CssClass="img-responsive" /></div>
<div>
<div class="col-md-4 text-left"><%# "$"+Eval("price_small")+"/small" %></div>
<div class="col-md-8 text-right">
</div>
</div>
</div>
</ItemTemplate>
</asp:Repeater>
I'm trying to put the button inside the text-right div
Within your Repeater's ItemTemplate, add a button. In the button, set the CommandArgument to some key value that uniquely identifies the product.
<asp:Button ID="Button1" runat="server" Text="Click Me" CommandArgument='<%# Eval("Product.Key") %>' OnClick="Button1_Click" />
In your code, the event handler can use the button's CommandArgument to get the product key and execute your "Add to card" method.
protected void Button1_Click(object sender, EventArgs e)
{
string myProductKey;
// Use "myProductKey" to do something with the Product record...
myProductKey = ((Button)sender).CommandArgument;
}
I have a button in a ListView that when its click it opens a modal with information relevant to the button that was clicked. I was able to get the index of the current listview item but I need to get the text from a label in both the previous item and next item. Heres what I have:
protected void List_ItemCommand(object sender, ListViewCommandEventArgs e)
{
ListViewDataItem dataItem = (ListViewDataItem)e.Item;
//Gets index of Listview
int DispalyIndex = e.Item.DisplayIndex;
int ItemIndex = e.Item.DataItemIndex;
Button index = (Button)dataItem.FindControl("TitleButton");
Label Name = (Label)dataItem.FindControl("LabelName");
}
I tried decrementing the index but no luck, anyone have an idea or a better solution? Thanks.
UPDATE
Heres my listviews, I use the first Listview to get the title and then the second to pull Jobs under the title. I bind both with a query using data bind
<asp:ListView ID="List" runat="server" OnItemCommand="List_ItemCommand" OnItemDataBound="List_ItemDataBound">
<LayoutTemplate>
<table>
<tr>
<asp:PlaceHolder ID="itemPlaceholder" runat="server"></asp:PlaceHolder>
</tr>
</table>
</LayoutTemplate>
<ItemTemplate>
<span class="label label-primary"><%# Eval("LabelName")%></span> <br />
<asp:ListView ID="JobList" runat="server" ItemPlaceholderID="JobPlaceHolder" OnItemDataBound="JobList_ItemDataBound">
<LayoutTemplate>
<asp:PlaceHolder runat="server" ID="JobPlaceHolder" />
</LayoutTemplate>
<ItemTemplate>
<br />
<asp:Button runat="server" ID="TitleButton" Text='<%# Eval("Job Title") %>' Font-Size="XX-Small" Font-Bold="true" CssClass="btn-xs btn-default" ClientIDMode="Static" OnClick="TitleButton_Click" />
</ItemTemplate>
<EmptyDataTemplate>
<br />
<b> <asp:Label runat="server" Text="There is no job for this Family and Level!" /></b>
</EmptyDataTemplate>
Try this
//null check before performing an operation, dataItem might be the first element in the page
//If so, prevItem will be null
var prevItem = List.Items[itemIndex - 1].FindControl("LabelName") as Label;
//null check before performing an operation, dataItem might be the last element in the page
//If so, nextItem will be null
var lastItem = List.Items[itemIndex + 1].FindControl("LabelName") as Label;
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 have an accordion that has another accordion inside one of its panes. This inner accordion is created using a datasource so each of its panes are loaded from a list of objects. In this particular case, this datasource is also loaded on demand. Now, where I'm stuck is that I want to be able to load the pane headers only and then load the contents when the pane is clicked; similar to what I have in the outer pane. The reason I'm confused here, is because the lazy load happens when the pane is clicked, but since this happens AFTER the databind, I don't know how to reference the content of the pane that invokes the ItemCommand. Not sure if that makes sense. Here is the inner accordion:
<ajaxToolkit:Accordion runat="server" ID="accReviewers" OnItemDataBound="accOuterAccordion_ItemDataBound" ContentCssClass="ReviewerContent" RequireOpenedPane="False" SelectedIndex="-1" OnItemCommand="accReviewers_ItemCommand">
<HeaderTemplate>
<div>
<asp:LinkButton Text='<%#Eval("Header") %>' CssClass="InReviewHeader" runat="server"
CommandName="LoadReviewers" CommandArgument='<%#Eval("MocRequestId") %>'/>
</div>
</HeaderTemplate>
<ContentTemplate>
<div>
<asp:ListView runat="server" ID="lvReviewers" ItemPlaceholderID="phReviewer" OnItemDataBound="lvReviewers_ItemDataBound">
<LayoutTemplate>
<div>
<asp:HyperLink runat="server" ID="lnkGotoRequest" Text="View this request"/>
</div>
<asp:PlaceHolder runat="server" ID="phReviewer"/>
<div style="margin-top: 5px;">
<asp:Button runat="server" ID="btnResubmit" Text="Resubmit" CssClass="ResubmitInitial"/>
</div>
</LayoutTemplate>
<ItemTemplate>
<div class="ReviewerItem">
<%#Eval("Assignee.Name") %><br />
<img src="" alt="Reviewer" runat="server" ID="imgReviewer" width="75" style="border: 1px solid gray; border-radius: 6px;"/><br />
<asp:Label runat="server" ID="lblStatus" Text='<%#Eval("ReviewStatus") %>' />
<asp:HyperLink runat="server" ID="lnkRejectComment" CssClass="InitialRejectComment">(details)</asp:HyperLink>
</div>
</ItemTemplate>
</asp:ListView>
</div>
</ContentTemplate>
</ajaxToolkit:Accordion>
</Content>
</ajaxToolkit:AccordionPane>
As you can see, the accordion accReviewers is generated via a DataSource. The listview contained in the LayoutTemplate will not have its datasource bound until the LinkButton has been clicked, which will fire the item command. Also worth noting that this entire accordion is wrapped in an UpdatePanel.
This is the code behind I was starting to work with, but it doesn't appear to get the correct instance of the listview and while the list is not empty, it will not display anything:
protected void accReviewers_ItemCommand(object sender, CommandEventArgs e)
{
var mocId = int.Parse(e.CommandArgument.ToString());
var list = (sender as AjaxControlToolkit.Accordion).FindControl("lvReviewers") as ListView; //APPARENTLY WRONG
var reviewers = MocApi.GetReviews(mocId);
list.DataSource = reviewers;
list.DataBind();
}
So to recap, when the LinkButton within the HeaderTemplate is clicked, I need to somehow gain reference to the correct instance of the ListView so that I can bind its datasource. As always, any help or insight is appreciated. This is similar to a previous question of mine but is specific to gaining this reference after databind which seems a bit more complicated. TIA
UPDATE:
I found that I can bind the item datasource if I can somehow capture its index. I'm exploring trying to set that as a command argument during the databinding of the inner accordion.
I managed to solve this with some minor shenannigans:
Here is the markup:
<ItemTemplate>
<div class="ReviewerItem">
<%#Eval("Assignee.Name") %><br />
<div style="display: inline-block; position: relative;">
<img src="" alt="Reviewer" runat="server" ID="imgReviewer" width="75" style="border: 1px solid lightgray; border-radius: 6px; overflow: hidden;"/><br />
<div runat="server" ID="divYes" Visible="False">
<img src="../Images/Yes.png" alt="Approved" class="ApprovalIcon" />
</div>
<div runat="server" ID="divNo" Visible="False">
<img src="../Images/No.png" alt="Rejected" class="ApprovalIcon" id="imgNo" />
</div>
</div>
<asp:Label runat="server" ID="lblStatus" Text='<%#Eval("ReviewStatus") %>' />
<asp:HyperLink runat="server" ID="lnkRejectComment" CssClass="InitialRejectComment">(details)</asp:HyperLink>
<asp:Panel runat="server" ID="pnlDemoApproval" Visible="False" CssClass="DemoButtons">
<asp:Button runat="server" ID="btnApprove" Text="Approve" CommandArgument='<%#Eval("Assignee.Guid") + "|" + Eval("Ticketid") %>' CommandName="ApproveReview"/>
<asp:Button runat="server" ID="btnDeny" Text="Deny" CommandArgument='<%#Eval("Assignee.Guid") + "|" + Eval("Ticketid") %>' CommandName="DenyReview"/>
</asp:Panel>
<ajaxToolkit:BalloonPopupExtender runat="server" ID="balloon" BalloonPopupControlID="pnlPopup"
TargetControlID="lnkRejectComment" Position="TopRight" BalloonStyle="Cloud" BalloonSize="Medium" DisplayOnMouseOver="True"/>
<asp:Panel runat="server" ID="pnlPopup">Rejection Reason</asp:Panel>
</div>
</ItemTemplate>
On databind, I catch the item so that I can grab the index and set it to the CommandName for later use:
AjaxControlToolkit.AccordionItemEventArgs e)
{
if (e.ItemType != AjaxControlToolkit.AccordionItemType.Content) return;
var index = e.ItemIndex;
var button = e.AccordionItem.Parent.FindControl("lnkbHeader") as LinkButton;
if (button != null) button.CommandName = index.ToString();
}
Now that control contains the index, i can use that to target the correct pane and bind its datasource:
protected void accReviewers_ItemCommand(object sender, CommandEventArgs e)
{
//This seems stupid to put here, but for some reason the item command bypasses the listview catch and passes it to the accordion
if (e.CommandName == "ApproveReview")
{
var assigneeGuid = new Guid(e.CommandArgument.ToString().Split('|')[0]);
var ticketId = int.Parse(e.CommandArgument.ToString().Split('|')[1]);
var ticket = new MocApproval(ticketId);
DoDemoApproval(ticketId, assigneeGuid, true);
var approvalIndex = (sender as AjaxControlToolkit.Accordion).SelectedIndex;
var lv =
(sender as AjaxControlToolkit.Accordion).Panes[approvalIndex].FindControl("lvReviewers") as ListView;
lv.DataSource = MocApi.GetReviews(ticket.MocRequest);
lv.DataBind();
return;
}
if (e.CommandName == "DenyReview")
{
var assigneeGuid = new Guid(e.CommandArgument.ToString().Split('|')[0]);
var ticketId = int.Parse(e.CommandArgument.ToString().Split('|')[1]);
var ticket = new MocApproval(ticketId);
DoDemoApproval(ticketId, assigneeGuid, false);
var approvalIndex = (sender as AjaxControlToolkit.Accordion).SelectedIndex;
var lv =
(sender as AjaxControlToolkit.Accordion).Panes[approvalIndex].FindControl("lvReviewers") as ListView;
lv.DataSource = MocApi.GetReviews(ticket.MocRequest);
lv.DataBind();
return;
}
...