gridview rebind does not work as expected

I have a page that has a gridview indside an update panel. This GridViewis bound to a SqlDataSourcewith FilterExpression. I have a RefreshWorkList button. When this button is clicked, I want the gridview to bind so that any new cases that came in the database can show on the page, but it does not happen. I have to click this button twice to see the new cases in the grid. I also have a Asynchronous Postback trigger with controlID = RefreshWorkListButton and EventName = Click for this updatePanel.

 It works fine when RefreshWorkListButton is clicked second time. How can I make it work by just clicking once.

Please check my code and suggest correction.

Thank you.

<asp:ImageButton ID="Internal_ButtonRefreshWorklist" runat="server"
                                    ImageAlign="Left"
                                    ImageUrl="~/Images/Refresh_Mask2.jpg"
                                    OnClick="Internal_Event_RefreshWorklist_Click"
                                    Height="20"                                    
                                    onmouseover='this.src="Images/Refresh_Mask.jpg";'
                                    onmouseout='this.src="Images/Refresh_Mask2.jpg";'
                                    />
...
...

<div class="gridContainer">
                                <asp:UpdatePanel runat="server" ID="Internal_UpdatePanelForWorklist" ChildrenAsTriggers="true"
                                    UpdateMode="Conditional">
                                    <Triggers>
                                        <asp:AsyncPostBackTrigger ControlID="Internal_ButtonSearchQuick" EventName="Click" />
                                        <asp:AsyncPostBackTrigger ControlID="Internal_btnClearQuick" EventName="Click" />
                                         <asp:AsyncPostBackTrigger ControlID="Internal_ButtonRefreshWorklist" EventName="Click" />
                                    </Triggers>
                                    <ContentTemplate>
                                        <asp:GridView ID="Internal_gvCases" runat="server" DataSourceID="Internal_SqlDataSourceGvCases" AutoGenerateColumns="false"
                                            OnRowDataBound="Internal_ShowReportIcons" AutoPostBack="true" AllowSorting="true" CssClass="gridCSS"
                                            AlternatingRowStyle-CssClass="alt" PagerStyle-CssClass="pagerInfo" PagerSettings-Mode="NumericFirstLast"
                                            AllowPaging="true" PageSize="20" PagerSettings-Position="Bottom" PagerStyle-HorizontalAlign="Center"
                                            Width="100%">
                                            <Columns>
..
..
</Columns>
                                            <EmptyDataTemplate>
                                                <div class="emptyData">
                                                    Your search did not return any cases. Modify your search filter(s) and try again.
                                                </div>
                                            </EmptyDataTemplate>
                                        </asp:GridView>
                                        <asp:SqlDataSource ID="Internal_SqlDataSourceGvCases" runat="server" ConnectionString='<%$ ConnectionStrings:APVX %>'
                                            SelectCommand="CSICustom.GetInternalWorklist" SelectCommandType="StoredProcedure"
                                            EnableCaching="true" CacheDuration="Infinite" CacheKeyDependency="WorklistRefreshFromCache2">
                                        </asp:SqlDataSource>
                                    </ContentTemplate>
                                </asp:UpdatePanel>

Open in new window



 protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            if (!this.IsRemoteCSIUser)
            {
                this.Internal_adv_ord_ddl.Enabled = false;
                Cache.Remove(this.Internal_SqlDataSourceGvCases.CacheKeyDependency);
                Cache[this.Internal_SqlDataSourceGvCases.CacheKeyDependency] = new object();
                //Internal_bindGrid(); //evicts cache
            }
            else
            {
                Response.Redirect("Default.aspx");
            }
        }
              
        //set FilterValues for Remote User
        if (!object.ReferenceEquals(ViewState["SaveFilterState"], null))
        {
            SaveFilterState = (Boolean)ViewState["SaveFilterState"];
        }
        if (SaveFilterState)
        {
            this.Internal_SqlDataSourceGvCases.FilterExpression = ViewState["FilterString"].ToString();
            Internal_bindGridWithoutCacheEvict();
        }        
    }

  
    private void Internal_bindGridWithoutCacheEvict()
    {
        Internal_gvCases.DataBind();
    }


 protected void Internal_Event_RefreshWorklist_Click(object sender, EventArgs e)
    {
        SaveFilterState = false;
        //evict the cache
        Cache.Remove(this.Internal_SqlDataSourceGvCases.CacheKeyDependency);
        Cache[this.Internal_SqlDataSourceGvCases.CacheKeyDependency] = new object();
        //Response.Redirect("InternalGrid.aspx");
        //Clear filter controls and filter string
        Internal_clearSearchControls();
        //load grid
        Internal_gvCases.DataBind();     
    }

Open in new window

patd1Asked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Obadiah ChristopherCommented:
Tried this UpdateMode="Always" instead of UpdateMode="Conditional"
patd1Author Commented:
I tried with UpdateMode="Always", but it did not work.

Thanks,
Kelvin McDanielSr. DeveloperCommented:
What is happening is that in your workflow your GridView bind() is occurring before the cache is actually updated.

1. move your "work" logic out of the Page Load event.

2. make sure you're using the "correct" event set, meaning the ones which fire in the order you're expecting.

I'm not in front of a machine right at this second, but I will be in a moment. I'll take a look again and make some suggestions for the code.
Fundamentals of JavaScript

Learn the fundamentals of the popular programming language JavaScript so that you can explore the realm of web development.

Kelvin McDanielSr. DeveloperCommented:
Don't do what I suggested before yet... instead, try adding Internal_UpdatePanelForWorklist.Update() as the last line of Internal_Event_RefreshWorklist_Click().

If that doesn't work I've got something else I think we can try, but it will be a tedious process of trial and error.

The basis problem is that you're using both the WebForm controls and C# code to manage the page workflow. I think you're going to experience "expected performance" issues as long as this is the case. It's not that "you're doing it wrong"... but getting everything to fire in the right order can be tough, and maintenance-wise the page is going to be very brittle.
patd1Author Commented:
Hi azarc, Thanks for trying tohelp me. I tried your suggestion and added Internal_UpdatePanelForWorklist.Update() as the last line of Internal_Event_RefreshWorklist_Click(). e But, the situation is still the same. I ran it in the debugger to see if control goes there. It does, but the UpdatePanel does not show updated on the screen on the first click. However, If I click Refresh button again, the update panle gets refreshed.

Please let me know how it can be handled better.

Thanks.


Kelvin McDanielSr. DeveloperCommented:
WARNING: This is going to be long read. My apologies now.

I figured that would happen because "Always" has the same behavior. :(

--> Response.Rant == "Started";

In my experience I've found that it's best to either let the HTML markup or the codebehind handle the workflow... not but not both. The problem with the HTML markup is that it's very, very difficult to get the order of operations to run as expected 100% of the time, and heaven forbid if you ever have to make changes. The problem with letting the codebehind do all the work is that you have to write much more code than you otherwise would have... including bastardizing the heck out of how the SqlDatasource is supposed to be used, OR writing all your own database calls (select, update, delete). After everything has been built, shoe-horning in either methodology is incredibly tedious. This is the situation you're in right now, btw.

--> Response.Rant == "Finished";  :)

I define "work" code as being the actual manipulation of objects. This could be database calls, enabling or disabling controls, etc.

Per my original comment, I suggest moving all the "work" code out of the Page_Load() event (even though there doesn't appear to be much). Incidently, I also think this is the source of the problem. Why? Because things might not be happening when you think they are.

The lifecycle occurs in this order: (blatant copy/paste from MSDN; full article is here)

1. Page request

The page request occurs before the page life cycle begins. When the page is requested by a user, ASP.NET determines whether the page needs to be parsed and compiled (therefore beginning the life of a page), or whether a cached version of the page can be sent in response without running the page.

2. Start

In the start stage, page properties such as Request and Response are set. At this stage, the page also determines whether the request is a postback or a new request and sets the IsPostBack property. The page also sets the UICulture property.

3. Initialization

During page initialization, controls on the page are available and each control's UniqueID property is set. A master page and themes are also applied to the page if applicable. If the current request is a postback, the postback data has not yet been loaded and control property values have not been restored to the values from view state.

4. Load

During load, if the current request is a postback, control properties are loaded with information recovered from view state and control state.

5. Postback event handling

If the request is a postback, control event handlers are called. After that, the Validate method of all validator controls is called, which sets the IsValid property of individual validator controls and of the page.

6. Rendering

Before rendering, view state is saved for the page and all controls. During the rendering stage, the page calls the Render method for each control, providing a text writer that writes its output to the OutputStream object of the page's Response property.

7. Unload

The Unload event is raised after the page has been fully rendered, sent to the client, and is ready to be discarded. At this point, page properties such as Response and Request are unloaded and cleanup is performed.

See what I mean? Microsoft did a decent job of handling and giving us hooks into a very complex set of operations, things aren't always intuitive. Sometimes just figuring out where to put what is harder and takes longer than actually coding a solution.

Here's a first stab at a recommended solution. Believe it or not, my goal is make the fewest changes possible.

1. Move all work code into appropriately abstracted unit of work methods.
- For example, my codebehind (based on your original sample) would look something like Figure #1.

2. Make the databind where IsPostBack in Page_Load() execute only once.
- As coded, you need this to execute in this fashion the only the first time (as in Figure #2).
- After that all page operations should be the result of Control events, otherwise you'll likely get some unexpected behavior.

Figure #3 is the combination of Figures #1 and #2. Use that one in your code (replacing the appropriate methods and adding in the new ones) and let me know what happens.


// ************
// Figure #1
// ************

protected void Page_Load(object sender, EventArgs e)
{
	if (!IsPostBack)
	{
		if (IsRemoteCSIUser) Response.Redirect("Default.aspx", true); // true says end processing for the rest of the current page.
		EvictCache(false);
	}

	EvaluateFilterState();
}


#region Newly Added And Abstracted Methods

private static void EvictCache(bool? enableControls)
{
	if(enableControls.HasValue) Internal_adv_ord_ddl.Enabled = enableControls.Value;
	Cache.Remove(Internal_SqlDataSourceGvCases.CacheKeyDependency);
	Cache[Internal_SqlDataSourceGvCases.CacheKeyDependency] = new object();
}

private static EvaluateFilterState()
{
	//set FilterValues for Remote User
	if (ViewState["SaveFilterState"] == null) return;
	
	SaveFilterState = (bool)ViewState["SaveFilterState"];
	if (SaveFilterState)
	{
		Internal_SqlDataSourceGvCases.FilterExpression = ViewState["FilterString"].ToString();
		Internal_bindGridWithoutCacheEvict();
	}
}

#endregion Newly Added And Abstracted Methods


#region Modified Original Sample Methods

private void Internal_bindGridWithoutCacheEvict()
{
	Internal_gvCases.DataBind();
}

protected void Internal_Event_RefreshWorklist_Click(object sender, EventArgs e)
{
	SaveFilterState = false;
	
	//evict the cache
	EvictCache(null);
	
	//Response.Redirect("InternalGrid.aspx");
	//Clear filter controls and filter string
	
	Internal_clearSearchControls();
	
	//load grid
	Internal_gvCases.DataBind();     
}

#endregion Modified Original Sample Methods

Open in new window

// ************
// Figure #2
// ************

protected void Page_Load(object sender, EventArgs e)
{
	if (!IsPostBack)
	{
		if (IsRemoteCSIUser) Response.Redirect("Default.aspx", true); // true says end processing for the rest of the current page.
		EvictCache(false);
		EvaluateFilterState();
	}
}

Open in new window

// ************
// Figure #3
// ************

protected void Page_Load(object sender, EventArgs e)
{
	if (!IsPostBack)
	{
		if (IsRemoteCSIUser) Response.Redirect("Default.aspx", true); // true says end processing for the rest of the current page.
		EvictCache(false);
		EvaluateFilterState();
	}
}

#region Newly Added And Abstracted Methods

private static void EvictCache(bool? enableControls)
{
	if(enableControls.HasValue) Internal_adv_ord_ddl.Enabled = enableControls.Value;
	Cache.Remove(Internal_SqlDataSourceGvCases.CacheKeyDependency);
	Cache[Internal_SqlDataSourceGvCases.CacheKeyDependency] = new object();
}

private static EvaluateFilterState()
{
	//set FilterValues for Remote User
	if (ViewState["SaveFilterState"] == null) return;
	
	SaveFilterState = (bool)ViewState["SaveFilterState"];
	if (SaveFilterState)
	{
		Internal_SqlDataSourceGvCases.FilterExpression = ViewState["FilterString"].ToString();
		Internal_bindGridWithoutCacheEvict();
	}
}

#endregion Newly Added And Abstracted Methods


#region Modified Original Sample Methods

private void Internal_bindGridWithoutCacheEvict()
{
	Internal_gvCases.DataBind();
}

protected void Internal_Event_RefreshWorklist_Click(object sender, EventArgs e)
{
	SaveFilterState = false;
	
	//evict the cache
	EvictCache(null);
	
	//Response.Redirect("InternalGrid.aspx");
	//Clear filter controls and filter string
	
	Internal_clearSearchControls();
	
	//load grid
	Internal_gvCases.DataBind();     
}

#endregion Modified Original Sample Methods

Open in new window

patd1Author Commented:
Thank azarc3. I really appreciate your taking time to help me.
I have re-factored the code to implement what you suggested in Fig 3. It has made my code much cleaner and I appreciate your suggestions.

But my problem is still the same. Probably I could not explain it completely, so I will try again.

I first load the grid, then run filtering on certain columns. Filtering event saves my filterstring in viewstate and also sets savefilterstate to true, so that if I do sorting or paging it should not go back to unfiltered state. If I want it to go to unfiltered state, I hit 'Clear' button that clears the filterstring and rebinds the grid without cache evict. All that works beautifully.

Now, if I want to see new cases that came in the mean time, I hit 'Refresh' button. I don't understand why it goes in the page load instead of first going to Internal_Event_RefreshWorklist_Click.
By going to page load at this point, it still has save filter state = true and it therefore it runs the Internal_bindGridWithoutCacheEvict  and loads the same filtered data in the grid.

How do I make the control go to Internal_Event_RefreshWorklist_Click without or before going to page load.

Thank You again.
Kelvin McDanielSr. DeveloperCommented:
Understood; thanks for the explanation.

Unfortunately, it sounds like the Click() event is occurring before the Page_Load(). that's what I meant with the page lifecycle information. The key here is that the Page_Load event is ALWAYS going to fire. ALWAYS. In your case, it sounds like it's always happening after the Click() events fire.

To fix this you're going to have to make it so that the GridView databind isn't dependent upon the Page_Load() event... Rather, it should happen in the Click() events EXCEPT for the very first time the page is loaded and the GridView takes it's default set of records.

You may also want to put breakpoints in the Init() and Load() events of the page, gridview, button, and datasource controls to see just what order things are occurring in.
Kelvin McDanielSr. DeveloperCommented:
Another aspect of the strategy would be to use the Click() events to set ViewState variables that are used by the GridView for databinding. That should reduce the amount of code changes you'll need to make.
patd1Author Commented:
How can I check in my page load event if this ImageButton fired the postback? I tried Page.Request.Params.Get("__EVENTTARGET"), but I always get a null value.

If I know the postback was caused by the Refresh ImageButton, then I can set my viewstate variable to false and then bind the grid with cache evict.

Thank you
Kelvin McDanielSr. DeveloperCommented:
You should be able to get the CommandName and CommandArgument out of the EventArgs (e), but you may have to cast the parameter appropriately.
patd1Author Commented:
I tried e.CommandName in my pageload event, but I am getting the following error:
'System.EventArgs' does not contain a definition for 'CommandName' and no extension method 'CommandName' accepting a first argument of type 'System.EventArgs' could be found (are you missing a using directive or an assembly reference?)
Kelvin McDanielSr. DeveloperCommented:
I'm not in front of a machine so I can't test this... Try setting a breakpoint there and see if it's not in there somewhere. At the very least you'll have to cast the EventArgs to ImageClickEventArgs so that you can get at the ImageButton properties.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Programming Languages-Other

From novice to tech pro — start learning today.