我只想创建一个动态解析控件的测试应用程序。我添加了new Page().ParseControl
。我到了,
System.ArgumentNullException
Value cannot be null.
Parameter name: virtualPath
at System.Web.VirtualPath.Create(String virtualPath, VirtualPathOptions options)
at System.Web.UI.TemplateControl.ParseControl(String content)
还尝试了BuildManager.CreateInstanceFromVirtualPath
,但它抛出了null异常。
答案 0 :(得分:2)
该异常实际上来自内部类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
从ParseControl()
继承System.Web.UI.TemplateControl
。所以你最终打电话......
public Control ParseControl(string content) {
return ParseControl(content, true);
}
public Control ParseControl(string content, bool ignoreParserFilter) {
return TemplateParser.ParseControl(content, VirtualPath.Create(AppRelativeVirtualPath), ignoreParserFilter);
}
供参考(来自VirtualPathOptions
):
internal enum VirtualPathOptions
{
AllowNull = 1,
EnsureTrailingSlash = 2,
AllowAbsolutePath = 4,
AllowAppRelativePath = 8,
AllowRelativePath = 16,
FailIfMalformed = 32,
AllowAllPath = AllowRelativePath | AllowAppRelativePath | AllowAbsolutePath,
}
自VirtualPathOptions.AllowAllPath
传递给VirtualPath.Create()
...
return Create(virtualPath, VirtualPathOptions.AllowAllPath);
此...
options & VirtualPathOptions.AllowNull
...评估为0
,并且会抛出ArgumentNullException
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/>" : "");
}
}
}
}
你可以阅读这一行...
var page = HttpContext.Current.Handler as Page;
此here。
结果:
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
此hack基于this SO answer,它基于将非WebForms测试工具附加到WebForms应用程序。
从为上面的示例创建的WebForms项目开始,添加一个新的WinForms项目。
对于最简单的情况,我们只修改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);
}
}
}
您需要向WinForms项目添加对System.Web
的引用,并使WebForms项目依赖于WinForms项目(这种依赖性在技术上是不必要的,我将在下面解释)。
您最终会得到以下结果:
在WinForms项目中创建一个post-build事件,该事件将WinForms输出复制到WebForms / bin。
xcopy /y "$(ProjectDir)$(OutDir)*.*" "$(ProjectDir)..\WebFormsTestBed\bin\"
将WinForms项目设置为启动项目并运行它。如果你正确设置了一切,你应该看到:
这样做会创建一个AppDomain
,它基于WebForms项目,但是在WinForms项目的执行上下文中,它提供了一种从范围内的WinForms项目中触发回调方法的方法新创建的AppDomain
。这将允许您在WebForms项目中正确处理VirtualPath
问题,而无需担心模拟路径变量等的细节。
创建AppDomain
时,它需要能够找到其路径中的所有资源,这就是为什么创建post-build事件以将已编译的WinForms文件复制到WebForms / bin文件夹的原因。这就是&#34;依赖&#34;是从WebForms项目设置到上图中的WinForms项目。
最后,我不知道对你有多大帮助。可能有办法将这一切都集成到一个项目或两个项目中。如果没有更详细的说明,为什么或如何使用它,我不会再花费更多的时间。
注意:从ctl
返回的ParseControl()
现在是一个包装器,其Controls
集合实际上包含asp:TextBox
- 我还没有想弄明白为什么
您可以尝试完全模拟AppDomain
,而不是保留虚拟WebForms项目,因此在AppRelativeVirtualPath
上设置new Page()
不会导致...
System.Web.HttpException The application relative virtual path '~/' cannot be made absolute, because the path to the application is not known.
为了做到这一点,你可能首先要回顾上面引用的SO答案所使用的source。我引用的SO答案实际上是这种方法的解决方法,这就是我首先提出的建议,但它需要与WinForms项目在同一主机上的有效WebForms项目。