Reading data from BaseDataBoundControl.DataSource (ASP.NET GridView) - asp.net

I have an ASP.NET 3.5 GridView on a WebForm.
The GridView gets data from an ObjectDataSource which is set via the DataSourceID property in the code in front.
The ObjectDataSource returns a List of custom data class objects (just a class with public properties) to populate the GridView.
What I want to do is use the a List comsumed by the GridView in another code-behind method. At a high level:
1. GridView is loaded with List data from ObjectDataSource.
2. In the GridView.OnDataBound method I call GridView.DataSource to get the List object.
3. I enumerate the List and use the same data to do some other operation.
The theory being one less duplicated method call and one less call to the back-end database.
I've tried calling DataSource from the GridView' DataBound method and calling GridView.Rows[x].DataItem. In each case I only get a Null reference exception ("Object reference not set to an instance of an object").
Is there any way to achieve what I'm after?

If I understand you correctly, you want the OnRowDataBound event. This way, you can use data from the row that was just databound:
protected void gvGrid_RowDataBound(object sender, GridViewRowEventArgs e)
{
CustomDataClass data = e.Row.DataItem as CustomDataClass;
if (data != null)
{
// access data here...
}
}

But do you want the onRowDataBound event? It looks like you want the onDataBound event for the GridView's entire datasource...
So you don't necessarily want one instance (row) of CustomDataClass, you want the entire CustomDataClass[] array of rows to use somewhere else.
HELP! I need this too.
******UPDATE******
I found the answer. Do this as below and set the OnSelected event in your objectdatasource:
protected void ObjectDataSource_Selected(object sender, ObjectDataSourceStatusEventArgs e)
{
ObjectListRow[] objectArray = (ObjectListRow[])e.ReturnValue;
List objectList = objectArray.ToList();
}
It turns out my datasource was an array, but if yours is a List<> then just cast the e.ReturnValue as the List.
EASY CHEESY.

Related

How to get Calling Gridview name in ObjectDataSource Selecting event

I have 2 gridviews, gv1 and gv2 and an ObjectDataSource with the id ods1. Both the gridviews are pointing to DataSourceID="ods1".
My question is, how do I know in selecting event of an ObjectDataSource that which gridview has called ods1. I want to set input parameters based on which gridview has made a call to the ods1.
I think this is not easily possible and it feels like it would be against the idea behind ODS.
You can delegate two ObjectDataSources to get the data from THE SAME repository class but still, you need two different data sources if you want to have two different sets of parameters. Thus, you do not duplicate code as the repository code is shared between object data source instances.
Warning: Hack ahead
I tend to agree with Wiktor Zychla's answer, but if you really need to do this...
The only thing I can think of to accomplish this would be to handle the "DataBinding" event of each of your GridViews, and set a session variable to indicate which one is about to call the ObjectDataSource "Selecting" event.
So you would have your GridView methods:
protected void gv1_DataBinding(object sender, EventArgs e)
{
Session["currentGridID"] = "gv1";
}
and
protected void gv2_DataBinding(object sender, EventArgs e)
{
Session["currentGridID"] = "gv2";
}
And then, your ObjectDataSource could check that Session variable, to see which ID is in it while the ObjectDataSource is firing this time:
protected void ods1_Selecting(object sender, ObjectDataSourceSelectingEventArgs e)
{
if(Session["currentGridID"] == "gv1")
{
}
else
{
}
}
To get the name of the gridview which call the objectdatasource
You can do something like:
string CallingGridName = ((ObjectDataSourceID)sender).ID;

Customizing output of datasource in repeater?

I have a Data Repeater hooked up to a datasource (datatable object). I need to change the output on the frontend for certain columns under certain conditions. What would be the most efficient way to do this?
I am currently trying to create the formatted output and assign it to another datatable and use that as the data source, but it seems overly complicated and something that would be hard to maintain.
Is there an easier way to manipulate column values for a datasource? I need the ability to check the previous and next rows for the source as that is a basis for some of the column values.
If you're talking about simple manipulation, the DataBinder.Eval method accepts a format string:
<%#Eval("SomeMoneyColumn", "{0:C}")%>
If the format string is not sufficient, you could create a method in the code-behind to handle the formatting, like this:
<%#FormatData(Eval("SomeColumn"))%>
In code-behind:
protected string FormatData(object data)
{
return String.Format("My name is {0}", data);
}
You can also use the ItemDataBound event too. Using this technique, you can still access the datasource object, in the case that your manipulation involves other data that is bound to the repeater.
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
Label lblCtrl = e.Item.FindControl("SomeControl") as Label;
if (lblCtrl != null)
{
lblCtrl.Text = String.Format("My name is {0}", DataBinder.Eval(e.Item.DataItem, "SomeColumn"));
}
}
I don't think there's a way to do what you want on the client side easily w/o using special logic like you are doing now. If you are getting data from a database, you could potentially do all the data manipulation on the DB side and pass it along transparently to the front end.

Why is this DropDownList data binding to a List<String> not working?

I'm trying to bind a List<String> to a DropDownList in a user control. I think I'm doing the right thing, but it seems that after my code executes the bindings are cleared. Here's the code for review!
User control:
<asp:DropDownList ID="subjectNameDropDown" runat="server"/>
<asp:DropDownList ID="yearLevelDropDown" runat="server"/>
Auto-generated designed code-behind:
public partial class NewSiteMetadataUserControl {
protected global::System.Web.UI.WebControls.DropDownList subjectNameDropDown;
protected global::System.Web.UI.WebControls.DropDownList yearLevelDropDown;
}
Code-behind:
public partial class NewSiteMetadataUserControl : UserControl
{
protected override void CreateChildControls()
{
subjectNameDropDown = new DropDownList();
yearLevelDropDown = new DropDownList();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
EnsureChildControls();
// Attempt 1
List<String> subjectNames = GetSubjectValues();
foreach (var subjectName in subjectNames)
subjectNameDropDown.Items.Add(subjectName);
subjectNameDropDown.DataBind();
// Attempt 2
List<String> yearLevels = GetYearLevelValues();
yearLevelDropDown.DataSource = yearLevels;
yearLevelDropDown.DataBind();
}
}
Should this approach work?
If it should, how can I debug what happens after the code executes?
Yes, this approach should work, here's why it currently isn't,
A DropDownList done with DataBind needs a DataSource. This is why Attempt #1 is not working.
If you're binding to a List<string>, there is no clear key/value pair to bind to. This is why when binding to a List<Person> (for example), you need to override .ToString() in the Person class to provide the key/value binding, or manually set the DataTextField, DataValueField.
There is no way for ASP.NET to work out a key/value pair for a string.
Think about what HTML you want. What should be the key/value for a simple string? Doesn't make sense does it.
Since you don't really care about the "key" (only what is displayed), i suggest you bind to a Dictionary<TKey,TValue> instead.
Either make your method return that, or iterate through the list and add them to the dictionary with an index.
The problem here was CreateChildControls. Somewhere in my attempts to make this work I added this method that initialises the controls. This isn't necessary and in fact caused the data bindings to be wiped out, as it was automatically called by the framework after OnLoad.
The solution was to remove this method and the call to EnsureChildControls.

Object data source select method returning nothing in code call

I have an ObjectDataSource with the proper SelectMethod and SelectParameters configured. The data source is bound to a grid view which successfully displays the data on page load.
What I need is the ability to rerun the Select method defined by the ObjectDataSource to be stored in a variable and manipulate the items in it. The issue I keep encountering is that calling the .Select() method always returns 0 rows despite it populating the grid view properly.
Is there a reason I can't manually rerun the Select() method on the object data source?
Update 2:
Here is how I setup the ObjectDataSource:
myObjectDataSource.TypeName = typeof(MyDataAccessObject).ToString();
myObjectDataSource.SelectMethod = "GetBy" + stringVariable;
myObjectDataSource.SelectCountMethod = "GetCountBy" + stringVariable;
myObjectDataSource.EnablePaging = true;
Update 1:
I run the Select() on a link button's OnClick event:
protected void LinkButton1_Click(object sender, EventArgs e)
{
SetupDataSource(); // populates the objSource's SelectMethod, SelectParameters and TypeName etc.
var items = objSource.Select();
int count = items.Count(); // returns 0;
}
The ObjectDataSource is setup (SelectMethod and SelectParameters are set) in the Page_Load event.
ObjectDataSource definition:
<asp:ObjectDataSource ID="objSource" runat="server" EnablePaging="True" SortParameterName="sortExpression" ></asp:ObjectDataSource>
GridView definition:
<asp:GridView
ID="myGridView"
runat="server"
DataSourceID="objSource"
AllowPaging="true"
ShowHeader="true"
AutoGenerateColumns="false"
AllowSorting="true"
Width="100%" >
Turns out that underlying data access object's method took pagination into account and was returning .Take(maximumRows) which was always 0.
As a workaround, I programmatically disabled pagination via myObjectDataSource.EnablePaging = false; then created a new function which does not take into account pagination (a new function was required because the ObjectDataSource was looking for a function with specific paremeters).
You don't mention when (in what event you are trying to rebind the to the DataSource)
Short code snippet may help.
If you are in Postback then Gridviews are not re-bound on postback, their rows are pulled back from viewstate. Resetting the gridview's DatasourceID to the object data source ID on page load (or init?) will cause the gridview to be rebound.
I had similar problem before. I think .Select() is implemented using DataReader and once Select has been called once the reader is empty so any subsequent call to .Select or .Count() will return empty result.
Therefore, what you can do is to use .ToList() to store the result in a list and then just keep reusing the list.

ASP.net binding to a property that is an array of custom types

I'll use Customer and Addresses as an example rather than explain my real objects.
Say I have a repeater bound to a collection of Customers and one of the properties of Customer is an array of addresses. How do I bind to the addresses property?
I don't even need to display this information I just want to use it in the Repeaters ItemDataBound event. So I tried to bind a hiddenField to the addresses property but all I get for every customer in the hiddenfields value is an empty array of addresses.
I suppose what would be ideal is if I could bind the hiddenfield to a string representing the addresses array. (perhaps in JSON format). How can I do that? Or has anyone got any better suggestions?
protected void Repeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
Customer c = (Customer)e.Item.DataItem;
foreach (Address a in c.Addresses)
{
//WHEE!
}
}

Resources