ListView + ObjectDataSource SelectMethod called twice when EnableViewState="false" - asp.net

Note: This question has been completely modified now that I have a simpler example.
I have set up a sample page which only has a ListView and ObjectDataSource. The first time the page comes up (!IsPostBack), my GetList method is called once. After paging (IsPostBack), the GetList method is called twice--the first time with the old paging values and the second time with the new values.
If I set EnableViewState="true" on the ListView, then the GetList method is only called once. It seems to me that the ListView wants an "initial state", which it either gets from ViewState or by re-running the method.
Is there any way to disable ViewState on the ListView and also prevent SelectMethod from being called twice?
ASPX page:
<asp:ListView ID="TestListView" runat="server" DataSourceID="ODS" EnableViewState="false">
<LayoutTemplate>
<asp:PlaceHolder ID="itemPlaceHolder" runat="server" />
<asp:DataPager ID="TestPager" runat="server" PageSize="10">
<Fields>
<asp:NumericPagerField />
</Fields>
</asp:DataPager>
</LayoutTemplate>
<ItemTemplate>
<div><%# Eval("Title") %></div>
</ItemTemplate>
</asp:ListView>
<asp:ObjectDataSource ID="ODS" runat="server" SelectMethod="GetList" SelectCountMethod="GetListCount"
TypeName="Website.Test" EnablePaging="true" />
ASPX code-behind:
namespace Website
{
public partial class Test : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
public IList<DataItem> GetList(int maximumRows, int startRowIndex)
{
return GetListEnumerable().Skip(startRowIndex).Take(maximumRows).ToList();
}
public IEnumerable<DataItem> GetListEnumerable()
{
for (int i = 0; i < 100; i++)
{
yield return new DataItem { Title = i.ToString() };
}
}
public int GetListCount()
{
return 100;
}
}
public class DataItem
{
public string Title { get; set; }
}
}

Either turn ODS caching on.
<asp:ObjectDataSource ID="ODS" ... EnableCaching="true" />
This way the GetList will be called only when new data is needed. Post backs to pages that already had data retrieved will use the cached version and not call the GetList.
Or move your DataPager out of the ListView and set the PagedControlID property.

Actually you should be using the OnSelecting event.
What happens is that ObjectDataSource calls the method SelectMethod twice
First time it gets the data.
Next time it gets the count.
So I think you have to implement the OnSelecting event
<asp:ObjectDataSource ID="ODS" runat="server" SelectMethod="GetList" SelectCountMethod="GetListCount"
OnSelecting="ods_Selecting">
TypeName="Website.Test" EnablePaging="true" />
and then cancel the event when the ObjectDataSource tries to call the count method.
protected void ods_Selecting(object sender,
ObjectDataSourceSelectingEventArgs e)
{
if (e.ExecutingSelectCount)
{
//Cancel the event
return;
}
}
You can look for full implementation as mentioned in the link below
http://www.unboxedsolutions.com/sean/archive/2005/12/28/818.aspx
Hope this helps.

I had a similar problem where it worked different depending on browser. IE one way and all other browsers one way.. Might not be the same issue as you have.
I solved it this way:
protected void DropDownDataBound(object sender, EventArgs e)
{
// Issue with IE - Disable ViewState for IE browsers otherwhise the dropdown will render empty.
DropDownList DDL = (DropDownList)sender;
if (Request.Browser.Browser.Equals("IE", StringComparison.CurrentCultureIgnoreCase))
DDL.ViewStateMode = System.Web.UI.ViewStateMode.Disabled;
else
DDL.ViewStateMode = System.Web.UI.ViewStateMode.Inherit;
}

Related

How to hide link button based on a result returned by a class ?

I am bit new to C# and got a question.
I have a class as below that simply return false ( this is just to test)
public class SetAuthority
{
public SetAuthority()
{
//
// TODO: Add constructor logic here
//
}
public static Boolean AuthorizedToAddEdit()
{
return false;
}
}
I have a DetailsView with two link buttons to Edit and add New record. I want to hide the link buttons based on the above class method returning value.
<ItemTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False" visible='<%# SetAuthority.AuthorizedToAddEdit() %>'
CommandName="Edit" Text="Edit"></asp:LinkButton>
<asp:LinkButton ID="LinkButton2" runat="server" CausesValidation="False" visible='<%# SetAuthority.AuthorizedToAddEdit() %>'
CommandName="New" Text="New"></asp:LinkButton>
</ItemTemplate>
Above works file and Edit and New link buttons are hidden when I run the program.
But the question is, I have a separate link button outside of the DetailsView. It is just a link to navigate to another page. I want to hide this in similar way using the same logic. I have the below code in my webform.
<asp:LinkButton ID="LinkButton5" runat="server" CausesValidation="False" visible='<%# SetAuthority.AuthorizedToAddEdit() %>'
CommandName="OpenAdminPage" Text="Open Admin Page"></asp:LinkButton>
But the link button is always visible and seems it is not calling the class and not getting the value back. It appeared to be the class not return any value and can someone help me to identify what is the different between having this and working in DetailsView and not working for a simple link button.
Note: have a workaround where I can call the same method in Page Load event that works fine without any issue. Code is below
protected void Page_Load(object sender, EventArgs e)
{
Boolean myAllowAdd;
myAllowAdd = SetAuthority.AuthorizedToAddEdit();
if (myAllowAdd == false)
{
LinkButton1.Visible = false;
}
}
The reason is that this is for databinding expressions only: <%# Since the DetailsView is databound it works there.
If you would DataBind the page it worked also for the LinkButton outside of the DetailsView:
protected void Page_Load(object sender, EventArgs e)
{
Page.DataBind();
}
inline asp.net tags... sorting them all out (<%$, <%=, <%, <%#, etc.)
Side-note: be careful with static in ASP.NET. The static method does not yet hurt. But if you'd also use static fields you'd enter a minefield since it would be shared across all requests. Your current code-behind "work-around" is the better approach anyway.

Webforms : Simplest way to allow a user to add/remove textboxes?

I don't want to use a gridview because the rest of the form isn't.
I simply need to be able to create a control to dynamically add/remove textboxes and get the value back either as a list of objects or a comma separated string.. It's proving to be much more difficult than it should.
I tried using Jquery + a regular asp.net textbox, but that only works nicely when they're starting from scratch--prepopulating the DOM with their information becomes a pain.
Is there something painfully simple that I'm missing?
It sounds like you could benefit from creating a CompositeControl.
I recently answered a similar question based on dynamically creating textboxes in which I provided a fairly detailed example.
See: Dynamically add a new text box on button click
Hope this helps.
You can add/remove the input[type=text] elements with jquery, and then use Request.Form in your code behind to get the values by element name.
javascript:
var itemCount = 0;
$("#list .add").click(function(){
itemCount++;
$(this).append("<input type='text' name='item"+itemCount+"'/><button class='remove'>Remove</button>");
});
$("#list .remove").click(function(){
$(this).prev().remove();
});
code behind:
string value1 = Request.Form["item1"];
string value2 = Request.Form["item2"];
There are two ways. The following is made using pure WebForm capabilities. Never do it in the production. It uses too much viewstate and too much updatepanel
this is a code behind
public List<String> ValueContainer {
get {
return (List<String>)ViewState["ValueContainer"];
}
set {
ViewState["ValueContainer"] = value;
}
}
protected void Page_Load(object sender, EventArgs e) {
if (!IsPostBack) {
ValueContainer = new List<string>();
}
}
private void PopulateRepeater() {
rp1.DataSource = ValueContainer;
rp1.DataBind();
}
protected void lbAdd_Click(object sender, EventArgs e) {
ValueContainer.Add("");
rp1.DataSource = ValueContainer;
rp1.DataBind();
}
protected void rp1_ItemCommand(Object Sender, RepeaterCommandEventArgs e) {
ValueContainer.RemoveAt(e.Item.ItemIndex);
rp1.DataSource = ValueContainer;
rp1.DataBind();
}
Here is the markup
<asp:ScriptManager runat="server" ID="sm1" />
<asp:UpdatePanel runat="server" ID="up1">
<ContentTemplate>
<asp:Repeater runat="server" OnItemCommand="rp1_ItemCommand" ID="rp1">
<ItemTemplate>
<asp:TextBox runat="server" ID="myTextBox" /> <asp:LinkButton Text="Remove" runat="server" ID="lbRemove" />
</ItemTemplate>
</asp:Repeater>
<asp:LinkButton runat="server" ID="lbAdd" onclick="lbAdd_Click" Text="Add" />
</ContentTemplate>
</asp:UpdatePanel>
This is more lightweight version
<asp:HiddenField runat="server" ID="hfMyField" ClientIDMode="Static" />
<script type="text/javascript">
//<![CDATA[
function addTextBox() {
$("#myTextboxesContainer").append($("<input type='text' />").keyup(function () {
var Data = "";
$("#myTextboxesContainer input").each(function () {
Data += $(this).val() + ",";
});
$("#hfMyField").val(Data);
}));
}
//]]>
</script>
<div id="myTextboxesContainer">
</div>
Add textbox
The idea here is doing all dom manipulations using client script and storing everything in a hidden field. When the data is posted back you can retrive the value of the hidden field in a standard way i.e. hfMyField.Value. In this example it is CSV.

ASP.NET - Control Events Not Firing Inside Repeater

This is a absurdly common issue and having exhausted all of the obvious solutions, I'm hoping SO can offer me some input... I have a UserControl inside a page which contains a repeater housing several controls that cause postback. Trouble is, all of the controls inside of the repeater never hit their event handlers when they postback, but controls outside of the repeater (still in the UC) are correctly handled. I already made sure my controls weren't being regenerated due to a missing if(!IsPostBack) and I verified that Request.Form["__EVENTTARGET"] contained the correct control ID in the Page_Load event. I attempted to reproduce the symptoms in a separate project and it worked as it should.
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="NoteListControl.ascx.cs"
Inherits="SantekGBS.Web.UserControls.NoteListControl" %>
<asp:UpdatePanel ID="upNotes" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<div class="NoteList" id="divNoteList" runat="server">
<asp:Repeater ID="repNotes" runat="server">
<HeaderTemplate>
<table width="98%" cellpadding="3" cellspacing="0">
</HeaderTemplate>
<ItemTemplate>
<tr class="repeaterItemRow">
<asp:ImageButton ID="ImageButton1" runat="server" ImageUrl="~/Content/images/DeleteIcon.gif"
OnClick="ibRemove_Click" CommandArgument='<%# Container.ItemIndex %>' CommandName='<%# Eval("ID") %>'
CausesValidation="false" AlternateText="Delete" />
<%# Eval("Text") %></td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
<asp:PlaceHolder ID="phNoNotes" runat="server" Visible="false">
<div class="statusMesssage">
No notes to display.
</div>
</asp:PlaceHolder>
</div>
</ContentTemplate>
</asp:UpdatePanel>
public partial class NoteListControl : UserControl
{
[Ninject.Inject]
public IUserManager UserManager { get; set; }
protected List<Note> Notes
{
get
{
if (ViewState["NoteList"] != null)
return (List<Note>)ViewState["NoteList"];
return null;
}
set { ViewState["NoteList"] = value; }
}
public event EventHandler<NoteEventArgs> NoteAdded;
public event EventHandler<NoteEventArgs> NoteDeleted;
public event EventHandler<NoteEventArgs> NoteChanged;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
UtilityManager.FillPriorityListControl(ddlPriority, false);
}
}
protected void ibRemove_Click(object sender, ImageClickEventArgs e)
{
System.Diagnostics.Debug.WriteLine("ibRemove POSTBACK"); // This is NEVER hit
}
public void Fill(List<Note> notes)
{
Notes = notes;
RefreshRepeater();
}
private void RefreshRepeater()
{
if (Notes != null && Notes.Any())
{
var sorted = Notes.OrderByDescending(n => n.Timestamp);
Notes = new List<Note>();
Notes.AddRange(sorted);
repNotes.Visible = true;
phNoNotes.Visible = false;
repNotes.DataSource = Notes;
repNotes.DataBind();
}
else
{
repNotes.Visible = false;
phNoNotes.Visible = true;
}
}
}
public class NoteEventArgs : EventArgs
{
public Note Note { get; set; }
public NoteEventArgs()
{ }
public NoteEventArgs(Note note)
{
this.Note = note;
}
}
The code is intentionally missing functionality so just disregard that fact.
Your edited code has residual CommandArgument and CommandName properties; are you actually handling the Repeater.ItemCommand event?
If so, and if your page calls the control's Fill method on postbacks, that would explain it.
This classic ASP.NET hair-tearing problem is explained in these posts: A Stumper of an ASP.NET Question and A Stumper of an ASP.NET Question: SOLVED!
The explanation is a little mind-bending, but the crux of it is that Repeater.DataBind interferes with ASP.NET's ability to determine which repeater button caused a postback.
I found a missing td-tag in the Itemtemplate, sometimes when DOM is incorrect, the updatapanel do strange things.
Just about EVERY time I run into this problem it's because DataBind() is being called when it shouldn't be. This will kill most events from controls inside a repeater. I see you have an !IsPostBack check in your Page_Load... so that's a start. But try putting a breakpoint on repNotes.DataBind() and see if it's getting called when you don't expect it.
Does it work OK outside of an UpdatePanel?
I ran into the same problem. It happened with me if I've ran the DataBind twice. In other words when I populate the repeater control twice (for any reason) the events wont fire.
I hope that helps.

ASP.NET DataGrid and custom paging

I'm trying to implement a DataGrid in ASP.NET, and want to achieve custom paging so that I don't have to provide all the data in one go. I've spent several hours researching on the internet, but haven't found anything useful.
When I view the page I see the first set of results in the grid, with the previous link disabled. When I click next however, I once again see the first page of the grid with the previous link disabled. When debugging the code I ascertained that the MyGrid_PageIndexChanged() event handler is never called.
I've included my simplified code below. I've changed variable names and omited methods to focus on the datagrid paging issue.
In the ASPX file:
<asp:DataGrid ID="myGrid" runat="server" GridLines="None" UseAccessibleHeader="true" AutoGenerateColumns="false" AllowPaging="true" AllowCustomPaging="true" PageIndexChanged="MyGrid_PageIndexChanged">
<PagerStyle Mode="NextPrev" NextPageText="Next >" PrevPageText="< Previous" />
<Columns>
<asp:BoundColumn HeaderText="Title" DataField="Name" />
<asp:BoundColumn HeaderText="Date" DataField="Date" />
</Columns>
</asp:DataGrid>
And in the CS file:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
myGrid.PageSize = 20;
myGrid.VirtualItemCount = GetNumItems();
BindMyGrid();
}
}
protected void MyGrid_PageIndexChanged(object sender, DataGridPageChangedEventArgs e)
{
myGrid.CurrentPageIndex = e.NewPageIndex;
BindMyGrid();
}
private int GetNumItems()
{
return 500;
}
private void BindMyGrid()
{
Data[] array = GetDataFromInternetSomehow();
this.myGrid.DataSource = array;
this.myGrid.DataBind();
}
private class Data
{
public string Date { get; set; }
public string Name { get; set; }
}
Any thoughts on this would be much appreciated.
There is an error in your ASPX: to wire up the PageIndexChanged event handler use the property OnPageIndexChanged (not PageIndexChanged as in your code):
<asp:DataGrid ID="myGrid" runat="server"
OnPageIndexChanged="MyGrid_PageIndexChanged" /// <--- here's the error
...
Then, if you have AllowCustomPaging="true", you must ensure that the GetDataFromInternetSomehow() method will only return the data for the currently selected page, e.g. pass the current page to the method and return only the corresponding data:
GetDataFromInternetSomehow(e.NewPageIndex);
Otherwise, disable custom paging and it will just work (but all data will be loaded everytime).

Datalist placed inside a usercontrol

I have a usercontrol.Inside usercontrol I have a datalist and sqldatasource.Sqldatasource needs a parameter to databind the datalist.Usercontrol gets a parameter by this way,
private string _urunIDparam;
public string urunIDparam
{
get { return _urunIDparam; }
set {_urunIDparam = value; }
}
And then this parameter is added to the sqldatasource in onprerender of usercontrol by this way,
protected override void OnPreRender(EventArgs e)
{
SqlDataSourceHareketler.SelectParameters["urunID"].DefaultValue = urunIDparam;
DataListHareketAna.DataBind();
base.OnPreRender(e);
}
And the usercontrols parameter is given from a button that is placed aspx page like this,
protected void Button1_Click(object sender, EventArgs e)
{
MyUserControl1.urunIDPARAM = urunID;
}
And button and usercontrol is placed in an updatepanel inside an aspx page like this,
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<uc2:MyUserControl ID="MyUserControl1" runat="server" />
<asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click"/>
</ContentTemplate>
</asp:UpdatePanel>
And the problem when I hit a button inside datalist, my usercontrol disappears (datalist loses its items).
It sounds like you may need to do a postback check around your functions within the controls. I have seen this exact issue before, and I think this is what I had to do, but it has been awhile.
if (!IsPostback) {
//LoadData
}

Resources