I am not setting my Gridview DataSource to a control like SqlDataSource or ObjectDataSource.
Instead I am Binding manually in the code behind using a method I wrote:
protected void Bind()
{
CustomDepartmentGV.DataSource = GetSortedDepartments();
CustomDepartmentGV.DataBind();
}
I am also manually handing update through the Gridview properties:
OnRowUpdating="UpdateRow"
And a simplified version of the UpdateRow method in the code behind:
protected void UpdateRow(object sender, GridViewUpdateEventArgs e)
{
var oldValues = e.OldValues;
}
When I run the code above the oldValues variable is set to a System.Collections.Specialized.OrderedDictionary class but the keys and values collections within that have 0 keys / values.
After researching this quite a bit I found a couple posts that say that e.OldValues is not set unless you set the Gridview DataSource to a control like SqlDataSource or ObjectDataSource. I do not want to use these as I like the flexibility it gives me to write my own methods in code behind before hitting the data access layer objects.
Does anyone know how I can get e.OldValues working or provide an alternative so I can look at the original values of the row being updated in the UpdateRow method?
Note
I am able to get the original values of the Gridview DataKey fields but I obviously don't want to set every column to be a data key.
Suppose the GetSortedDepartments() method returns a Datatable. You can put this datatable in the session (for example) and in the UpdateRow method use the Datakey of the the current row to get the old values from this datatable.
Don't forget to update the datatable and to put it again in the session after a row is updated.
I've found a strange behavior with Telerik RadGrid Datasource using Microsoft LINQ To Sql, which I'd like to know what reason causes it.
Suppose I've a basic grid with OnNeedDataSource event:
<telerik:RadGrid ID="grid" runat="server" OnNeedDataSource="grid_NeedDataSource">
...
</telerik:RadGrid>
Event handler in code-behind:
protected void grid_NeedDataSource(object sender, GridNeedDataSourceEventArgs e)
{
grid.DataSource = Dal.Db.VIEW_DETAILs;
}
On the Microsoft Sql Server, VIEW_DETAIL is defined as a view (which select query joins multiple ordinary tables). One of the source tables (let's say it's name is DETAIL) for the view query uses also a computed column that's present also in the view. Querying the view directly from Sql always returns consistent and updated results.
When in my program the source table DETAIL is updated, I call normally
grid.DataBind()
to update its content. Surprisingly nothing is updated (for instance the computed column mentioned above remains the old value). After dealing with some workarounds, I've found that changing the source above on the grid_NeedDataSource handler to
protected void grid_NeedDataSource(object sender, GridNeedDataSourceEventArgs e)
{
grid.DataSource = Dal.Db.VIEW_DETAILs.Where(x=> 1 == 1);
}
(e.g. adding a Where clause that is always true) the data now is correctly updated.
Any insights on this behavior?
Try calling Rebind() instead of DataBind().
According to Telerik, when you need to reassign the datasource of a RadGrid, you should be calling Rebind(). Check the 2nd section Misusing or Not Using NeedDataSource Event on this Telerik page here for more.
ASP.NET [Horizontal scroll fro GridView]
shows how to add horizontal scrolling to my grid
the problem is that it also scrolls around the autogenerated pager.
I CAN make external pager but surely theres a better solution?
the question is: is there an easy way to make only my gridview horizontaly scrollable without making the pager scrollable.
Another solution much simpler:
Create an empty table and give it an id. In my case customPager.
Give your default GridView pager a css style. You just need a name no css coding.
Include jquery in your aspx (head section).
You see the 2 lines of script below... Those move the default paging outside where you put the customPager
This is the simplest that I can produce... hopefully it helps...
<table id="customPager">
</table>
<hr />
<div id="dvGridView" style="height: 200px;overflow:scroll;">
<asp:GridView ID="GridView1" runat="server" AllowPaging="True" PageSize="5">
<PagerSettings Position="Top" />
<PagerStyle CssClass="pagerStyle" />
</asp:GridView>
</div>
<script>
$('#customPager').html($('.pagerStyle').html());
$('.pagerStyle').html('');
</script>
erich007 gave a very intresting answer and thats probably the way to go but I didn;t have time to test it.
the following method worked for me:
quote:
http://blogs.visoftinc.com/2008/03/19/extending-the-gridview-to-work-with-the-new-datapager-control/
Extending the GridView to Work With the New DataPager Control
By Dave Marini | Published: March 19, 2008
Technorati Tags: ASP.NET,C#
Recently I read an article by Scott Mitchell about the new ListView and DataPager controls that are included in the .NET Framework version 3.5. This got me curious as to how to adapt other well known databound controls like the GridView so that they can work together with the DataPager control. After some light reading and rather heavy reflecting over the guts of the GridView control, I had a gameplan and was ready to go. Extending a databound control to allow communication with the DataPager control isn't too difficult to do, but it does assume that you have an understanding of how the data is bound to the control. But why bother to do this when the GridView has its own paging control built in? Well, for one thing, being able to decouple the pager from the grid allows us to do some pretty cool things. One thing I can think of is having the GridView in the left bar and the paging control someplace in the main window. Also, we can have a grid with two pagers. Each keeps track of the current page consistently, regardless of which one we use to page through our data. Finally, the templating controls of the pager are very powerful compared to those of the GridView by itself.
Let's start with how a databound control can communicate with the DataPager. The DataPager control can be hooked up to any control that implements the IPageableItemContainer interface located in the System.Web.Extensions assembly. Here's a quick shot of what the interface looks like courtesy of .NET Reflector:
public interface IPageableItemContainer
{
//Events
event EventHandler<PageEventArgs> TotalRowCountAvailable;
// Methods
void SetPageProperties(int startRowIndex, int maximumRows, bool databind);
// Properties
int MaximumRows { get; }
int StartRowIndex { get; }
}
The MaximumRows and StartRowIndex properties of the interface are simply there to create a way for your databound control to determine the data window to display. This effectively defines the basis for what you will consider to be a "Page" of data. The SetPageProperties method is important in the interaction with the DataPager because this is the method in your control that the DataPager will call whenever you click on any of the DataPagerField controls (The Next or Previous Buttons, for example). Finally, the interface defines an event called TotalRowCountAvailable. This event tells the DataPager how many records are in the data being bound to your control. This is obviously important as the DataPager uses the total row count to determine how man page buttons to render or whether to disable the next or previous buttons so that you don't go to a nonexistent page index.
So let's begin extending the GridView with the hooks for the DataPager. Thinking about the IPageableItemContainer interface in terms of the GridView, we realize that MaximumRows is equivalent to the existing PageSize property, and StartRowIndex can be calculated from the existing PageSize and PageIndex properties. We also prepare the event by declaring it and creating a corresponding event invoker. Since I want my new Paging capability to be the default for this grid, I force the Pager to hide itself every page load. You can add a toggle to this behavior should you so desire. Finally, we stub out the SetPageProperties method, but we'll leave it blank for now as we'll revisit it shortly. So far our new GridView looks like this:
public class PageableGridView : GridView, IPageableItemContainer
{
public PageableGridView() : base()
{
PagerSettings.Visible = false;
}
public event EventHandler<PageEventArgs> TotalRowCountAvailable;
public int MaximumRows
{
get{ return this.PageSize; }
}
public int StartRowIndex
{
get{ return (this.PageSize * this.PageIndex); }
}
protected virtual void OnTotalRowCountAvailable(PageEventArgs e)
{
if (TotalRowCountAvailable != null)
TotalRowCountAvailable(this, e);
}
protected virtual void SetPageProperties(int startRowIndex, int maximumRows, bool dataBind) { }
}
The good news is that we're more than half way there. Now is where things get a bit more complex. We still need a way to set the control with the pagesize and starting row values based on what the DataPager says we need to display once the user clicks on one of its buttons. This is where the SetPageProperties method comes into play. Here is a basic implementation that gets the job done:
protected virtual void SetPageProperties(int startRowIndex, int maximumRows, bool dataBind)
{
if (databind)
{
PageSize = maximumRows;
int newPageIndex = (startRowIndex / PageSize);
if (PageIndex != newPageIndex)
{
OnPageIndexChanging(new GridViewPageEventArgs(newPageIndex));
PageIndex = newPageIndex;
OnPageIndexChanged(EventArgs.Empty);
}
}
RequiresDataBinding = databind;
}
When the DataPager sends the grid its paging information, the grid needs to set any appropriate params on itself to prepare to bind to the right window of data. The 2 properties that the grid is already equipped to do this with are the PageSize and PageIndex properties. These properties can be calculated from the information sent into the method, so we set them. Of course, if the Page is being changed, we should probably fire the OnPageIndexChanging event. This means if you aren't binding to a datasource, you'll still need to make sure to handle this event. Finally, we instruct the grid to rebind itself should the DataPager be in the process of binding to data. This is a basic implementation so you'll want to do any data integrity checks here as well, for example checking to ensure that the new PageIndex and StartRowIndex values are in the valid range.
There's only one thing left to do to complete our integration of the GridView to the DataPager. For the DataPager to render the appropriate number of page buttons, or for it to know when to disable the next or previous page buttons on the pager, it needs to know how many total rows there are. This, coupled with the Page Size, which is specified declaratively on the DataPager itself, help it determine the number of pages that the data source contains. The problem is, this information isn't known to the pager. It is known to the GridView however, and we need to get it from there and pass it on to the DataPager. But when can we be sure that the GridView has this data? The GridView control inherits from CompositeDataboundControl. This type contains a special variant of the CreateChildControls method that also takes in a property indicating whether the control is being bound to data or simply re-rendered. A little reflecting shows that the GridView uses this method to bind to its data source. Knowing this, it appears this is the place we will want to inject our trigger for the TotalRowCountAvailable event to be raised. This gets a little complicated because we need to handle the case where we're binding to a datasource manually or using a DataSource Control like ObjectDataSource or SqlDataSource, which have the total row count specified within them. A couple of helper methods are required to ensure we get the right value:
//Gets row count from SqlDataSource and the like...
private int _GetTotalRowsFromDataSourceObject(IEnumerable dataSource)
{
DataSourceView view = this.GetData(); if (AllowPaging && view.CanPage && view.CanRetrieveTotalRowCount)
return base.SelectArguments.TotalRowCount;
else
return (PageIndex * PageSize) + _GetSourceCount(dataSource);
}
//Gets the row count from a manually bound source or from a source in viewstate
private int _GetSourceCount(IEnumerable dataSource)
{
ICollection source = dataSource as ICollection;
return source != null ?
source.Count :
(from x in dataSource.OfType<object>() select 1).Sum();
}
The _GetTotalRowsFromDataSourceObject method retrieves the total number of records from the DataSource object, if it's available. This depends on a couple of things, such as whether the EnablePaging property was set on the DataSource Control and whether the Object has completed the query operation. Worst case, we return a single page of data and be done with it. The _GetSourceCount method is used for two particular occasions. First, it's how you get the row count in the event that you bind the grid's DataSource property manually and then call DataBind(). Secondly, this method will also prove useful when the grid is rebinding after a postback to data that is stored in the viewstate. In both cases, we use a little linq to extract the total number of resulting data items (or rows in the case of viewstate) in the datasource. Now let's see how we use these methods to tie it all together:
protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
{
int baseResult = base.CreateChildControls(dataSource, dataBinding);
if (dataSource != null)
{
int dataSourceCount = (IsBoundUsingDataSourceID && dataBinding) ?
_GetTotalRowsFromDataSource(dataSource) :
_GetSourceCount(dataSource);
OnTotalRowCountAvailable(new PageEventArgs(StartRowIndex, MaximumRows, dataSourceCount));
}
return baseResult;
}
The base control's CreateChildControls method is called first, since the grid uses it to actually do the databinding. if there isn't any data to bind to, there will not be a reason to notify the pager, so we check to make sure we have data, then determine the number of rows in the source through the above mentioned process. Finally, we fire off the event with the derived data and then we return the original result so as not to affect any other operations that may rely on it. We now have a GridView that can be coupled with the new DataPager control. Here's a sample of markup that we would use to use these controls together on a page:
<asp:DataPager ID="DataPager1" runat="server" PageSize="2" PagedControlID="grid2">
<Fields>
<asp:NextPreviousPagerField />
</Fields>
</asp:DataPager>
<custom:pageablegridview id="grid2" runat="server" autogeneratecolumns="true" allowpaging="true"
onpageindexchanging="grid2_PageIndexChanging" />
Notice that I don't have to specify the PageSize property of the GridView because the PageSize property on the DataPager will be in control of this value. To ensure this, we could shadow the PageSize property on the grid, but this will suffice for now. So that's really all there is to it. For newly created server controls that are bound to data, making these inroads to allow your control to work with the DataPager is pretty simple. For the existing controls, like the GridView, it's just a matter of knowing how the binding takes place and then injecting the functionality someplace where you know the needed values will exist.
I'm using GridView to display data from dataBase, the gridView is bound to objectDataSource.
each row in gridview represent an item that belongs to user and I want to add another field to the gridView that will display the users detail (that actually are in a different table at the dataBase).
I'm not sure what is the best way to do that,
I tried to add to the gridView a TemplateField that contain a DetailView,
and bound it to another objectdatasource but I don't know how to take a parameter from a specific field at the row-(the userID field).
Any suggestions would be welcome...
It's going to be a little trickier than that. You can set the userID as a data key, and in the RowDataBound event of the GridView, bind the detail view.
<asp:GridView ID="GridView1" runat="server" DataKeyNames="UserID" OnRowDataBound="GridView1_RowDataBound">
And in the code behind:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
//get the data key
int userID = (int)GridView1.DataKeys[e.Row.RowIndex]["UserID"];
//get the nested details view control
DetailsView dv = (DetailsView)e.Row.FindControl("DetailsView1");
dv.DataSource = GetUserDetailsTable(userID); //your data source
dv.DataBind();
}
I have a ObjectDataSource which is placed in de source code of my .aspx page, not the code behind, and is used inside an EditTemplate column of a datagrid.
<asp:DropDownList ID="ddlist1" runat="server" DataSourceID="osCreditType" ...
After selecting a value in the dropdown and setting the datagrid state back to ItemTemplate, I have the ValueMember of that DropDownItem in the NewValues collection inside the RowUpdating Event.
protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
int primary = int.Parse(Convert.ToString(e.NewValues[0]));
}
Now, I want to retrieve some other information from the ObjectDataSource using that primary.
Is the data, used for the dropdown still available in the ObjectDataSource, or will a call to that source from code-behind make the datasource go back to the database
Can I use the ObjectDataSource to retrieve additional information using this primary key, and if so, how do I accomplish it ?
Thanks a lot in advance
You have to remember that the ObjectDataSource is really
just a Binder between your Data Layer and Your Controls.
What would be better is for you in the GridView1_RowUpdating routine
is to use a SqlDataAdapter and go and get the information from the database yourself
and then use that information to change the values of the Updated Row.
You will have a problem trying to run another Query with the Same ObjectDataSource,
cause as soon as you do that the control will try to rebind to the new data.
Hope this helps.