Get Current System.Web.Ui.Page from Httpcontext

Get current System.Web.UI.Page from HttpContext?

No, from MSDN on HttpContext.Current: "Gets or sets the HttpContext object for the current HTTP request."

In other words it is an HttpContext object, not a Page.

You can get to the Page object via HttpContext using:

Page page = HttpContext.Current.Handler as Page;

if (page != null)
{
// Use page instance.
}

HttpContext from System.Web.UI.Page?

you can access the current context from anywhere with

HttpContext.Current

HttpContext.Current vs System.Web.UI.Page.Context

If you have choice it is always better to avoid using global variables/properties of any kind in favor of more local versions to make code more self-documenting (explicit dependencies are easier to see) and testable (it is easier to pass mock/custom object than trying to set up potentially non-writable global property).

In case of HttpRequest all ways you've mentioned will give you the same object. The difference is if you pass one in to your methods than you can test code much easier compared to using HttpContext.Current.

Getting URL of current page

There is a full set of examples for Request.Url at this site
To isolate the querystring you can do:
Request.Url.GetComponents(UriComponents.Query, UriFormat.SafeUnescaped)
Or
Request.QueryString which is my assumption of what you're trying to do from your own code.

Finding Page or Page assembly from current context

When you do it this way:

private readonly Type _t = Business.GetPageType();

it is actually compiled into a field initialization within the constructor.
It means that the object (your page) is not constructed yet. It does not exist yet, it is "being born". You are just within the constructor at this stage.

Until your object (the page) is constructed, ASP.NET infrastructure cannot assign it to an HttpContext.Current.CurrentHandler static property. Well, because the handler (your page) does not exist yet and id being constructed.

So you cannot do what you want.

What you can do is to create a PageBase class, override OnInit method and add this code there:

public abstract class PageBase
{
protected Type PageType { get; private set; }

protected override void OnInit(EventArgs e)
{
PageType = Business.GetPageType();
}
}

and now just derive your pages from this base class:

public partial class FrontEnd: PageBase { .... }

(or specify PageBase as a base class directly in ASPX file, whatever you do.

How could I access the ViewState of the current page using HttpContext?

    private static T GetViewState<T>(string name)
{
return (T) ((BasePage)HttpContext.Current.CurrentHandler).PageViewState[name];
}

I added a new PageViewState property and let all my pages inherit from my BasePage to expose ViewState then being able to get or set it.

Using System.Web.UI.Page.ParseControl() outside of an ASP.NET project

The exception is actually coming from the internal class System.Web.VirtualPath:

// Default Create method
public static VirtualPath Create(string virtualPath) {
return Create(virtualPath, VirtualPathOptions.AllowAllPath);
}

...

public static VirtualPath Create(string virtualPath, VirtualPathOptions options) {
...

// If it's empty, check whether we allow it
if (String.IsNullOrEmpty(virtualPath)) {
if ((options & VirtualPathOptions.AllowNull) != 0) // <- nope
return null;

throw new ArgumentNullException("virtualPath"); // <- source of exception
}

...
}

System.Web.UI.Page inherits ParseControl() from System.Web.UI.TemplateControl. So you are ultimately calling...

public Control ParseControl(string content) {
return ParseControl(content, true);
}

public Control ParseControl(string content, bool ignoreParserFilter) {
return TemplateParser.ParseControl(content, VirtualPath.Create(AppRelativeVirtualPath), ignoreParserFilter);
}

For reference (from VirtualPathOptions):

internal enum VirtualPathOptions
{
AllowNull = 1,
EnsureTrailingSlash = 2,
AllowAbsolutePath = 4,
AllowAppRelativePath = 8,
AllowRelativePath = 16,
FailIfMalformed = 32,
AllowAllPath = AllowRelativePath | AllowAppRelativePath | AllowAbsolutePath,
}

Since VirtualPathOptions.AllowAllPath is passed to VirtualPath.Create()...

return Create(virtualPath, VirtualPathOptions.AllowAllPath);

this...

options & VirtualPathOptions.AllowNull

... evaluates to 0, and an ArgumentNullException will be thrown


Please consider the following example.

Default.aspx:

<%@ Page Title="Home Page" Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebFormsTestBed._Default" %>

<html>
<head>
<title></title>
</head>
<body>
<form id="formMain" runat="server">
<asp:Label ID="lblResults" runat="server"></asp:Label>
</form>
</body>
</html>

Default.aspx.cs:

using System;
using System.Web;
using System.Web.UI;

namespace WebFormsTestBed {
public partial class _Default : Page {
protected void Page_Load(object sender, EventArgs e) {
Control ctl;
var page = HttpContext.Current.Handler as Page;

// First, using `HttpContext.Current.Handler as Page`,
// - already has `AppRelativeVirtualPath` set to `~\Default.aspx`
if (page != null) {
ctl = page.ParseControl(@"<asp:TextBox ID=""txtFromCurrentHandler"" runat=""server"" Text=""Generated from `HttpContext.Current.Handler`""></asp:TextBox>");

if (ctl != null) lblResults.Text = "Successfully generated control from `HttpContext.Current.Handler`";
}

// Next, using `new Page()`, setting `AppRelativeVirtualPath`
// - set `AppRelativeVirtualPath` to `~\`
var tmpPage = new Page() {
AppRelativeVirtualPath = "~\\"
};

ctl = tmpPage.ParseControl(@"<asp:TextBox ID=""txtFromNewPageWithAppRelativeVirtualPathSet"" runat=""server"" Text=""Generated from `new Page()` with `AppRelativeVirtualPath` set""></asp:TextBox>", true);

if (ctl != null)
lblResults.Text +=
string.Format("{0}Successfully generated control from `new Page()` with `AppRelativeVirtualPath` set",
lblResults.Text.Length > 0 ? "<br/>" : "");

// Last, using `new Page()`, without setting `AppRelativeVirtualPath`
try {
ctl = new Page().ParseControl(@"<asp:TextBox ID=""txtFromNewPageWithoutAppRelativeVirtualPathSet"" runat=""server"" Text=""Generated from `new Page()` without `AppRelativeVirtualPath` set""></asp:TextBox>", true);

if (ctl != null)
lblResults.Text +=
string.Format("{0}Successfully generated control from `new Page()` without `AppRelativeVirtualPath` set",
lblResults.Text.Length > 0 ? "<br/>" : "");
} catch (ArgumentNullException) {
lblResults.Text +=
string.Format("{0}Failed to generate control from `new Page()` without `AppRelativeVirtualPath` set",
lblResults.Text.Length > 0 ? "<br/>" : "");
}
}
}
}

You can read about this line...

var page = HttpContext.Current.Handler as Page;

at this here.


Result:


Successfully generated control from `HttpContext.Current.Handler`
Successfully generated control from `new Page()` with `AppRelativeVirtualPath`
Failed to generate control from `new Page()` without `AppRelativeVirtualPath` set

Example usage from WebForms project

This hack is based on this SO answer, which is based on attaching a non-WebForms test harness to a WebForms application.

Starting with the WebForms project that would have been created for the example above, add a new WinForms project.

For the simplest case, we will just modifying Program.cs:

using System;
using System.IO;
using System.Linq;
using System.Web.Hosting;
using System.Windows.Forms;
using System.Web.UI;

namespace WinFormsTestBed {
public class AppDomainUnveiler : MarshalByRefObject {
public AppDomain GetAppDomain() {
return AppDomain.CurrentDomain;
}
}

internal static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
private static void Main() {
var appDomain = ((AppDomainUnveiler)ApplicationHost.CreateApplicationHost(
typeof(AppDomainUnveiler), "/", Path.GetFullPath("../../../WebFormsTestBed")))
.GetAppDomain();

try {
appDomain.DoCallBack(StartApp);
} catch (ArgumentNullException ex) {
MessageBox.Show(ex.Message);
} finally {
AppDomain.Unload(appDomain);
}
}

private static void StartApp() {
var tmpPage = new Page() {
AppRelativeVirtualPath = "~/Default.aspx"
};
var ctl = tmpPage.ParseControl(@"<asp:TextBox ID=""txtFromNewPageWithAppRelativeVirtualPathSet"" runat=""server"" Text=""Generated from `new Page()` with `AppRelativeVirtualPath` set""></asp:TextBox>");

ctl = ctl == null ||
(ctl = ctl.Controls.OfType<System.Web.UI.WebControls.TextBox>().FirstOrDefault()) == null
? null
: ctl;

MessageBox.Show(ctl == null ? "Failed to generate asp:TextBox" : "Generated asp:TextBox with ID = " + ctl.ID);
}
}
}

You will need to add a reference to System.Web to the WinForms project and make the WebForms project dependent on the WinForms project (this dependency isn't technically necessary, I'll explain below).

You will end up with the following:

Sample Image
Sample Image

Create a post-build event in the WinForms project, which will copy the the WinForms output to the WebForms /bin.

xcopy /y "$(ProjectDir)$(OutDir)*.*" "$(ProjectDir)..\WebFormsTestBed\bin\"

Set the WinForms project as the startup project and run it. If you set everything up correctly you should see:

Sample Image

What this does is creates an AppDomain, which is based on the WebForms project, but in the execution context of the WinForms project, and it provides a method for firing a callback method from the WinForms project within the scope of the newly created AppDomain. This will allow you to handle VirtualPath issues correctly within the WebForms project without worrying about the details of mocking up path variables and such.

When the AppDomain is created, it needs to be able to find all resources in its path, which is why the post-build event was created to copy compiled WinForms files to the WebForms /bin folder. This is why the "dependency" is set up from the WebForms project to the WinForms project in the image above.

In the end I don't know how helpful that will be for you. There might be a way to get this all into a single project, or two projects. I'm not going to spend any more time on this without more detail as to why or how you would be using this.

NOTE: the ctl returned from ParseControl() is now a wrapper, with a Controls collection that actually contains the asp:TextBox - I haven't bothered to figure out why yet


Another Alternative

Instead of keeping a dummy WebForms project around, you could try mocking up the AppDomain entirely, so that setting the AppRelativeVirtualPath on the new Page() does not result in...

System.Web.HttpException The application relative virtual path '~/' cannot be made absolute, because the path to the application is not known.

To go about doing this you'll probably want to start by referring back to the source used by the SO answer that I cited above. The SO answer I cited is actually a workaround for this method, which is why I suggested that first, but it requires a valid WebForms project on the same host as the WinForms project.



Related Topics



Leave a reply



Submit