Is it possible to compile a web application project .ascx (user control) into a dll?
I want to do the following:
Use the same control in multiple websites
Embed css and .js as resources into the control
Be able to update super easy. If the user control updates, I just want to update 1 .dll
I have successfully followed this article, http://msdn.microsoft.com/en-us/library/aa479318.aspx.
However, that uses web site projects, and I cannot embed js css as resources into web site projects.
Any ideas? Am I going about this wrong, should I change the approach?
Conversion is easy, and could even be fully automated. It simply requires changing a few settings and base classes in the DLL Project you want your ASCX controls embedded in.
1... For each UserControl, set the ASCX file's Build Action (under Properties) to "Embedded Resource", and delete its associated designer file.
2... Save the project.
3... Right click the project and choose "Unload Project".
4... Right click it again and choose the "Edit *.csproj" option.
Change sections that look like this (where the asterisk represents your class name):
<Compile Include="*.ascx.cs">
<DependentUpon>*.ascx</DependentUpon>
<SubType>ASPXCodeBehind</SubType>
</Compile>
to look like this
<Compile Include="*.ascx.cs" />
That will cause the code-behind files to be compiled independently of the ASCX files.
5... Save changes, and right click the project and choose "Reload Project".
6... Open all your "*.ascx.cs" files and make them inherit from the following custom UserControl class, instead of the System.Web.UI.UserControl class (you may need to locate parent classes to complete this step).
public class UserControl : System.Web.UI.UserControl
{
protected override void FrameworkInitialize()
{
base.FrameworkInitialize();
string content = String.Empty;
Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream( GetType().FullName + ".ascx" );
using (StreamReader reader = new StreamReader(stream))
content = reader.ReadToEnd();
Control userControl = Page.ParseControl( content );
this.Controls.Add( userControl );
}
}
This base class will take care of loading and parsing the embedded ASCX file.
7... Finally, you may need to place ASCX files in subfolders so that their resource names (automatically determined by folder path) match the full type name of their associated class (plus ".ascx"). Assuming your root namespace matches your project name, then a class named "ProjectName.Namespace1.Namespace2.ClassName" will need its ASCX file in a subfolder "Namespace1\Namespace2", so it gets embedded with the name "ProjectName.Namespace1.Namespace2.ClassName.ascx".
And that's it! Once you compile the DLL and include it in another project, you can instantiate instances of your user controls using the "new" operator, like any other class. As always, your control will be "caught up" to the current page event once added as a child control to the page or another control on the page.
It is difcult to use user controls in this way due to the markup ascx file. If you want to create reusable control libraries your are much better off creating custom controls.
Another way would be to convert user control to the custom control. There is an nice article on MSDN: Turning an .ascx User Control into a Redistributable Custom Control which describes exactly how to do that. Here is the summary:
Write your user control as you normally would, typically using the Visual Studio designer.
Test it using a simple page before trying to deploy it.
Deploy the application to precompile it.
Grab the user control's assembly produced by the deployment step,
and you're essentially done: You have your custom control.
Finally, use your custom control in other apps the same way as you
always use custom controls.
Hope this helps.
I had to do it once and I followed this article
http://www.codeproject.com/KB/user-controls/EmbeddedUserControl.aspx
It's based on the possibility of mounting a virtual file system on different places (an assembly, database, etc)
There are quite a few articles out there on how to do exactly that:
http://www.nathanblevins.com/Articles/Compile-a-Web-User-Control-into-a-DLL-.Net-c-.aspx
http://blogs.msdn.com/davidebb/archive/2005/10/30/487160.aspx
I know this is old, but yes its possible.. I do it all the time, see https://kocubinski.wordpress.com/2013/05/06/compile-asp-net-webforms-ascx-usercontrols-into-assembly-for-re-use/
Related
My understanding is that when a component or custom control is defined in an aspx page using the <%Register%> tag, it is declared in an auto-generated designer.cs (C#) file by the compiler. If this custom control is never used in the aspx page, does this still happen in the designer.cs file?
Assuming the control is used in the aspx page, what mechanism then instantiates this control, how is it new-ed up behind the scenes? The designer file only declares it. Thanks much, and if there are good articles out there discussing this I’d be happy to read them.
This may not answer all your questions, but some of them. Rick Strahl wrote a great article a while back on compilation and deployment that describes how it works:
Compilation and Deployment in ASP.NET 2.0
I added part of the article that I think relates most to your question below:
Referencing other Pages and Controls
Remember that page and control compilation happens on a per directory basis! So referencing other pages and controls becomes a little more tricky for ASP.NET 2.0, because you can no longer assume that a CodeBeside class from another page or control is available in the current assembly. At best all pages and controls in the same directory end up in the same assembly, at worst each page or control gets its own assembly and they know nothing about each other.
If you need to reference another page from a control or another page you need to explicitly import it with the #Reference directive. Again this is different than ASP.NET 1.1 where all CodeBehind classes were immediately available to your entire Web application. In ASP.NET 2.0 an explicit assembly reference is required to load it.
Assume for a minute that you have the DataEntry.aspx page I showed earlier and you want to create a second page that uses the same CodeBeside class so you can reuse the page logic, but change the page layout in DataEntry2.aspx by changing a few colors and moving around the controls of the page. In essence you want to have two ASPX pages reference the same CodeBeside file.
Here’s how to do this:
<%# Reference Page="~/DataEntry.aspx" %>
<%# Page Language="C#" AutoEventWireup="true" Inherits="DataEntry" %>
I’m leaving out the CodeFile attribute reference the CodeBeside class of the DataEntry page, and add the #Reference tag to the page to force the CodeBeside class to be imported.
The same is true with any User Control definitions. To import a user control you need to use the #Register tag, which imports the assembly that the control lives in. ASP.NET is smart during compilation and figures out exactly where related assemblies live based on how the project is compiled. If the control or page lives in the same assembly no reference is actually added. But if it is external – in another directory for example, then the assembly reference is added.
Referencing problems
If you can explicitly reference other pages and controls in your markup pages, then all works well and as expected. But if you dynamically load controls or reference pages dynamically in your code, things get a lot more complicated.
The most common problem I run into is dynamic loading of controls. In ASP.NET 1.x you might have run code like this for dynamically loading controls into a page:
public partial class DynamicControlLoading : System.Web.UI.Page
{
protected CustomUserControl MessageDisplay = null;
protected void Page_Load(object sender, EventArgs e)
{
MessageDisplay = this.LoadControl( "~/UserControls/CustomUserControl.ascx") as CustomUserControl;
this.Controls.Add(MessageDisplay);
}
protected void btnSay_Click(object sender, EventArgs e)
{
this.MessageDisplay.ShowMessage(this.txtMessage.Text);
}
}
CustomUserControl in this case is a simple User Control that lives in another directory and is loaded dynamically at runtime. Further assume that you truly dynamically want to load this control so you may have a choice of several controls, or the end-user might even create a custom control that gets dropped into place instead.
If you run the code above in ASP.NET 2.0 it will likely fail. I say likely because there are some inconsistencies that will sometimes pick up control references automatically, for example if the user control lives in the same directory and gets compiled into the same assembly as the page, or if another page has the control referenced.
It should and usually will fail. Why? Because ASP.NET compiles on a directory level and the CustomUserControl lives in a separate directory and so goes into a separate assembly. It’s not visible to page class to get a strongly typed reference. Intellisense will show a big, fat and red exclamation point or nothing at all for the MessageDisplay control. When you run the page it will bomb.
You can reference the control as the Control type of course, but if you need to access any custom properties on the user control beyond Control properties you can’t unless you resort to Reflection. As far as I know there’s no way to add a reference to another user control or page programmatically because the reference needs to be available way earlier at compile time before your code ever runs.
Alternatives are to not load controls dynamically or at least provide some mechanism to load up any user controls beforehand on a page with the appropriate #Register tags. But that’s not always possible. The other option is to create a user control base class in APP_CODE and expose the public interface there. The main problem with this is that this base class will not have access to any internal controls of the user control and so the base class would have to use FindControl to reference any embedded controls. So this is inefficient as hell, and cumbersome to boot.
I’ve run into similar situations with inheritance scenarios. For example, inheriting one master page off another’s CodeBeside class. All works well, but the ASP.NET compiler complains that the Profile object is being overridden illegally (a compiler warning). Running with the inherited master page works, but there are quirks. User Controls added to the master page often fail with type conflicts as ASP.NET treats the user control added to the base page as a different type than the user control added to the second page.
It’s inconsistencies like these that deal with referencing other types that have made me waste an incredible amount of time, thinking I had something fixed only to find out later that it didn’t actually work consistently when I changed a completely different page. Worse you have to really understand the model to get your head around what might be wrong.
Bottom line: The overall ASP.NET 2.0 compilation model is internally complex. Most of the time you don’t need to understand it, but when you run into these boundary scenarios, you really DO have to understand what goes on behind the scenes to be able to work around the quirks.
So I'm having problems when I try to publish the website.
I'm in visual studio 2008 sp1.
I've got a bunch of user controls and on a few pages I'm using them programatically.
I've got a reference on the aspx page
<%# Reference Control="~/UserControls/Foo.ascx" %>
Then on the code behing I use
ASP.usercontrols_foo newFoo control = (ASP.usercontrols_foo)Page.LoadControl("~/UserControls/Foo.ascx");
If I navigate to the page it works fine, but when I goto publish the website I get a compile time error.
Argh, I'm bleeding development hours on this same issue. Does anyone have a solution to this ?
BTW: It builds if you uncheck "Allow this precompiled site to be updatable" (Right-click on the website, choose Property Pages, MSBuild Option)
But I need the site to be updatable.
I had this same problem - actually, in my case I could get it to compile and build my website, but it would blow up spectacularly when doing an automated build.
Try replacing your code as follows:
ASP.usercontrols_foo newFoo control = (ASP.usercontrols_foo)Page.LoadControl("~/UserControls/Foo.ascx");
with
USERCONTROLS_Foo newFoo control = (USERCONTROLS_Foo)Page.LoadControl("~/UserControls/Foo.ascx");
(Capitalization will be based on how you capitalized your directory name/control name - but in either case should highlight appropriately).
Specify a namespace for user control (see Dynamically Load a user control (ascx) in a asp.net website ).
I've found a solution for it. If i'll declare controls in user defined namespace, then I can use controls directly without using ASP or referencing it into aspx page.
It may have something to do with the specific type not being available. Can you change the control reference so the type and cast just use the base Control class:
Control control = Page.LoadControl("~/UserControls/Foo.ascx");
Yes, I can cast it to Control. But then I lose direct access to the methods on the usercontrol.
I know that I can access the methods by reflecting into the control, and I've successfully done that but that's far from ideal to access all the other user controls on the site.
Also, the follow-up error is that it cant find the usercontrols that on the markup
<%# Register src="CategoryRows.ascx" tagname="CategoryRows" tagprefix="abc" %>
<abc:CategoryRows ID="CategoryRows" runat="server" />
Note that I can run the site successfully both locally and on the server if I essentially XCopy the entire site (source code and all). But Publish fails.
Casting the user control may create many problem .my approach is to create a class (say control Class) put all the properties and method you need after casting and inherit this class from System.Web.UI.UserControl .Then in your user cotrol code file instead of System.Web.UI.UserControl user this control class .
now when ever you need casting cast with this class only . it will be light casting as well.
Please check:
Do you have any website.publishproj file?
If it exists, then delete and try again and publish code.
I have a series of .NET 2.0 web pojects that have identicle code behind in their web pages. The only thing that is different is how the html is laid out.
Is there a way that I can have a main web project that holds all the code behind (including control events like button click) and so that the other web projects reference the web page code file to this project's code files?
Edit: Note that the changes in html include exclution of certain controls, I am catoring for this by using the FindControl method so that if the control doesnt exists, I would simply have a null value.
You may try putting all the code-behind classes into a library and inheriting from a common base-class.
You can take a look at "Skin File" file type of Visual Studio. On the other hand you can learn if it works for you from the address below: http://msdn.microsoft.com/en-us/library/ykzx33wh(VS.80).aspx
I'm using VS2008 and want to create a web application (not a web site) with Code-Beside
but, the default mode of aspx is Code-Behind.
I have tried to change the CodeBehind=ClassFile.cs to CodeFile=ClassFile.cs in the header of aspx's <%#Page%> part, and deleted the aspx.designer.cs file,but if I added a server control to the page, the compiler is also send me an error of no member defined.the cs file is the orinal file of codebehind, it is partial class.
You don't want to delete aspx.designer.cs you want to delete the aspx.cs file, then place a similar file next to it and declare it as a partial class. designer.aspx.cs is still required to provide you direct access to controls placed within the page, rather than going through FindControl.
You definitely don't want to delete the .designer.cs file, as this is where the server control definitions will be placed.
In general the codebehind model is much better as it makes the code easier to find, use and maintain.
Suppose you have 2 different ASP.NET applications in IIS. Also, you have some ASCX controls that you want to share across these 2 applications.
What's the best way to create a "user control library", so that you can use the same control implementation in the 2 applications, withuot having to duplicate code?
Controls have ASCX with HTML + code behind.
Composite controls will be difficult, because we work with designers who use the HTML syntax in the ASCX files to style the controls.
Tundey, we use SVN here. Do you have an example on how to implement your suggestion? How can SVN share the ASP.NET controls?
Thanks!
Scott Guthrie gives some great advice here on how to set up a User Control Library project, then use pre-build events to copy the user controls into multiple projects. It works really well.
http://webproject.scottgu.com/CSharp/usercontrols/usercontrols.aspx
You would need to create composite controls instead of .ASCX controls if you wanted to be able to use them in separate projects.
In addition to what Tundey said, NTFS Link shell extension is helpful when it comes to sharing a large chunk of content (e.g.: a folder with .ascx/.aspx) between otherwise independent projects. In case of code, i think making another working copy from VCS is preferable.
Have a look at this: http://www.codeproject.com/KB/aspnet/ASP2UserControlLibrary.aspx?msg=1782921
An alternative is to use your source control tool to "share" the ASCX controls between your webapps. This will allow you to make changes to the controls in either application and have the source control ensure the changes are reflected in the our webapps.
The biggest problem I've noticed with controls in ASP.Net is that you can't easily get designer support for both building the control and using the control in a site once you built it. The only way I've been able to do that is create an .ascx control with no code-behind (ie: all the server-side code is in a script tag in the .ascx file with the runat="server" attribute).
But even then, you still have to copy the .ascx file around, so if you ever need to make a change that means updating the file at every location where you've used it. So yeah, make sure it's in source control.
I managed to do this by sacrificing some of the ease of building the controls in the first place.
You can create a Control Library project that will generate a control library DLL for you. The drawback is that you have to create the controls with code only. In my last project, this was fine. In more complicated controls, this may be a problem.
Here's an example:
<DefaultProperty("Text"), ToolboxData("<{0}:BreadCrumb runat=server />")> _
Public Class BreadCrumb
WebControl
<Bindable(True)> _
Property Text() As String
'...'
End Property
Protected Overrides Sub RenderContents(output as HtmlTextWriter)
output.write(Text)
End Sub
Private Sub Page_Load(...) Handles MyBase.Load
' Setup your breadcrumb and store the HTML output '
' in the Text property '
End Sub
End Class
Anything you put in that Text property will be rendered.
Then, any controls you put in here can function just like any other control you use. Just import it into your Toolbox, make your registration reference, then plop it onto the ASP page.
I use StarTeam here and it allows you to "share" objects (files, change requests, requirements etc) across multiple folders. Not sure if Subversion (SVN) has that feature. If it doesn't, here's another trick you can use: create a junction from the primary location of the controls to a location in the other projects. A junction is just like a Unix symbolic link. You can download the tool for creating junctions in Windows from here
I have a suggestion.WE can use user control across multiples application by creating user control inside website project as normally.Then change the website property Use fixed naming and single page assemblies.Then we can use the user control dll into multiple applications.
I recently did a web application that just referenced the files (about 90 in total) from one web application (aspx, master and ascx) without too much of an issue. That said I was using a heavily modified version of the MVP pattern, a lot of interfaces and conventions to keep the complexity down, the same middle tier and one site was a subset of the other.
Big issues:
Master pages (and in turn designers and html view formatting) don’t work on a referenced file so you lose a lot of functionality. A pre-build step and a lot of svn:ignore entries was my hack around this. It was also a pain to get CruiseControl.NET to get the pre-build task to execute in the right folders.
Shared pages/controls need to be extremely aware of what they touch and reference as to avoid bringing in extra dependencies.
Both sites are locked together for deployment.
I now have to pray that the maintainer reads my pokey little document about the mess I made. It’s so far outside of what I’ve seen in ASP.NET projects.
I was under a massive time pressure to get it to work, it does and now both apps are in production. I wouldn’t recommend it but if you’re interested start at:
Add Existing Item, select some files, click on the Add button’s arrow and say Add as a Link.