Get entity object from CommandArgument in GridView RowCommand - c#

I am using a GridView which is bound to a List<Customer>. When RowCommand is fired by some button I want to be able to retrieve the current row's Customer object from the e.CommandArgument, like so:
protected void GridView_RowCommand(object sender, GridViewCommandEventArgs e)
{
Customer customer = (Customer)e.CommandArgument;
DoSomething(customer);
}
How do I assign the Customer object to CommandArgument before the event is fired?

I am not sure you can actually store a complex object in the CommandArgument as it seems to only take string or numeric values.
Something, which I have never really put into practice, is to convert your object into a Json string and use this as your command argument.
For example, using Newtonsoft.Json and a dummy object called FooBar
<asp:Button ID="btnOne" runat="server"
CommandName="Click"
CommandArgument='<%#Newtonsoft.Json.JsonConvert.SerializeObject((FooBar)Container.DataItem) %>'
Text="Click" />
Then when you handle the GridView RowCommand click event, you can Deserialize the object from the CommandArgument
FooBar fooBar = Newtonsoft.Json.JsonConvert.DeserializeObject<FooBar>(e.CommandArgument.ToString());
Now this works, but whether it is the best solution, I am not sure.

Related

How to pass a value from function in aspx to cs.file

I am sure that I am missing a small thing. I have ddl and a function, i want to pass the 'value attribute' of a chosen ddl option to my function in cs file.
But I cant get the value attribute..
-I checked the DDL and the Value attribute is fine and helds my info well.
--I tried using the word - this. to get my value but it didnt work..
aspx page
<asp:DropDownList ID="myDdl" runat="server" onchange='<%# orgenaize('here I need the value attribute') %>'/><br />
aspx.cs file
public void orgenaizeCheckBox(string currentId)
{
//do something
}
You can add javascript event handler using Attributes.Add in code behind where you can easily use the value from dll.
myDdl.Attributes.Add("onchange", "orgenaize(" + dllClass.Attribute + ");");
<asp: DropDownList ID ="ddlValue" runat ="server" AutoPostBack="True"
OnSelectedIndexChanged="ddlValue_SelectedIndexChanged"
ToolTip ="Please Select value">
</asp: DropDownList>
Then in your .cs class just handle the event
protected void ddlPhoneModel_SelectedIndexChanged(object sender, EventArgs e)
{
// What ever you want to do on selected index change
ddlValue.SelectedItem.Value
}
.Value should work, long time since i played around with Web controls but that should work

Pass object as CommandArguement in a Repeater Link Button

I have a Repeater with a list of Customers. Against each customer there is a delete link button. As part of the linkbutton I want to pass the Customer object to the Command Arguement as follows (where Container.DataItem is the customer object):
<asp:LinkButton ID="lnkDelete"
OnClientClick="return confirmDelete();"
OnClick="Customer_OnDelete"
CommandArgument="<%# Container.DataItem %>"
CommandName="Delete"
runat="server"></asp:LinkButton>
When I do this:
var button = (((LinkButton) sender));
var customer= button.CommandArgument;
button.CommandArguement is a string. I need all the object properties as we are using Nhibernate so everything needs to be set, the ID of the deleted record is not enough. I have seen examples online regarding passing a comma seperated list of values into the command arguement but want to avoid doing that. Is this possible?
Any ideas?
Thanks
In my opinion the best way for this case is:
Get the ID from CommandArgument
Get the Customer by ID
Delete the Customer Entity
Use the Repeater event OnItemCommand. This event contains RepeaterCommandEventArgs. You cant get the CommandArgument this way:
protected void myRepeater_ItemCommand(object source, RepeaterCommandEventArgs e)
{
int customerID= Convert.ToInt32(e.CommandArgument.ToString());
}
At your asp:LinkButton tag use:
CommandArgument='<%# DataBinder.Eval(Container.DataItem, "ID") %>'
The issue you are running into here is that you repeater has to get translated into HTML. Therefore you are constrained to the limits of what is allowed by the specification in an element attribute.
On the server side CommandArgument will always be a string, so you cannot do what you want as you have it coded.
Now... there are several hacks you could implement to get around this, like the aforementioned CSV, or you could use binary serialization and Base64 encode the result. However, these are all terrible solutions!
What you need is to re-think how you are doing this. I promise there is an easier way.

Passing data from gridview as session

I've a gridview with client ID, client name, client contact number and a hyperlink to client detail.
I would like to pass selected clint ID to another asp page (clientDetail.aspx & ClientContact.aspx).
I'm thinking of passing the clientID as session. But how do i go about doing this? Can someone guild me on this as im quite new to this.
& how do i use the passed data in clientDetail.aspx & ClientContact.aspx?
Thanks in advance for your help.
Add two new columns of type HyperLinkField to your gridView as follows. Now clientID is passed as QueryString. One link is here
<asp:HyperLinkField DataNavigateUrlFields="ClientID"
DataNavigateUrlFormatString="~/ClientDetails.aspx?id={0}"
Text="Client Details" />
Assuming that you are selected the grid row with a button or link button you could use the OnRowCommand event
When you wire into this event you can pick out the value you want from the selected item then save it into the Session which will then be available for subsequent pages. In the below exampel I've assumed the value is in a label field so you can pick it out of that control.
void ContactsGridView_RowCommand(Object sender, GridViewCommandEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
Label lblMyValue = (Label)e.Row.FindControl("lblMyValue");
Session["myValue"] = lblMyValue .Text;
}
}
There are other variants of this for instance you could store the value you are interested in in the CommandArgument property of the button that you use to select the row. The command argument will then be available in the RowCommand event
void ContactsGridView_RowCommand(Object sender, GridViewCommandEventArgs e)
{
string arg = e.CommandArgument;
//.. put into session here
}
And there are alternatives using different events for instance you could use the DataKeys collection of the GridView to store the value you are interested in and pick out the value from there
Markup fragment
<asp:gridview id="CustomersGridView"
//.. more properties
datakeynames="myID"
onselectedindexchanged="MyGridView_SelectedIndexChanged"
runat="server">
Code behind
void MyGridView_SelectedIndexChanged(Object sender, EventArgs e)
{
int index = MyGridView.SelectedIndex;
Session["myValue"] = CustomersGridView.DataKeys[index].Value.ToString();
}
There are a number of alternatives to get this working. I would use the first one detailed if it were me - I've always found it easiest to get to work. You can make the label hidden with Css if you want - if it isn't suitable for the UI. Or use a hidden field (with runat="server"). I'm going to stop - I'm risking confusuing by just typing on.
You should be able to evaluate the clientid field of the asp.net gridview in the navgiate url of the hyperlink and pass it as a query string like so:
<asp:gridview id="OrdersGridView"
datasourceid="OrdersSqlDataSource"
autogeneratecolumns="false"
runat="server">
<columns>
<asp:boundfield datafield="ClientID"
headertext="Client ID"/>
<asp:boundfield datafield="ClientName"
headertext="Client Name"/>
<asp:boundfield datafield="ClientContact"
headertext="Client Contact"/>
<asp:hyperlinkfield text="Details..."
navigateurl="~\details.aspx?clientid='<%# Eval("ClientID") %>'"
headertext="Order Details"
target="_blank" />
</columns>
</asp:gridview>

Dynamically adding a command button to a GridView

I'm having an issue with trying to add a button to my grid. My GridView is first loaded with data in the PageLoad event.
I'm then taking the data in the first cell of each row, and creating a button that will link to a URL. To get the URL, I have to run a query with the data in the first cell as a parameter. I was doing this in the RowDataBound event at first, but hitting that query for every row was making it really slow.
So I decided to add a button that would retrieve the URL only when you clicked the button.
Here's my GridView:
<asp:GridView ID="gvResults" runat="server"
OnRowDataBound="gvResults_RowDataBound"
OnRowCommand="gvResults_RowCommand">
</asp:GridView>
And my code:
protected void gvResults_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.DataItem != null)
{
LinkButton lb = new LinkButton();
lb.CommandArgument = e.Row.Cells[0].Text;
lb.CommandName = "NumClick";
lb.Text = e.Row.Cells[0].Text;
e.Row.Cells[0].Controls.Add((Control)lb);
}
}
protected void gvResults_RowCommand(object sender, CommandEventArgs e)
{
switch (e.CommandName.ToLower())
{
case "numclick":
string url = GetUrl(e.CommandArgument.ToString());
Response.Redirect(url);
break;
default:
break;
}
}
The grid generates fine, the button gets added to the grid for each row. But when I click on it, the RowCommand event doesn't fire, and the page just refreshes.
Does anyone know what the issue is?
Why use a dynamic button at all? You can easily put the linkbutton directly into the markup of the gridview (as long as you don't mind using a template field) and there will be no need to mess around with the RowDataBound event.
Your markup would look something like the following:
<Columns>
<asp:TemplateField HeaderText="SomeHeaderText">
<ItemTemplate>
<asp:LinkButton ID="lnkBtn" runat="server" CommandName="NumClick" CommandArgument= '<%# (string)Eval("dbValue") %>' Text='<%# (string)Eval("dbValue") %>'></asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField></asp:BoundField>
<asp:BoundField></asp:BoundField>
<asp:BoundField></asp:BoundField>
</Columns>
Add breakpoints to the RowCommand event and make sure that you can hit the breakpoints.
The problem may lie elsewhere.
Also, make sure that you're not databinding on postback.
You have a big trouble with your code. It's pretty hard for me to explain what's your big mistake, but I can easily tell you how to fix.
The problem is that you generate a new button inside the RowDataBound event, definitely the wrongest choice. The button gets rendered because it exists after that event when page renders, but doesn't exist before data binding. If you bind data everytime you load the page (even during postback) the button still gets rendered because you generate a new button.
But since the button doesn't exist before data binding, it cannot raise events. You must declare the button from markup into a template of GridView, then access it not by using new LinkButton() but by using e.Row.Cells[0].FindControl("buttonId") and set its text. Then, you have to set its markup in order to fire its own Command event (not RowCommand) and handle it as you used (don't forget to set CommandArgument during data binding)
[Edit] I also made a mistake: controls inside data bound controls also don't exist before data binding. But they are initialized not with new Control() (by the private methods of data bound control) but with Page.LoadControl(typeof(Control)). That's the first thing you must fix when you load controls dynamically!!
Because the control is added dynamically on databind and you have to databind the gridview for each postback, the control being "clicked" is different each time. The event doesn't fire because at the time it needs to fire it doesn't exist as it did in the last iteration of the page.
I notice you don't have any logic determine if the button should be there, and it always goes into cell[0].
You should place this button into a TemplateItem so that it exists properly. If you have a need to do it in code-behind, you are probably better served doing it in the RowCreated event.

SqlReader, Repeater controls and CommandArgument problem

I have a repeater control that generates a list of links from a SqlReader. I'm trying to create a button alongside each link that will allow the user to delete that link. My original thought was to use the <%#Eval("URL") %> expression in the Item template like below. However the CommandArgument in the ItemCommand method would always come back empty.
<asp:Repeater ID="rptLinks" runat="server" onitemdatabound="rptLinks_ItemDataBound"
onitemcommand="rptLinks_ItemCommand">
<ItemTemplate><%# DataBinder.Eval(Container.DataItem, "URL") %><asp:ImageButton visible="false" ID="btnDeleteLink" runat="server" ImageUrl="/Images/DeleteIcon.gif" CommandName="DELETE" CommmandArgument=<%#Eval("URL") %> />
</ItemTemplate>
<SeparatorTemplate><br /></SeparatorTemplate>
</asp:Repeater>
The next thing I tried was to use the ItemDataBound event to programatically set the CommandArgument, but I can't figure out what to cast the e.Item.DataItem into so I can reference the ["URL"] Item.
protected void rptLinks_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
ImageButton btnDelete;
btnDelete = (ImageButton) e.Item.FindControl("btnDeleteLink");
if (btnDelete != null)
{
btnDelete.Visible = (bool)ViewState["LinkEditing"];
string URL = ((WhatTypeGoesHere)(e.Item.DataItem))["URL"].ToString();
btnDelete.CommandArgument = URL;
}
}
You can cast the DataItem to any of the classes in your reader's inheritance chain that implement IDataRecord:
// You could also cast to SqlDataReader, DbDataReader, or IDataReader
string URL = ((IDataRecord)(e.Item.DataItem))["URL"].ToString();
Data readers are a bit confusing, because objects that inherit from DbDataReader can act as both a data source (through its implementation of IEnumerable) and as the object representing your data (through its implementation of IDataRecord).
The IDataRecord interface provides access to the column values of a data reader's records (via the Item property and the various Get* methods).
In other words, it's like you're binding to an IEnumerable<IDataRecord> (though not really, because DbDataReader implements the non-generic version of IEnumerable).

Categories

Resources