GridView Template - How to Grab Data from Selected Row - asp.net

<asp:TemplateField>
<ItemTemplate>
<table width="540" cellpadding="5">
<tr>
<td align="left" style="width:60%;">
<img src='PurchaseHandler.ashx?ProductID=<%# Eval("ProductID")%>'
alt="<%# Eval("ProductName") %>" />
</td>
<td align="left">
<h3 style="text-align:left;">
<asp:Label ID="nameLabel" runat="server"
Text='<%# Eval("ProductName") %>' />
</h3>
<asp:Label ID="priceLabel" runat="server" Text='<%# Eval("Price") %>' />
<br />
<asp:LinkButton ID="cartLink" runat="server" Text="<b>Add to Cart</b>"
CommandName="Add" CommandArgument='<%# Eval("ProductID") %>' />
</td>
</tr>
</table>
</ItemTemplate>
</asp:TemplateField>
I'm using a shopping cart business object which contains fields not used for display in the GridView. What I'm attempting to do next in the RowCommand event handler is to retrieve the rest of the data fields from the selected row. This gives me the correct product ID from the selected row:
if (e.CommandName == "Add")
{
int productID = 0;
int.TryParse(e.CommandArgument as string, out productID);
// Big blank!
}
How can I grab the rest of the data fields from the selected row to populate my cart? By way of explanation, I can probably use the productID to dig into the DataSet pulled from Session state, and get the data that way. However, what I'm trying to determine is if there is a syntax similar to this that can be used in this situation?
DataRow[] rows = ds.Tables[0].Select("ProductID=" +
gridProducts.SelectedDataKey.Values["ProductID"].ToString());
DataRow row = rows[0];

One way to do this would be to use the command argument to store the row index (perhaps setting it in the on row created event).
You could then use code as below to access your row (code snipped from MSDN):
// Convert the row index stored in the CommandArgument
// property to an Integer.
int index = Convert.ToInt32(e.CommandArgument);
// Retrieve the row that contains the button clicked
// by the user from the Rows collection. Use the
// CommandSource property to access the GridView control.
GridView customersGridView = (GridView)e.CommandSource;
GridViewRow row = customersGridView.Rows[index];
If you need to keep your command argument as the product id then you can use the code below to access the row:
GridViewRow row = (GridViewRow)(((LinkButton)e.CommandSource).NamingContainer);
You can then use the FindControl() method of the GridViewRow to select the controls that you need.
Label priceLabel = row.FindControl("priceLabel") as Label;
Edit after comments from OP
So if I'm correct, you want to access the DataItem of your row?
I don't think this is possible within a RowCommand event handler - I did a little bit of digging on other forums and apparently this property is only not null during DataBind - MSDN has this to say:
The DataItem property is available only during and after the RowDataBound event of a GridView control.
Looks like your method or something similar is the only way of linking back to the original objects in the RowCommand (hopefully someone proves me wrong)

Related

how to bind memory stream to asp.net image controls with out using image url

We have a new requirement to show the signature image in result grid for each record. Currently we store the signature image as base64 format in our database .we are able to convert base 64 to memory stream.
We want to understand does any of your image control support to bind image from memory stream instead of Url .
You could keep images as base64 string form, and use data url.
The following is a example.
<asp:Image runat="server" ID="myImage" />
The code behind is
this.myImage.ImageUrl = "data:image/png;base64,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";
The premise is that you need to know all the MIME type of images.
Reference: data URIs
Sure, you can do it this way.
A gridview, listview, repeater control - they can all do this.
So markup for a grid view:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="Animal" HeaderText="Animal" ItemStyle-Width="200px"/>
<asp:TemplateField HeaderText="Picture">
<ItemTemplate>
<asp:Image ID="Image1" runat="server"
Height="128px" Width="128px"
src='<%# "Data:Image/png;base64," + Eval("Image") %>'
/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
And the code to load up the gridview would be:
protected void Page_Load(object sender, System.EventArgs e)
{
if (System.Web.UI.Page.IsPostBack == false)
LoadGrid();
}
public void LoadGrid()
{
using (SqlCommand cmdSQL = new SqlCommand("SELECT Animal, Image from tblAnimals",
new SqlConnection(My.Settings.TEST4)))
{
cmdSQL.Connection.Open();
GridView1.DataSource = cmdSQL.ExecuteReader;
GridView1.DataBind();
}
}
Results:
Now I used a direct eval expression - but you could also use the ItemDataBound event of the gridview (or list view).
Also, I did hard code the "mine" type in above (assumed .png). You often might want to consider to save the file/mine type as a extra column in the database. But, if the picture types are to be all the same types - then a hard code right in the expression as per above will work fine.
If you have a lot of columns for this grid, then I tend to switch over to listview, since each column does NOT need a template, and I find for quick and dirty - grid view is ok, but listview is better for tweaking and lots of customizing. The trick is to get Visual Studio to generate the template + markup, and then blow out all templates except for item template. When you do this, then the crazy truckload of markup is reduced down to "easy" levels. And you can then with greater ease drop in controls - and you don't have template them.
So, the same listview for above would be this:
<asp:ListView ID="ListView1" runat="server">
<ItemTemplate>
<tr style="">
<td><asp:Label ID="AnimalLabel" runat="server" Text='<%# Eval("Animal") %>' /></td>
<td>
<asp:Image ID="ImageT1" runat="server"
Height="128px" Width="128px"
src='<%# "Data:Image/png;base64," + Eval("Image") %>'
/>
</td>
</tr>
</ItemTemplate>
<LayoutTemplate>
<table id="itemPlaceholderContainer" runat="server" border="0" style="">
<tr runat="server" style="">
<th runat="server" width="200">Animal</th>
<th runat="server">Image</th>
</tr>
<tr id="itemPlaceholder" runat="server">
</tr>
</table>
</LayoutTemplate>
</asp:ListView>
So there is somewhat "more" markup, but you can with much greater ease drop in asp.net controls - and you don't have add a templatefield - (about 4 extra lines of markup for every column - so in the long run, I like the listview.
it looks quite much the same, like this:
And the behind code would be 100% the same as above.

Set IDs of controls of a repeater

I have a repeater that contains a few controls and I want to set their ID's based on the IDs from database.
The datasource of the repeater is a list so basicly I want to do something like this in code behind, in repeater_ItemDataBound():
var myControl = e.Item.FindControl("controlID");
myControl.ClientIDMode = ClientIDMode.Static;
myControl.ID = e.Item.DataItem("ID"); //but I can't access the ID property, so here's my problem.
considering that I declared my repeater something like:
<asp:Repeater ID="repeater" runat="server">
<ItemTemplate>
<div class="someClass">
<asp:Label ID="controlID" runat="server"><%# Eval("Name")%></asp:Label>
<!-- list of other controls -->
</div>
</ItemTemplate>
</asp:Repeater>
Don't change ID's of controls that were already created with a different ID. That could cause nasty errors. Instead use the right ID in the first place. Or use whatever ID you use and assign the identifier to a different property like CommandArgument if it's a Button, Value if it's a HiddenField or Text if it's an (invisible) TextBox or Label.
So in this case you could use another control to store the ID:
<asp:HiddenField ID="hiddenID" runat="server" Value='<%# Eval("ID")%>' />
<asp:Label ID="lblName" runat="server"><%# Eval("Name")%></asp:Label>
Now, if you need the ID of the current item and you have the reference of the label or another control in that repeater-item:
var item = (RepeaterItem) lblName.NamingContainer;
HiddenField hiddenID = (HiddenField) item.FindControl("hiddenID");
string id = hiddenID.Value;

DataValueField pass value to repeater?

So if the user selects a value from a dropDownList and clicks the button, the ID is passed to the code behind(which is where I want it)
E.g.
<asp:DropDownList ID="DropDownList" runat="server" AppendDataBoundItems="true" DataTextField="Company_Name" DataValueField="id">
<asp:ListItem Text="---Select---" Value="0" />
</asp:DropDownList>
So dataValueField will pass the 'id' of the selected record in the DD.
However On the same page I am using a repeater to display records that have been previously chosen from the drop down, Beside each record i have a 'Change Prices' button which I want to perform a task when clicked, all works fine however all I need is the same 'id'.
So is this done in a similar way? E.g
<asp:Repeater ID="repeaterShowName" runat="server">
<HeaderTemplate>
<tr>
<th>
<asp:Label ID="SubConName" Text="Current SubContractors" runat="server"></asp:Label>
</th>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<asp:Label ID="SubCon" Text='<%# Bind ("Company_Name") %>' runat="server"></asp:Label>
</td>
<td>
<asp:LinkButton ID="AddNewBOQLink" runat="server" OnClick="EditPricesForSubContractor" CssClass="bottomhyperlink">Change Prices</asp:LinkButton>
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
//So Do I add in DataValueField="id" inside the link button, so when the user selects the hyperlink beside the name, it will automatically have the 'id?
You should use the CommandArgument property of the LinkButton to pass the id to the method that handles the click.
So your LinkButton will now look like this:
<asp:LinkButton ID="AddNewBOQLink" runat="server" CommandArgument='<%# Bind("id")%>' CssClass="bottomhyperlink">Change Prices</asp:LinkButton>
And you want to add the following property to your Repeater:
<asp:Repeater .... OnItemCommand="repeater_Command" ...
The method that will handle the command event will look like this
void repeater_Command(Object Sender, RepeaterCommandEventArgs e) {
//retrieve the id like so
int id = (int)e.CommandArgument;
}
Also, note that I removed the OnClick property from the LinkButton. It is not needed as you are handling the click via the repeater's OnItemCommand method.
In addition, you may want to consider adding a CommandName propery to the LinkButton. This is used to identify which command you wish to execute. Currently, you only have a single command: Change Prices. But in the future you may want to add an additional button with a different command.
In order to do this, add the following property to the LinkButton:
<asp:LinkButton ... CommandName="ChangePrice" ...
And modify the repeater_Command method to handle the CommandName. Like so:
void repeater_Command(Object Sender, RepeaterCommandEventArgs e) {
switch(e.CommandName)
{
case "ChangePrice":
//retrieve the id like so
int id = (int)e.CommandArgument;
break;
}
}

find control in listview

By pressing button in GridView i need to find control in Listview.
<ItemTemplate>
<td>
<asp:Label ID="lblMarketLVBalanceHeader" runat="server" Text="Balance: "></asp:Label>
</td>
<td>
<asp:Label ID="lblMarketLVBalanceValue" runat="server" Text='<%# Bind("Money", "{0:####}$") %>'></asp:Label>
</td>
</ItemTemplate>
Code Behind:
protected void GVMarketItems_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName.Equals("Buy"))
{ GridViewRow row = (GridViewRow)(((Button)e.CommandSource).NamingContainer);
/* string itemID = row.Cells[0].Text;
string itemName = row.Cells[1].Text;
string itemCostStr = row.Cells[3].Text; */
string curBalanceStr = ((Label)LVMarketBalanceShow.FindControl("lblMarketLVBalanceValue")).Text;
}
Code seems find, but i have "Object reference not set to an instance of an object" when i'm trying to Find control.
string curBalanceStr = ((Label)LVMarketBalanceShow.FindControl("lblMarketLVBalanceValue")).Text;
Did the same to DetailsView and it was Ok.. What's wrong with ListView?
UPD: Trying to get first fow of listview
ListViewDataItem curBalanceLV = LVMarketBalanceShow.Items[0];
string curBalanceStr =((Label)curBalanceLV.FindControl("lblMarketLVBalanceValue")).Text;
But getting an error "Index was out of range."
I think you want to find the control within the specific row.
string curBalanceStr = ((Label)row.FindControl("lblMarketLVBalanceValue")).Text
Did the same to DetailsView and it was Ok.. What's wrong with
ListView?
A DetailsView is used to tackle with one row at a time so you can directly call FindControl() on the detailsview but gridview and listview are meant to display multiple records and your markup you define inside <ItemTemplate /> is just a template for each row. You'll be able to find the controls that you define in the template inside of each row.

Is it possible to do custom grouping in the ASP.NET ListView control?

You can only define a GroupItemCount in the ListView, but what if you want to do grouping based on a property of the items in the data source? Sort of an ad-hoc group by. The data source is sorted on this property.
I have seen some examples where some markup in the ItemTemplate was conditionally show, but I want to leverage the GroupTemplate if possible.
Is this possible?
When I had to add basic group headings in a repeater I did so with a Literal control in the ItemTemplate:
<asp:Literal runat="server" Text='<%# GetGroupHeading(Eval("Group")) %>' />
The 'GetGroupHeading' method in the code kept track of the previous group heading and sent back '<h2>Group Name</h2>', or an empty string if we were on the same group as the previous item. As I said though, I did this on a Repeater, so not sure if it will cover what you need for a ListView.
Yes Nick gave a great lead. Here's my code-behind
Dim sCategory_Descr As String
Function GetGroupHeading(ByVal sGroupName As String) As String
Dim sReturn As String
If sCategory_Descr <> sGroupName Then
sCategory_Descr = sGroupName
sReturn = "<H5>Category: " & UCase(sGroupName) & "</H5>"
Else
sReturn = ""
End If
Return sReturn
End Function
And my item_template
<ItemTemplate>
<tr>
<td style="background-color:#ccc;" colspan="2" id="tdCategory_Placeholder" runat="server" >
<asp:Label Font-Bold="true" ID="Literal1" runat="server" Text='<%# GetGroupHeading(Eval("Category_Descr")) %>' />
</td>
</tr>
<tr>
<td >
<asp:DynamicControl1 />
</td>
<td >
<asp:DynamicControl2 />
</td>
</tr>
</ItemTemplate>
Try this article from 4 Guys from Rolla: Using ASP.NET 3.5's ListView and DataPager Controls: Grouping Data with the ListView Control

Resources