Strongly-typed ASCX in WebForms 3.5? - asp.net

I'm looking to get rid of the code-behind for a control in my WebForms 3.5 application. Again bitten by the bug of how it's done in MVC, I'd like to get a step closer to this methodology by doing:
<%# Control Language="C#" Inherits="Core.DataTemplate<Models.NewsArticle>" %>
This gives me the parser error you'd expect, so I remembered back to when this was an issue awaiting a fix in the MVC Preview, and changed it to:
<%# Control Language="C#" Inherits="Core.DataTemplate`1[[Models.NewsArticle]]" %>
But this doesn't work either! How is it that the MVC team were able to harness this ability? Was it something special about the MVC project type rather than the latest VS2008 Service Pack?
Short of giving up and requiring future templates to have code-behind files, what are my best options to get this as close to the generic user control method as possible?

Well, it appears like I've managed to do it. After looking at the PageParserFilter implemented by the MVC team for ViewUserControl<T>, I was able to construct something similar for my own DataTemplate<T> purposes. Sweet. I can now use the line:
<%# Control Language="C#" Inherits="Core.DataTemplate<Models.NewsArticle>" %>
And, without any code behind file, it parses! I'll report back if I find that I've broken something else in the process!

With WebForms you lose pretty much everything that makes them useful without a code behind page, because then VS can't auto generate the designer file that holds the actual definitions for all your runat="server" controls.
What you can do is have a common base page class, and make that generic:
public class DataTemplate<T> : Page {
public T Model {get;set;}
}
public partial class MyCodeBehindClass :
DataTemplate<Models.NewsArticle> {
...
}
This would allow all the drag-drop component stuff that WebForms does to work unhindered, while also allowing you to access a strongly typed model on the page:
<%# Control Language="C#" Inherits="MyCodeBehindClass" %>
<% foreach( var item in Model ) { %>
<!-- do stuff -->
<% } %>

Related

How to disable/override naming container's ID generation of Content page's control id's

We have an existing ASP.Net web application which we want to convert into using masterpages.
In the process of doing this, I found that the HTML id's generated for the HTML elements are prefixed with the ContentPlaceHolder's id. And this is what can be expected when we set the ContentPlaceHolder's clientidmode=static.
Now since we have a lot of existing client side scripts that make use of the id's, this part breaks when we use masterpages, and it is quite a big job to run through all our javascript to make sure we call the javascript using Control.ClientID, as a lot of it is hardcoded.
Is there a way to disable the prefixing? I can succeed doing this, if I create every control setting its ClientIdMode=static, but then again I would prefer settings this once, to ensure that all controls are have their ClientIdMode=static. Is that possible? Or is it possible to override the NamingContainer of the ContentPlaceHolder?
The platform is .Net 4.0
(After fixing the above problem with ClientIdMode=static in the web.config as described in the answer below), I have bumped into the problem that the "name" attribute is automatically generated, and is not set to whatever it was before I introduced masterpages. This gives me a problem with my existing server code, which has many Request.Form[]. Any idea what best practice is here to solve this problem?
Thanks
Jihad
You can have ClientIDMode at Page Level:
<%# Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" ClientIDMode="Static" %>
MasterPage Level:
<%# Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="MasterPage" ClientIDMode="Static" %>
and Web.Config level (making all pages inherit this behavior):
<system.web>
<compilation debug="true" targetFramework="4.0"/>
<pages clientIDMode="Static"></pages>
</system.web>
In case you don't target .Net framework 4, and the clientIDMode enum is not available, you can simply override the control class. Here is an example with HtmlGenericControl but can be done with any other control:
public class NoNamingContainerControl : HtmlGenericControl
{
public NoNamingContainerControl(string tag) : base(tag) { }
public override string ClientID
{
get
{
return this.ID;
}
}
}
This code should work on any .Net framework versions, although in .net 4 you should probably use the clientIDMode

How to pass an object from .cs to .aspx

I am a asp .net beginner. I want to use some objects created at Site.Master.cs in Site.Master. Is there an easy way to do it?
I know how to do it in MVC(by using view(the object)). But how can i do it in normal ASP .net web application?
I don't understand what exactly you want to do.
If you want to insert some string into tag's title you can insert the following thing in SiteMaster.master file:
<img src="<%= Page.ResolveUrl("~/") %>images/logo.png">
instead of:
<img src="images/logo.png">
In the first case there will be calculated the path from the root of your application. In the second case there will be relative link. This is because server will CALCULATE the value of Page.ResolveUrl("~") function and will WRITE it in src tag.
You can do the same thing with any other methods, classes if you defined them properly. But I wouldn't recommend you to implement complicated logic in .aspx files (or .master files). Because you can end up with many difficulties with testing and styling such application.
There are other server tags:
<% %> - an embedded code block is server code that executes during the page's render phase. The code in the block can execute programming statements and call functions in the current page class. Description and examples
<%= %> - most useful for displaying single pieces of information. Description and examples
<%# %> - data binding expression syntax. Description and examples
<%$ %> - ASP.NET Expression. Description and examples
<%# %> - Directive Syntax. Description and examples
<%-- --%> - Server-Side Comments. Description and examples
<%: %> like <%= %> - But HtmlEncodes the output (new with Asp.Net 4). Description and examples
Another way: you can use JSON to send some data to the client and then process it with javascript. Take a look at this project.
If the #Page directive in your .aspx file has Inherits="XYZ" where XYZ is the class declared in your .cs file, you can simply add a protected field to your class and assign a value to it. You'll be able to access it in the .aspx file just by using its name.
You can also use HttpContext.Items property to keep objects during a single request:
HttpContext.Current.Items["SavedItem"] = "hello world";
And use it in page:
<%= ((string)Context.Items["SavedItem"]) %>
Any public or protected property or method in Site.Master.cs will be accessible from Site.Master.
but how to invoke c# code in aspx ?
There are several ways, including the <%= %> construction, and databinding syntax.
It would help if you explained what you're trying to achieve.

Add custom user control to published web project

I am beginning to wonder if this is even possible. It just seems so simple.
I have a published web project. I want to add some .ascx files (with .cs & designer.cs files) to that published web site. These are simple custom user controls that access methods already part of the original application.
Question? Is it possible to just drop these in the published web project without building the entire solution? If not why?
When I drop these files in and run my application I get the error:
"Parse Error: Could not load type 'the name of my custom controls namespace'".
There is not a lot of code to show so this is all I have.
Default.aspx
<%# Page Language="C#" MasterPageFile="~/MasterPages/TwoColumn.master" AutoEventWireup="true"
Inherits="ApplicationName.Web.Default" CodeBehind="Default.aspx.cs" %>
<%# Register TagPrefix="uc1" TagName="CustomControl" Src="~/Controls/Custom/CustomControl.ascx" %>
<asp:Content ID="content" contentplaceholder="cph" runat="Server">
<uc1:CustomControl ID="cc1" runat="server" CustomProperty="Hello World" />
</asp:Content>
CustomControl.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="CustomControl.ascx.cs"
Inherits="ApplicationName.Web.Controls.Custom.CustomControl" %>
<asp:PlaceHolder ID="ph1" runat="server></asp:PlaceHolder>
CustomControl.ascx.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace ApplicationName.Web.Controls.Custom
{
public partial class CustomControl : System.Web.UI.UserControl
{
///My logic
}
}
Again it seems so easy. What am I missing? Or is this not possible? Thanks.
UPDATE:
I figured this out. The above scenario is possible. The problem is not with the name-space as the error message suggests. Rather it is the code-behind declaration. Code-behind files for any type of file are compiled when the application is published. I am still confused as to why it appears to be editable when you browse through a web directory, I would think it would be stored in a .dll file or something. Maybe someone can shed some light on this.
Anyways, replacing code-behind with code-file rectifies the problem as code-files are not compiled and are therefore readable at application run-time.
Some links that were helpful can be found here and here.
It is possible but you still have to compile your user control and drop that dll into the proper bin directory for your app. That's usually the cause of the type loading error you described.
This approach could be sloppy, you are basically either
1) Creating the User Control in the wrong project
2) Trying to add the same User Control to two projects
Have you thought about a cleaner approach and just creating a class that inherits from System.Web.Ui.Control and then adding this in a .common project? Then pulling this into the corrct project? The problem with your approach is on precompilation and deployment you could end up trying to put two user controls into the same folder which will break the build....
The alternate approach (and the microsoft way) would be like this...
The code - write a custom control
namespace MyProject.Common.Controls
{
public class PolicyTab : System.Web.UI.Control
{
protected override void CreateChildControls()
{
base.CreateChildControls();
HtmlGenericControl policyTab = new HtmlGenericControl();
policyTab.InnerHtml = "<strong> Some policy code here! </strong>";
this.Controls.Add(policyTab);
}
}
}
The page reference - how to reference it in your UI project
<CommonControls:PolicyTab runat="server" ID="temp"></CommonControls:PolicyTab>
Web.config - what you need to import this control into all of your UI pages
<add tagPrefix="CommonControls" namespace="MyProject.Common.Controls" assembly="MyProject.Common"/>

Register User Control Issue

I have a user control registered at the top of my page:
<%# Register Src="/Controls/User/Navbar.ascx" TagName="Navbar" TagPrefix="pmc" %>
and I reference it in my page like this:
<pmc:Navbar runat="server" id="navbar"></pmc:Navbar>
but it does not know what <pmc:Navbar is. I cannot figure out why.
I'm using VS 2008, in a Web Application Project.
Maybe you should specify the path with ~: ... Src="~/Controls/User/Navbar.ascx" ...
Remove either the initial slash from the path to the control, or better still, prefix it with "~" :
<%# Register Src="Controls/User/Navbar.ascx" TagName="Navbar" TagPrefix="pmc" %>
or
<%# Register Src="~/Controls/User/Navbar.ascx" TagName="Navbar" TagPrefix="pmc" %>
The first solution is flakey as it relies on the page existing in the root folder and the control existing below it. The second is the preferred as it will work from any page in your project.
You should also consider registering your user controls in your web.config, as it keeps things much neater, and tends to avoid path issues a little better.

Using ASP.NET MVC with generic views

I am currently investigating MVC for a new project we are starting. So far I like it, but I'm wondering about something.
The actual views that we will be displaying will not be known at design time, we will be specifying in a config file somewhere how to build these views. Is this pattern supported by MVC or do we need to know at design time exactly what data we will be viewing?
If not, can someone give me some pointers on what I should be looking at as most of the info I have assumes that you have a model/view that is defined during your design.
Regards,
Alex..
You can have your views weakly-typed... Your initial page directive on the view will look like:
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
... and then you can refer to data from your Controllers like this:
<%= ViewData["MyData"] %>
Is there some common interface that you are intending to pass to your view? If so, you can benefit from a using the generic ViewPage<> :
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<IamTheInterface>" %>
Then, you can use your interface to refer to your Model:
<%= Model.MyProperty %>
There is cool post in LosTechies.com about building an "autoform" with fields autogenerated from the Model properties. Take a look, it might be what you are looking for.

Resources