Embedded UserControls: Revisited
2007-07-09 13:32
204 查看
Download demo project and source files - 39.1 Kb
http://www.codeproject.com/aspnet/EmbeddedUserControl.asp
A control library
A test web site
You can use the project stub attached to this project, or if you prefer, you can setup your own project from scratch. When you finish, your solution layout should have one web project for testing and one web project that will contain your embedded controls.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0115.png)
To start from scratch, create a new ASP.NET Web Application project. Notice the difference here - we choose New > Project.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0111.png)
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0116.png)
Since we're creating an assembly, remove Default.aspx and web.config. This will leave you with an empty project.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0113.png)
Create VirtualPathProvider.cs or copy it from the sample project.
![](http://www.codeproject.com/images/minus.gif)
Collapse
Note the exceptions thrown if the assembly or resource name is not found. This differs from the original code.
Create or copy the EmbeddedUserControlLoader.cs class. This is the one that gives us VS2005 toolbox support.
![](http://www.codeproject.com/images/minus.gif)
Collapse
Registering a virtual path has a special sequence tied to it. Microsoft recommends registering all virtual paths "before any page parsing or compilation is performed by the Web application" and that registering them later will not affect pages that are compiled and cached.
In web portal settings, you do not always have access to
Static prefix property
Your control library must implement a unique prefix for the virtual paths to work. This property allows you to configure the prefix at runtime.
This class allows you to
Toolbox design-time properties
You can specify the assembly name, namespace, and control name to load at design-time. More on that later.
As a final step, create your test web site. This can be either a new ASP.NET Web Project or a traditional VS2005 Website project. Keep the default.aspx and web.config, and be sure to add a reference to your
Create a new
Then - and this is the key step - setthe
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0117.png)
Now turn to your test site where you have included a reference to
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0121.png)
Run Default.aspx and be amazed. Your user control loads flawlessly, directly from the
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0122.png)
Now that you've done something basic, let's take a look at a few more complicated scenarios.
Let's make a feedback control. We want to collect some basic information and expose it through properties.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0123.png)
FeedbackControl.ascx
FeedbackControl.ascx.cs
![](http://www.codeproject.com/images/minus.gif)
Collapse
Now, let's make a feedback form that collects the information. Rather than emailing it, we will just display it to the user at postback.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0125.png)
Feedback.aspx
![](http://www.codeproject.com/images/minus.gif)
Collapse
Feedback.aspx.cs
![](http://www.codeproject.com/images/minus.gif)
Collapse
And there you have it. You have accessed your properties almost as easily as you can do with custom controls.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0126.png)
Let's approach that by creating a light-weight, custom control wrapper for the feedback control. Create a custom control FeedbackControlWrapper.cs in the
![](http://www.codeproject.com/images/minus.gif)
Collapse
Creating a palatable design-time rendering of your control is beyond the scope of this article, but
You can add properties to the wrapper that will appear in the VS2005 property designer box. Search around for your favorite article demonstrating how to expose properties of custom controls at design-time.
The new custom control feedback page is nearly identical to the earlier example. However this time, we drop our custom control from the VS2005 toolbox and change our code-behind to something a little more familiar.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0129.png)
FeedbackCustomControl.aspx.cs
![](http://www.codeproject.com/images/minus.gif)
Collapse
Success, and a much more professional architecture!
http://www.codeproject.com/aspnet/EmbeddedUserControl.asp
Introduction
Visual Studio 2005 Service Pack 1 restores support for ASP.NET Web Projects, making it once again possible to compile ASPX and ASCX pages into a single assembly. Combined with design-time WYSIWYG support and clever use of resource embedding, it is possible to leverage the ASP.NETUserControltechnology to create web control libraries that are as flexible and robust as first-class custom controls.
Background
The initial release of Visual Studio 2005 dropped support for the "web project", much to the dismay of web developers. In response, a Web Application Project option became available to restore this functionality, but you had to go getit and install it. With the Service Pack 1 release, this functionality was restored and now we're going to take it for a spin!Prerequisites
Visual Studio 2005 Service Pack 1Project Setup
To demonstrate the power of embedded controls, we need to create two projects:A control library
A test web site
You can use the project stub attached to this project, or if you prefer, you can setup your own project from scratch. When you finish, your solution layout should have one web project for testing and one web project that will contain your embedded controls.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0115.png)
To start from scratch, create a new ASP.NET Web Application project. Notice the difference here - we choose New > Project.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0111.png)
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0116.png)
Since we're creating an assembly, remove Default.aspx and web.config. This will leave you with an empty project.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0113.png)
Create VirtualPathProvider.cs or copy it from the sample project.
![](http://www.codeproject.com/images/minus.gif)
Collapse
using System.Web.Hosting; using System.Web.Caching; using System.Collections; using System; using System.IO; using System.Web; using System.Reflection; namespace ControlLibrary { public class AssemblyResourceProvider : VirtualPathProvider { string mResourcePrefix; public AssemblyResourceProvider() :this("EmbeddedWebResource") { } public AssemblyResourceProvider(string prefix) { mResourcePrefix = prefix; } private bool IsAppResourcePath(string virtualPath) { String checkPath = VirtualPathUtility.ToAppRelative(virtualPath); return checkPath.StartsWith("~/"+mResourcePrefix+"/", StringComparison.InvariantCultureIgnoreCase); } public override bool FileExists(string virtualPath) { return (IsAppResourcePath(virtualPath) || base.FileExists(virtualPath)); } public override VirtualFile GetFile(string virtualPath) { if (IsAppResourcePath(virtualPath)) return new AssemblyResourceVirtualFile(virtualPath); else return base.GetFile(virtualPath); } public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart) { if (IsAppResourcePath(virtualPath)) return null; else return base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart); } } class AssemblyResourceVirtualFile : VirtualFile { string path; public AssemblyResourceVirtualFile(string virtualPath) : base(virtualPath) { path = VirtualPathUtility.ToAppRelative(virtualPath); } public override Stream Open() { string[] parts = path.Split('/'); string assemblyName = parts[2]; string resourceName = parts[3]; assemblyName = Path.Combine(HttpRuntime.BinDirectory, assemblyName); Assembly assembly = Assembly.LoadFile(assemblyName); if (assembly == null) throw new Exception("Failed to load " + assemblyName); Stream s = assembly.GetManifestResourceStream(resourceName); if (s == null) throw new Exception("Failed to load " + resourceName); return s; } } }
Points of interest
Note the constructor takes a configurable prefix. This differs from the original code.Note the exceptions thrown if the assembly or resource name is not found. This differs from the original code.
Create or copy the EmbeddedUserControlLoader.cs class. This is the one that gives us VS2005 toolbox support.
![](http://www.codeproject.com/images/minus.gif)
Collapse
using System.Web.UI.WebControls; using System.Web.UI; using System; using System.ComponentModel; using System.Web; namespace ControlLibrary { [ToolboxData("<{0}:EmbeddedUserControlLoader runat=server> </{0}:EmbeddedUserControlLoader>")] public class EmbeddedUserControlLoader : WebControl { # region VirtualPathProvider setup code staticEmbeddedUserControlLoader() { if (!IsDesignMode) { System.Web.Hosting.HostingEnvironment. RegisterVirtualPathProvider( new AssemblyResourceProvider(ResourcePrefix)); } } staticbool IsDesignMode { get { return HttpContext.Current == null; } } staticstring mResourcePrefix = "EmbeddedWebResource"; public staticstring ResourcePrefix { get { return mResourcePrefix; } set { mResourcePrefix = value; } } #endregion #region Toolbox properties private string mAssemblyName = ""; [Bindable(true)] [Category("Behavior")] [Localizable(true)] public string AssemblyName { get{ return mAssemblyName; } set{ mAssemblyName = value; } } private string mControlNamespace = ""; [Bindable(true)] [Category("Behavior")] [Localizable(true)] public string ControlNamespace { get{ return mControlNamespace; } set{ mControlNamespace = value; } } private string mControlClassName = ""; [Bindable(true)] [Category("Behavior")] [Localizable(true)] public string ControlClassName { get{ return mControlClassName; } set{ mControlClassName = value; } } #endregion #region Private members string Path { get { return String.Format("/{0}/{1}.dll/{2}.{3}.ascx", ResourcePrefix, AssemblyName, ControlNamespace, ControlClassName); } } Control c; protected override void OnInit(EventArgs e) { c = Page.LoadControl(Path); Controls.Add(c); base.OnInit(e); } protected override void RenderContents(HtmlTextWriter output) { if (IsDesignMode) { output.Write(Path); return; } base.RenderContents(output); } #endregion #region Helper members to access UserControl properties public void SetControlProperty(string propName, object value) { c.GetType().GetProperty(propName).SetValue(c, value, null); } public object GetControlProperty(string propName) { return c.GetType().GetProperty(propName).GetValue(c, null); } #endregion } }
Points of interest
Static methodRegistering a virtual path has a special sequence tied to it. Microsoft recommends registering all virtual paths "before any page parsing or compilation is performed by the Web application" and that registering them later will not affect pages that are compiled and cached.
In web portal settings, you do not always have access to
Application_Start(). Therefore, I prefer an alternative that initializes the provider within the user code. In my testing I didn't have any problem registering in the
staticconstructor of
EmbeddedUserControlLoaderbecause that executes before any instance of the class executes, therefore the virtual path is configured in time for
OnInitto utilize it. For more information, read about VirtualPathProvider on MSDN.
Static prefix property
Your control library must implement a unique prefix for the virtual paths to work. This property allows you to configure the prefix at runtime.
Get/
Setmethods
This class allows you to
getand
setproperties on the underlying control. More on that later.
Toolbox design-time properties
You can specify the assembly name, namespace, and control name to load at design-time. More on that later.
As a final step, create your test web site. This can be either a new ASP.NET Web Project or a traditional VS2005 Website project. Keep the default.aspx and web.config, and be sure to add a reference to your
ControlLibraryassembly.
Creating and Embedding Your First UserControl
With our solution stub in hand, we are ready to begin! Let's create a Hello WorldUserControlto demonstrate.
Create a new
UserControl(named
HelloControl) in your
ControlLibraryproject. Put some text in the
UserControl.
Then - and this is the key step - setthe
Build Actionon the ASCX file to
Embedded Resource. That's it! You are done. Compile. Your ASCX file is now embedded in ControlLibrary.dll.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0117.png)
Now turn to your test site where you have included a reference to
ControlLibrary. Open Default.aspx and drag an
EmbeddedUserControlLoadercontrol from the VS2005 toolbox to your page. You can check HelloControl.ascx.cs in
ControlLibraryfor the appropriate values for
AssemblyName,
ControlClassName, and
ControlNamespace. As you change them, the control's design-time representation will display the path to the embedded resource that it intends to load.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0121.png)
Run Default.aspx and be amazed. Your user control loads flawlessly, directly from the
ControlLibraryassembly!
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0122.png)
Now that you've done something basic, let's take a look at a few more complicated scenarios.
Advanced Technique: Nested Controls
Your embeddedUserControlscan be nested. This works so well that I'll leave it up to you. Just drop
EmbeddedUserControlLoaderobjects in the
UserControlsyou create in the
ControlLibraryproject. You can nest your controls all day long.
Advanced Technique: Property Accessors
You can useEmbeddedUserControlLoaderto access properties of your
UserControls. This is worth understanding in detail because it is the only thing that separates your controls from feeling like genuine custom controls.
Let's make a feedback control. We want to collect some basic information and expose it through properties.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0123.png)
FeedbackControl.ascx
<%@ Control Language="C#" AutoEventWireup="true" Codebehind="FeedbackControl.ascx.cs" Inherits="ControlLibrary.FeedbackControl" %> <h1> Your Feedback is <span style="font-size: 5pt">not</span> Valuable</h1> Name: <asp:TextBox ID="txtName" runat="server"></asp:TextBox><br /> Subject: <asp:TextBox ID="txtSubject" runat="server"></asp:TextBox><br /> Message:<br /> <asp:TextBox ID="txtMessage" runat="server" Height="256px" TextMode="MultiLine" Width="353px"></asp:TextBox>
FeedbackControl.ascx.cs
![](http://www.codeproject.com/images/minus.gif)
Collapse
using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; namespace ControlLibrary { public partial class FeedbackControl : System.Web.UI.UserControl { protected void Page_Load(object sender, EventArgs e) { } public string Name { get { return this.txtName.Text; } } public string Subject { get { return this.txtSubject.Text; } } public string Message { get { return this.txtMessage.Text; } } } }
Now, let's make a feedback form that collects the information. Rather than emailing it, we will just display it to the user at postback.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0125.png)
Feedback.aspx
![](http://www.codeproject.com/images/minus.gif)
Collapse
<%@ Page Language="C#" AutoEventWireup="true" Codebehind="Feedback.aspx.cs" Inherits="DemoSite.Feedback" %> <%@ Register Assembly="ControlLibrary" Namespace="ControlLibrary" TagPrefix="cc1" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> <cc1:EmbeddedUserControlLoader ID="EmbeddedUserControlLoader1" runat="server" AssemblyName="ControlLibrary" ControlClassName="FeedbackControl" ControlNamespace="ControlLibrary"> </cc1:EmbeddedUserControlLoader> <br /> <br /> <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Submit Feedback" /><br /> <br /> <asp:Panel ID="Panel1" runat="server" Height="244px" Visible="False" Width="383px"> Name: <asp:Label ID="lblName" runat="server" Text="Label"> </asp:Label><br /> Subject: <asp:Label ID="lblSubject" runat="server" Text="Label"> </asp:Label><br /> Message:<br /> <asp:Label ID="lblMessage" runat="server" Text="Label"> </asp:Label></asp:Panel> </div> </form> </body> </html>
Feedback.aspx.cs
![](http://www.codeproject.com/images/minus.gif)
Collapse
using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; namespace DemoSite { public partial class Feedback : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { this.Panel1.Visible = true; this.lblName.Text = this.EmbeddedUserControlLoader1.GetControlProperty("Name") as string; this.lblSubject.Text= this.EmbeddedUserControlLoader1.GetControlProperty("Subject") as string; this.lblMessage.Text = this.EmbeddedUserControlLoader1.GetControlProperty("Message") as string; } } }
And there you have it. You have accessed your properties almost as easily as you can do with custom controls.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0126.png)
Advanced Technique: Custom Control Wrappers
Ok, some of you may not be satisfied withEmbeddedUserControlLoader.GetPropertyValue()and I can appreciate that. For some situations, such as if you are distributing your control commercially, you want full design-time support available in Visual Studio 2005.
Let's approach that by creating a light-weight, custom control wrapper for the feedback control. Create a custom control FeedbackControlWrapper.cs in the
ControlLibraryproject.
![](http://www.codeproject.com/images/minus.gif)
Collapse
using System.Web.UI; namespace ControlLibrary { [ToolboxData("<{0}:FeedbackControlWrapper runat=server> </{0}:FeedbackControlWrapper>")] public class FeedbackControlWrapper : EmbeddedUserControlLoader { public FeedbackControlWrapper() { ControlClassName = "FeedbackControl"; AssemblyName = "ControlLibrary"; ControlNamespace = "ControlLibrary"; } public string Name { get { return GetControlProperty("Name") as string; } } public string Subject { get { return GetControlProperty("Subject") as string; } } public string Message { get { return GetControlProperty("Message") as string; } } } }
Points of interest
The wrapper is a genuine custom control that uses the same property accessor technique shown above in Feedback.aspx.cs, but hides it from the end user of your control. Notice that theToolboxDatacontrol prefix is
FeedbackControlWrapper. It should match the class name and should be different than the
UserControlclass name. (An alternate naming convention is to save the
SomeControlname pattern for the wrapper names and use a different pattern for the underlying
UserControlsthat nobody else will ever see).
Creating a palatable design-time rendering of your control is beyond the scope of this article, but
EmbeddedUserControlLoaderdemonstrates what you would need to do to create a design-time rendering. In general,
HttpContextis not available and must be simulated. Any commercial control must deal with the issue of design-time rendering better than I have in this article!
You can add properties to the wrapper that will appear in the VS2005 property designer box. Search around for your favorite article demonstrating how to expose properties of custom controls at design-time.
The new custom control feedback page is nearly identical to the earlier example. However this time, we drop our custom control from the VS2005 toolbox and change our code-behind to something a little more familiar.
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0129.png)
FeedbackCustomControl.aspx.cs
![](http://www.codeproject.com/images/minus.gif)
Collapse
using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; namespace DemoSite { public partial class FeedbackCustomControl : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { this.Panel1.Visible = true; this.lblName.Text = this.FeedbackControlWrapper1.Name; this.lblSubject.Text = this.FeedbackControlWrapper1.Subject; this.lblMessage.Text = this.FeedbackControlWrapper1.Message; } } }
Success, and a much more professional architecture!
![](http://www.codeproject.com/aspnet/EmbeddedUserControl/SNAG-0127.png)
相关文章推荐
- Vistual Studio User Controls not showing up in the toolbox
- User controls and custom controls
- How to Add ASP.NET User Controls To Master Pages or Page Layouts
- C#--工作笔记(直方图单个UserControls)
- 对 Web user controls 和 Web custom controls 的一些建议
- ASP.Net User Controls as Static or Movable PopUps
- Login Controls[6]->CreateUserWizard
- ASP.NET MVC Tip #31: 给 Master Pages 和 User Controls 传递数据
- \Controls\User\RegisteredUserControl.cs
- How to access UI controls from a user defined class (CFormView based SDI application)
- C#--工作笔记(直方图整体UserControls)
- Qt for Embedded Linux data directory is not owned by user xxxxh
- Qt/Embedded data directory is not owned by user 0: /tmp/qtembedded-0
- User Interface Technologies for Windows Embedded CE
- Web User Controls之间的通讯
- Some Experience about userControls
- Custom Controls and User Controls
- Creating Reusable XAML User Controls with Xamarin Forms
- SharePoint2016母版页的直接依赖项的数目限制超过10的限制解决方案Direct Dependencies Limit with Master page User Controls
- How to: Create Instances of ASP.NET User Controls Programmatically