Customizing output of datasource in repeater? - asp.net

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.

Related

Parsing JSON data in an ASP.NET repeater control

I'm building a small test prototype where I'm pulling data from a back-end SQL database using a repeater and an entity data source. One of my columns returns data in JSON format.
Question: is there any way to parse JSON data within a repeater (or, for that matter, any other ASP.NET data control)? I was hoping that there'd be a relatively easy way to do this, but I'm discovering that's not the case.
Thanks in advance!
You can parse the JSON data, but the real question is where you want to parse it; on the client or on the server? Assuming you want to parse the data on the server, you can use the ItemDataBound event and the JavaScriptSerializer class:
using System.Web.Script.Serialization;
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
var jsonData = (string)DataBinder.Eval(e.Item.DataItem, "JsonData");
var jss = new JavaScriptSerializer();
var dict = jss.Deserialize<Dictionary<string,dynamic>>(jsonData);
}

How are DataTextField and DataValueFields evaluated

For a data bound control it is common scenario where we provide data text field and data value field ( in simple controls like Dropdownlist) but more fields in controls like Gridview. Generally the datasource is of type IEnumerable.
How does the control internally process these values or rather how do they get the value from the data source without knowing what kind of datasource they are dealing with.
Can someone explain with code how the controls evaluate these fields from the data source.
Typically, the data-bound control (or concerned components such as
DataControlField in GridView) will handle DataBinding event.
Within event handler, the data item that is being currently bound (e.g. DataRowView or entity instance) is retrieved. This is done via DataBinder.GetDataItem passing the actual control or control's NamingContainer. For example, if you are implementing a lower level control such as DataControlField for higher level data-bound control such as GridView then it would handle data-binding of a cell control and hence it will use cell's naming container to pass to DataBinder.GetDataItem method which uses current data binding context to get the same.
Once the data item object is retrieved, one need to evaluate the given data-binding expression against it to get the actual value and apply any formatting as per different properties set to the control/component. The most simple way is to use DataBinder.Eval overload. However, one may use the more efficient ways - for example, say DataField string is going to be only property name then you may look and cache the property descriptor and then use the same against different data items.
I will suggest you to use tool such as Reflector to inspect relevant control's code to get the better idea.
I never knew i could find this information so easily and LLyod was in fact wrong on using reflection to find data from a datasource. None of the data controls use it when i inspected through Reflector ;(
link that solved the problem
http://msdn.microsoft.com/en-us/library/ms366540.aspx
how you do it is below
protected override void PerformDataBinding(IEnumerable retrievedData)
{
base.PerformDataBinding(retrievedData);
// Verify data exists.
if (retrievedData != null)
{
string dataStr = String.Empty;
foreach (object dataItem in retrievedData)
{
if (DataTextField.Length > 0)
{
dataStr = DataBinder.GetPropertyValue(dataItem,
DataTextField, null);
}
else
{
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(dataItem);
if (props.Count >= 1)
{
if (null != props[0].GetValue(dataItem))
{
dataStr = props[0].GetValue(dataItem).ToString();
}
}
}
}
}
}
If the above code seem Greek and Latin , you will have to have a course on asp.net controls development to understand what is being done.

Asp.Net GridView EditIndex race condition?

This is a bit of a hypothetical question that has sent me off down the garden path... Say I have a gridview that I'd like to edit... given a method that binds the data..
private void BindGridFirst() {
var data = new List<string>() {
"A","B","C","D","E","F"
};
gridView.DataSource = data;
gridView.DataBind();
}
Now presume that I'm looking at this page, and another user has come along and made some changes to the underlying data, and I now go and click the edit button to edit D...
The edit method is pretty straight forward:
protected void RowEdit(object sender, GridViewEditEventArgs e) {
gridView.EditIndex = e.NewEditIndex;
BindGridSecond();
}
Edit: I feel compelled to point out, that this method is used in pretty much all the online examples, including ones from Microsoft.
This BindGridSecond() method looks like so:
private void BindGridSecond() {
var data = new List<string>() {
"A", "AA", "B","C","D","E","F"
};
gridView.DataSource = data;
gridView.DataBind();
}
It's exactly the same, but the data is now changed. Once the UI updates, the user is now in edit mode against row C.
Not what the user expected or wanted. How should this scenario be handled to avoid such an issue?
Personally, I use the DataKeyNames property and the SelectedDataKey on the GridView, so that I can easily obtain the primary key of the row the user wants, rather than relying on the index of the grid.
By using the primary key, you don't have any issues with new items being added to the collection, such as in your example. Plus, using the primary key makes it easier to deal with paging on the grid, as you don't have to take the page number and index into account.
Imho there are two options:
You could cache the Data you want to bind to the Grid in a Session for example. So you are able to check for changes before you call the BindGridSecond-Method and alert the user if any changes have been made while he was browsing the Page.
In option 2 you would again cache the Data you were binding in the BindGridFirst-Method and just work with this data for the next PostBack actions. So you don't have to worry about changes that may occur while browsing the Grid.

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!
}
}

Reading data from BaseDataBoundControl.DataSource (ASP.NET GridView)

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.

Resources