I have to move my UI page from a .cshtml file to an .aspx file. Now I'm having a couple of compiling errors.
First is that 'ViewBag' does not exist in the current context. Can I not use it in .aspx? If not, what is a good substitute?
Second, the .cshtml page had a model declaration:
#model myProject.Models.Navigation
I changed it so that it would work in the .aspx page as follows:
<%# Import Namespace="myProject.Models" %>
I'm still not sure that's a correct substitute, because I could not include the word "Navigation" without getting an error. And now, in the code where I used to have:
#foreach (myProject.Models.Navigationitem item in Model.navigationItems){...
I've replaced it with:
<% foreach (myProject.Models.Navigationitem item in Model.navigationItems){...
And I get this error:
The name 'Model' does not exist in the current context
Apparently, I'm the only guy who has ever gone from razor to aspx, because there's exactly zilch about it online. Appreciate any help.
WebForms don't usually use a ViewBag, which is just a way to make data available to your View in ASP.Net MVC. With WebForms, a nice way to make data available to your "View" (the aspx page containing the HTML) is to expose a property containing that data.
The MVC way might be to set ViewBag.MyValue = "Some Value"; in your Controller, and reference it in your view with <h1>#ViewBag.MyValue</h1>. To do the equivalent in WebForms you would first define a property in your codebehind:
protected string MyValue { get; set; }
Then, set the value somewhere, perhaps in your Page_Load:
protected void Page_Load (object sender, EventArgs e)
{
this.MyValue = "Some Value";
}
And write the value on the page using WebForms syntax:
<h1><%= MyValue %></h1>
For your specific case, you don't seem to actually be using ViewBag. That's ok, you can make objects available as properties also:
protected MyProject.Models.Navigation Model { get; set; }
protected void Page_Load (object sender, EventArgs e)
{
this.Model = SomeMethodThatReturnsModel();
}
With the property defined and the value set, the code you have above for your ASPX should work just fine.
Your page should have
<%# Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of myProject.Models.Navigation)" %>
at the top to specify the model type.
Instead of ViewBag, you can use ViewState, as shown in the example below.
private List<TrimPackage> Packages
{
get
{
return (List<TrimPackage>)ViewState["Packages"];
}
set
{
ViewState["Packages"] = value;
}
}
Related
I have a webusercontrol with a public int property SelectedCatID. I use this control on other pages and in other controls as follows:
<NewStore:LeftMenuLinks runat="server" SelectedCatID="<%#CatIDToSelect%>" />
How do I output cache this control based on SelectedCatID? Everything I've tried fails.
The closest I've gotten is getting it to cache, but it doesn't vary on SelectedCatID leaving the same menu item selected until the cache expires. Without caching, the control works as expected.
I figured out why the VaryByControls approach you used initially does not work. Sadly you edited it out of your question, so my research for this will just have to go into a blog post. Update: the blog post in question: http://tabeokatech.blogspot.be/2014/09/outputcache-on-user-controls.html .
The long and short of it though is that VaryByControls is kinda shorthand for VaryByParams, and does nothing for properties: it only looks at POST values. The fact that it ever worked for properties with a static value appears to be a bug - any string whatsoever in the VaryByControls would have made that part work. The accepted answer to this question is wrong: Vary by control properties using PartialCaching in ASP.NET .
There is no built-in way to vary by control property values.
That wouldn't make sense anyway, because user controls need to be created to have property values, and you want to avoid creating them, instead caching their rendered markup - cached user controls fields are null in code-behind if cached markup is served for them.
This works by injecting a PartialCachingControl into the page instead of the actual user control. This PartialCachingControl checks the cache, and only creates the control if no cached version exists.
As for making it work, I see two options:
If you only have 1 usercontrol per page, you could use the VaryByCustom approach. To make things easy you could write an interface that returns your property value for that page, and implement it on every page that hosts the user control, e.g.:
interface INumberProvider
{
int GetNumber();
}
// and the page:
public partial class _Default : Page, INumberProvider
{
public int GetNumber()
{
return this.SomeNumberPropertyOrWhatever;
}
...
In your Global.asax you cast the current handler to INumberProvider and get the number:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (custom == "INumberProvider")
{
var page = context.CurrentHandler as INumberProvider;
if (page != null)
{
return page.GetNumber().ToString();
}
}
return base.GetVaryByCustomString(context, custom);
}
And in your control you obviously add:
OutputCache Duration="180" VaryByCustom="INumberProvider" VaryByParam="None" Shared="true"
That's if you only have one user control per page, and should be pretty straightforward. If you need more than one user control per page you're out of luck:
Build your own wrapper around your user control by writing a custom WebControl. Add the properties you need, capture the output of the rendered user control, and insert it into HttpContext.Current.Cache with a key that includes the SelectedCatID. Basically write your own custom PartialCachingControl.
There's also option 3:
Decide caching is not that important after all
<%# OutputCache Duration="60" VaryByParam="SelectedCatID" %>
Now store youre <%#CatIDToSelect%> as an a parameter ex ?SelectedCatID=12
Now you're Page or UserControl depending on what you want to cache will output the cache depending on what the Request.Param["SelectedCatID"] is equal to.
You can also do something like this (although not the easiest way)
This goes on the page/usercontrol you want cached:
<%# OutputCache duration="120" varybyparam="None" varybycustom="SelectedCatID" %>
This goes into the Gloabal.asax file:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if(custom == "SelectedCatID")
{
return CatIDToSelect;
}
return String.Empty;
}
I'm late to the party here what with an accepted answer and a 500 point bounty awarded. Still wanted to give my few cents on how this could be achieved.
It can be made to work in the control itself. You can have the control store it's own output in the cache and use the cached version in the Render method if found. I have made a really simple UserControl to test with. The markup looks like this:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="TestUC.ascx.cs"
Inherits="Webforms_Test.UserControls.TestUC" %>
<div>
<asp:Label ID="curTime" runat="server"></asp:Label>
</div>
It just contains a label that is set to DateTime.Now when it is initialized. The code behind looks like this:
public partial class TestUC : System.Web.UI.UserControl
{
private string cachedOutput = null;
public bool RenderFromCache = true; // set to false in containing page if this control needs to be re-rendered
protected void Page_Load(object sender, EventArgs e)
{
cachedOutput = HttpContext.Current.Cache["key"] as string;
if (cachedOutput == null)
{
// not found in cache, do the heavy lifting here to setup the control
curTime.Text = "UC:" + DateTime.Now.ToString("yy-MM-dd hh:mm:ss");
}
}
protected void Page_PreRender(object sender, EventArgs e)
{
if (cachedOutput == null || !RenderFromCache)
{
RenderFromCache = false;
StringBuilder b = new StringBuilder();
HtmlTextWriter h = new HtmlTextWriter(new StringWriter(b));
this.RenderControl(h);
cachedOutput = b.ToString();
HttpContext.Current.Cache.Insert("key", cachedOutput, null, DateTime.UtcNow.AddSeconds(10), TimeSpan.Zero);
RenderFromCache = true;
}
}
protected override void Render(HtmlTextWriter writer)
{
if (!RenderFromCache)
base.Render(writer);
else
writer.Write(cachedOutput);
}
}
In this sample, the control itself checks if its output is found in the cache, and if so the Render method will just write the cached output. If it is not found in the cache, the PreRender method will run the Render method normally and capture the output and store it in the cache.
In your case you would of course need a bit more logic which would check the relevant property on the control and use that to check if a cached version exists.
Disclaimer: This is an extremely simple test control. I have not tried to figure out how to make all of this work with controls that contain event handlers etc. So take it for what it's worth...
What's the "skeleton" code for using CsQuery in the code-behind of a MasterPage in order to modify the HTML output? I need to be able to modify everything in the <body> of the HTML?
I'm hoping to use CsQuery to "touch-up" the HTML output of a Dynamic Data website without rewriting / messing with the default code.
Just looking for sample code specific to MasterPage code-behind? Thanks.
There is an example in the CsQuery project that shows how to do this (which I just made sure was working right!) in the CsQuery.WebFormsApp project.
The core of the usage looks like this. You must override the Render method in a class that inherits Page, and use this instead of Page as the base class for the codebehind in an aspx page:
public class CsQueryPage: System.Web.UI.Page
{
public CQ Doc { get; protected set; }
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
// most of the work is done for you with the
// `CsQuery.WebForms.CreateFromRender` method
var csqContext = WebForms.CreateFromRender(this, base.Render, writer);
// if you are using update panels, this lets you also manipulate that
// HTML, otherwise you don't need the IsAsync part
if (csqContext.IsAsync)
{
foreach (var item in csqContext.AsyncPostbackData) {
Cq_RenderUpdatePanel(item.Dom,item.ID);
}
}
else
{
Doc = csqContext.Dom;
Cq_Render();
}
// writes the altered content to the base HtmlTextWriter
csqContext.Render();
}
protected virtual void Cq_Render()
{ }
protected virtual void Cq_RenderUpdatePanel(CQ doc, string updatePanelId)
{ }
}
The two virtual methods are where you can alter the dom, which is populated in the Doc property of the CsQueryPage object - the intent of leaving them unimplemented here is that each aspx page that inherits CsQueryPage can optionally override them and make changes to the DOM.
To see how this works in practice just pull down the CsQuery code from github and run the example.
The same technique can be used for a UserControl which is also shown in the example. I don't actually show how to do it with MasterPage but it's very much the same-- MasterPage derives from UserControl, you just override it's Render method same as the other situations.
as the title said is SqlDataSource can be shared across different aspx page?
i have exact same sqldatasource on multiple aspx page, is it possible to create one and shared for all the pages.
thanks
Sure. If you really mean shared, as in all pages use the same SqlDataSource, create a Master Page and put the data source in the master. In the codebehind, expose it as a property of the master. From there, you can reference it from any page that uses the Master.
Second option - create a base Page class:
public class MyPage : Page
{
private SqlDataSource mDataSource;
public override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// some code to init your data source - depending on your
// implementation, this may need to be in OnInit instead
}
public SqlDataSource DataSource
{
get { return mDataSource; }
}
}
In this case, any time you create a new page, go to the code behind and change the declaration from implementing Page to MyPage. All pages that implement MyPage will have an SqlDataSource member, though each would have its own instance, so that's not really "sharing" the same SqlDataSource.
Either option gets you where you want to go I think.
UPDATE: Poster requested an example of exposing in as a property of the master:
Given a Master Page with the following:
<asp:SqlDataSource runat="server" ID="mDataSource" ... the rest of your properties .... />
<asp:ContentPlaceHolder runat="server" ID="MainContent"/>
In the code-behind for the master, define the property:
public class SiteMaster : System.Web.UI.MasterPage
{
public SqlDataSource MasterDataSource
{
get { return mDataSource; }
}
// the rest of your master page's codebehind
}
In the pages you define for using your master page, add the following below the #Page declaration:
<%# MasterPage VirtualPath="~/site.master"%>
Now, in the codebehind for that page, you can reference:
protected void Page_Load(object sender, EventArgs e)
{
SqlDataSource ds = this.Master.MasterDataSource;
}
As long as you have as long as you have a <%# MasterType VirtualPath="~/ PATH TO YOUR MASTER" %> in your aspx page, you can reference any properties you expose in the master.
Happy coding.
B
Controls are specific to pages. To share it across pages put it in a UserControl and then expose it through the public property of the UserControl.
If you mean the connection string, the answer is yes. You can put it in a public shared class.
If you mean the connection being open during several pages. No.
You should always close the connection ASAP to avoid memory leaks.
I would like to change dynamically the page theme in a MVC 2 Application.
I found several solutions, but I want to use this method: in the Global.asax, change the current page theme:
protected void Application_PreRequestHandlerExecute(object sender, System.EventArgs e)
{
// cast the current handler to a page object
Page p = HttpContext.Current.Handler as Page;
if (p != null)
{
string strTheme = "Theme1";
if (Convert.ToString(HttpContext.Current.Session["THEME"]) != string.Empty)
strTheme = Convert.ToString(HttpContext.Current.Session["THEME"]);
p.StyleSheetTheme = strTheme;
}
}
But this code always returns null in "p"...
I've also tried a similar code using the event PreRequestHandlerExecute in a HttpModule and the PreInit event of a page, but the code
HttpContext.Current.Handler as Page
always returns null.
Can anyone help me?
Thank you in advance.
I don't use baked in themes, but I do use jQuery UI themes. The way I handle it is in my master page I have logic that gets the current theme from a common viewmodel. The master page is strongly typed to this view model. The common viewmodel properties are updated from user preferences and other sources in a common base controller that all my controllers inherit. I do this in OnActionExecuted. I check if the result is a ViewResult, then cast the result from ViewData on the ActionExecutedContext.Result property to my common view model and set the property. The master page uses the value of the property to build the url for the CSS file.
Model
public abstract class CommonViewModel
{
public string Theme { get; set; }
// ...
}
Controller
public abstract class BaseController : Controller
{
public override void OnActionExecuted( ActionExecutedContext context )
{
if (context.Result is ViewResult)
{
var model = ((ViewResult)context.Result).ViewData.Model as CommonViewModel;
if (model != null)
{
var preferences = ...get from database for current user...
model.Theme = preferences.Theme;
}
}
}
}
Master note it uses a custom HtmlHelper to generate the stylesheet link, you could
do it by hand.
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<...CommonViewModel>" >
<%: Html.Stylesheet( "themes/" + Model.Theme + ".css" ) %>
The technique you are talking about works for standard asp.net, not asp.net MVC. The reason is that (in general) asp.net MVC does not use the web control model that standard asp.net does, and as such there is nothing to interpret the theme setting.
#tvanfosson has some great advice. Just remember that with MVC, you have much more control over things.. but that also means you have to do more work to get some of the features that standard asp.net provides for free. MVC makes many things easier, but this is not one of them.
I am creating an object at server side of an aspx (test.cs) page from a class (asp.net 2.0 C#)
public partial class Vendor_VendorUsedTicketsPopup : System.Web.UI.Page
{
ReportInvoice _objReportInvoice = new ReportInvoice();
protected void Page_Load(object sender, EventArgs e)
{
_objReportInvoice.ReportId = 1;
}
}
as you see above before Page Load I am creating a new ReportInvoice object and on page load I am setting property ReportId to 1
On test.aspx I want to use the ReportId value bu using the _objReportInvoice object like below
<div><% _objReportInvoice.ReportId; %></div>
But when I build the site I get the error
The name '_objReport' does not exist in the current context
I know that I can create a public integer for ReportId above Page_Load and use it on aspx page. That works fine , but I want to use class object properties on aspx page.
What is the way of doing sth like that ?
Thanks...
You need a = sign in there to print it to the page:
<div><%= _objReportInvoice.ReportId; %></div>
However, I would suggest just using a Literal or Label control there and then setting it's text to the ReportID property in the code behind. Inline code like that can make your HTML messy.
Remember that your .ASPX markup page inherits from the codebehind class.
This means that unless you declare your field as protected or public, the .aspx will not have access to your field.
You need to add an access modifier to your field to make it non-private.