views:

514

answers:

5

Hi,

I have a user control which contains the following code:

<form id="CurrencyForm" method="post" runat="server">    
    Display prices in&nbsp;
    <asp:DropDownList ID="currency" runat="server" AutoPostBack="true">
        <asp:ListItem Value="GBP">GBP (£)</asp:ListItem>
        <asp:ListItem Value="USD">USD ($)</asp:ListItem>
        <asp:ListItem Value="EUR">EUR (€)</asp:ListItem>
    </asp:DropDownList>
</form>

This user control is included in my Master page so it is available on every page of the site.

Here is the codebehind:

protected void page_load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        currency.SelectedValue = (string)Session["currency"];
    }
    else
    {
        session["currency"] = currency.SelectedValue;
        Response.Redirect(Request.RawUrl);
    }
}

When I change the selection of the drop down list, a postback is triggered. However, the value of IsPostBack seems to always be false. I can't quite put my finger on what I am doing wrong here?

Solution

After looking at the advice from the majority of posts I understand I was going wrong by using code_behind code etc instead of the controllers. I figured out how to use an action to basically redirect back to the same page so the following is now what I have:

User control:

<% using (Html.BeginForm("ChangeCurrency", "Home", FormMethod.Post)) {%>    
    Display prices in&nbsp;
    <select id="currency" name="currency">
        <option value="GBP">GBP (£)</option>
        <option value="USD">USD ($)</option>
        <option value="GBP">EUR (€)</option>
    </select>
    <input type="submit" value="change" /> // this is temporary to test the submit
<%} %>

Action

public ActionResult ChangeCurrency(string currency)
{
    Session["currency"] = currency;
    return new RedirectResult(Request.UrlReferrer.AbsoluteUri);
}
A: 

Your code is checking Page.IsCallback rather than Page.IsPostback.

Dr. Wily's Apprentice
Oops apologies that was a typo...
James
Is this MVC or webForms? It looks very much like webforms.
Simon Hazelton
@Simon: It is an MVC2 project.
James
+5  A: 

MVC never does postbacks. Therefore, IsPostBack will always, correctly, be false. The thing you think is a postback is actually a POST, because MVC doesn't do postbacks. You should not be using the ASP.NET server controls at all in MVC.

Craig Stuntz
@craig: Ah ok that will be the problem then! So how would I achieve this in MVC? Basically I want to just reset a session variable and refresh the current page. I tried doing this through actions but couldn't quite figure out how to do it?
James
This SO link should help.http://stackoverflow.com/questions/263336/asp-net-mvc-radio-button-state
kenny
@James I would use an asynchronous post for this, since you just want to pass information to the server without taking the user away from the page. Use `Ajax.BeginForm` instead of `Html.BeginForm` and return Json instead of a redirect.
Craig Stuntz
@Craig: Depending on what page I am on (as remember this is on my master page) I may have other user controls that need to reload in order to use the updated currency. I thought it would be easier to simply refresh the page. Is there a better alternative?
James
Yes. Just return the data you need in a JSON response and update the controls with JavaScript (alternately: return a partial HTML view and update part of the page). That way your page doesn't give the user obnoxious warnings if they dare use the back button later on, as it would with ASP.NET and postbacks.
Craig Stuntz
A: 

If this is MVC, it's setup entirely wrong. MVC eliminates the use for postbacks. You should be using a WebForms project if you need this logic.

Scott
@Scott: See my comment on @Craigs post, how would I achieve this in MVC then? The action requires a return view, but I just want to refresh the current page.
James
+1  A: 

I should note that this did used to work in ASP.NET MVC 1. But we made a change in ASP.NET MVC 2 that prevents this from working.

One way or another, the other commenters here are correct: You shouldn't have logic like this in the view because this logic belongs in the controller.

Eilon
@Eilon: I do understand that the code should be in the controller (as I had it there originally). However, in my case all I want to do is change a session variable and refresh the current page. How can I do that through an action. I can change the session variable fine, its the refreshing the page I can't figure out. As actions require you to return a view.
James
+1  A: 

1) lose the code-behind - in MVC "views" (i.e. .aspx or .ascx files) are not meant to contain any codeblocks, just markup and short snippets of code to call properties of the model etc. Also ditch your "runat=server" attributes, and add "name" attributes to all the input elements you're going to post back to the server.

2) set the "action" attribute of the form to a URL on one of your controllers which will handle the post

3) add an action to your controller corresponding to the URL you set above. In it check Request.Form["currency"] to see what value was selected, and set the session variable based on this.

4) from this action, you can return a RedirectResult (? name may be a bit wrong) specifying the original page URL; this will tell the browser to do a "GET" request for the original page after posting the data. (this is known as the post, redirect, get pattern which MVC allows you to make use of, it can be very effective).

5) this won't in itself give you the "auto post on change of value" functionality you were after with autopostback, so I'd add a jQuery script to submit the form on change of value in the drop-down. Something like


$("#currency").change(function(){ $("#CurrencyForm").submit(); });

(which will require you to add a script reference to jQuery if you don't already have one).

I hope that doesn't sound like too much work! Trust me, try it out, embrace MVC and you'll never look back!

Tom Carver
@Tom: Thanks for your answer. I am no stranger to MVC so I understand the post/redirect/get pattern etc I just can't seem to quite get how something as simple as wanting to change a session variable and refresh the current page is quite tedious in MVC! So from inside the action, how do I get the current url? As Request.RawUrl will now be the Url of my action not the page I was on...
James
@James, to avoid hard-coding the URL you can return a "RedirectToAction", specifying just the action and/or controller of the URL you want to send the user to. This does the same as a RedirectResult but the framework generates the exact URL for you.
Tom Carver
@Tom: But which action would you know to return? I could be anywhere on the site. See my updated post it seems to work fine :)
James
@James I see what you mean, I missed that it was a UserControl used in a variety of different locations. You're right, your UrlReferrer should work fine in your scenario, if it ever got more complex you could consider setting the current URL in a hidden field in the form, that would also handle scenarios where (conceivably) you want to redirect the user to a different page/specify a query string on return etc.
Tom Carver