开发者

Asp.Net single control render for AJAX calls

开发者 https://www.devze.com 2023-01-05 10:40 出处:网络
I\'m trying to implement something similar to this or this. I\'ve created a user control, a web service and a web method to return the rendered html of the control, executing the ajax calls via jQuer

I'm trying to implement something similar to this or this.

I've created a user control, a web service and a web method to return the rendered html of the control, executing the ajax calls via jQuery.

All works fine, but if I put something in the user control that uses a relative path (in my case an HyperLink with NavigateUrl="~/mypage.aspx") the resolution of relative path fails in my developing server.

I'm expecting: http://localhost:999/M开发者_如何学运维yApp/mypage.aspx

But I get: http://localhost:999/mypage.aspx

Missing 'MyApp'...

I think the problem is on the creation of the Page used to load the control:

Page page = new Page();
Control control = page.LoadControl(userControlVirtualPath);
page.Controls.Add(control);
...

But I can't figure out why....

EDIT Just for clarity

My user control is located at ~/ascx/mycontrol.ascx and contains a really simple structure: by now just an hyperlink with NavigateUrl like "~/mypage.aspx". And "mypage.aspx" really resides on the root.

Then I've made up a web service to return to ajax the partial rendered control:

[ScriptService]
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class wsAsynch : System.Web.Services.WebService
{
    [WebMethod(EnableSession = true)]
    public string GetControl(int parma1, int param2)
    {
        /* ...do some stuff with params... */
        Page pageHolder = new Page();

        UserControl viewControl = (UserControl)pageHolder.LoadControl("~/ascx/mycontrol.ascx");
        Type viewControlType = viewControl.GetType();

        /* ...set control properties with reflection... */

        pageHolder.Controls.Add(viewControl);
        StringWriter output = new StringWriter();
        HttpContext.Current.Server.Execute(pageHolder, output, false);

        return output.ToString();
    }
}

The html is correctly rendered, but the relative path in the NavigateUrl of hyperlink is incorrectly resolved, because when I execute the project from developing server of VS2008, the root of my application is

http://localhost:999/MyApp/

and it's fine, but the NavigateUrl is resolved as

http://localhost:999/mypage.aspx

losing /MyApp/ . Of Course if I put my ascx in a real page, instead of the pageHolder instance used in the ws, all works fine.

Another strange thing is that if I set the hl.NavigateUrl = Page.ResolveUrl("~/mypage.aspx") I get the correct url of the page: http://localhost:999/MyApp/mypage.aspx

And by now I'll do that, but I would understand WHY it doesn't work in the normal way. Any idea?


The problem is that the Page-class is not intented for instantiating just like that. If we fire up Reflector we'll quickly see that the Asp.Net internals sets an important property after instantiating a Page class an returning it as a IHttpHandler. You would have to set AppRelativeTemplateSourceDirectory. This is a property that exists on the Control class and internally it sets the TemplateControlVirtualDirectory property which is used by for instance HyperLink to resolve the correct url for "~" in a link.

Its important that you set this value before calling the LoadControl method, since the value of AppRelativeTemplateSourceDirectory is passed on to the controls created by your "master" control.

How to obtain the correct value to set on your property? Use the static AppDomainAppVirtualPath on the HttpRuntime class. Soo, to sum it up... this should work;

[WebMethod(EnableSession = true)]
public string GetControl(int parma1, int param2)
{
    /* ...do some stuff with params... */
    var pageHolder = new Page() { AppRelativeTemplateSourceDirectory = HttpRuntime.AppDomainAppVirtualPath };

    var viewControl = (UserControl)pageHolder.LoadControl("~/ascx/mycontrol.ascx");
    var viewControlType = viewControl.GetType();

    /* ...set control properties with reflection... */

    pageHolder.Controls.Add(viewControl);
    var output = new StringWriter();
    HttpContext.Current.Server.Execute(pageHolder, output, false);

    return output.ToString();
}


The tildy pust the path in the root of the app, so its going to produce a the results you are seeing. You will want to use:

NavigateUrl="./whatever.aspx"

EDIT:
Here is a link that may also prove helpful...http://msdn.microsoft.com/en-us/library/ms178116.aspx


I find the /MyApp/ root causes all sorts of issues. It doesn't really answer your question 'why is doesn't work the normal way', but do you realize you can get rid of the /MyApp/ and host your website at http:/localhost/...?

Just set Virtual Path in the website properties to '/'.

This clears everything up, unless of course you are trying to host multiple apps on the development PC at the same time.


It might be that the new page object does not have "MyApp" as root, so it is resolved to the server root as default.

My question is rather why it works with Page.ResolveUrl(...).
Maybe ResolveUrl does some more investigation about the location of the usercontrol, and resolves based on that.


Weird, I recreated the example. The hyperlink renders as <a id="ctl00_hlRawr" href="Default.aspx"></a> for a given navigation url of ~/Default.aspx. My guess is that it has something to do with the RequestMethod. On a regular page it is "GET" but on a webservice call it is a "POST".

I was unable to recreate your results with hl.NavigateUrl = Page.ResolveUrl("~/mypage.aspx") The control always rendered as <a id="ctl00_hlRawr" href="Default.aspx"></a> given a virtual path. (Page.ResolveUrl gives me "~/Default.aspx")

I would suggest doing something like this to avoid the trouble in the future.

protected void Page_Load(object sender, EventArgs e)
{
    hlRawr.NavigateUrl = FullyQualifiedApplicationPath + "/Default.aspx";
}

public static string FullyQualifiedApplicationPath
{
    get
    {
        //Return variable declaration
        string appPath = null;

        //Getting the current context of HTTP request
        HttpContext context = HttpContext.Current;

        //Checking the current context content
        if (context != null)
        {
            //Formatting the fully qualified website url/name
            appPath = string.Format("{0}://{1}{2}{3}",
            context.Request.Url.Scheme,
            context.Request.Url.Host,
            (context.Request.Url.Port == 80 ? string.Empty : ":" + context.Request.Url.Port),
            context.Request.ApplicationPath);
        }

        return appPath;
    }
}

Regards,


It is hard to tell what you are trying to achieve without posting the line that actually sets the Url on of the HyperLink, but I think I understand your directory structure.

However, I have never run into a situation that couldn't be solved one way or another with the ResolveUrl() method. String parsing for a temporary path that won't be used in production is not recommended because it will add more complexity to your project.

This code will resolve in any object that inherits from page (including a usercontrol):

Page page = (Page)Context.Handler;
string Url = page.ResolveUrl("~/Anything.aspx");

Another thing you could try is something like this:

Me.Parent.ResolveUrl("~/Anything.aspx");

If these aren't working, you may want to check your IIS settings to make sure your site is configured as an application.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号