So, I've been working on this project for a few days now, and have been unable to resolve the issue of getting intellisense support for my custom-defined inner properties for a user control (ascx, mind you).
I have seen the solution to this (using server controls, .cs mind you) many times. Spelled out in this article very well. Everything works for me while using ascx controls except intellisense.
Here's the outline of my code:
[PersistChildren(true)]
[ParseChildren(typeof(BreadCrumbItem))]
[ControlBuilder(typeof(BreadCrumbItem))]
public partial class styledcontrols_buttons_BreadCrumb : System.Web.UI.UserControl
{
...
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public List<BreadCrumbItem> BreadCrumbItems
{
get { return _breadCrumbItems; }
set { _breadCrumbItems = value; }
}
...
protected override void AddParsedSubObject(object obj)
{
base.AddParsedSubObject(obj);
if (obj is BreadCrumbItem)
BreadCrumbItems.Add(obj as BreadCrumbItem);
}
...
public class BreadCrumbItem : ControlBuilder
{
public string Text { get; set; }
public string NavigateURL { get; set; }
public override Type GetChildControlType(string tagName, System.Collections.IDictionary attribs)
{
if (String.Compare(tagName, "BreadCrumbItem", true) == 0)
{
return typeof(BreadCrumbItem);
}
return null;
}
}
}
Here's my mark up (which works fine, just no intellisense on the child object declarations):
<%# Register src="../styledcontrols/buttons/BreadCrumb.ascx" tagname="BreadCrumb" tagprefix="uc1" %>
...
<uc1:BreadCrumb ID="BreadCrumb1" runat="server" BreadCrumbTitleText="Current Page">
<BreadCrumbItem Text="Home Page" NavigateURL="~/test/breadcrumbtest.aspx?iwentsomewhere=1" />
<BreadCrumbItem Text="Secondary Page" NavigateURL="~/test/breadcrumbtest.aspx?iwentsomewhere=1" />
</uc1:BreadCrumb>
I think the issue lies with how the intellisense engine traverses supporting classes. All the working examples I see of this are not ascx, but Web Server Controls (cs, in a compiled assembly).
If anyone could shed some light on how to accomplish this with ascx controls, I'd appreciate it.
I have solved this before by removing all the user controls and directives from the page and then switching the markup to design view. You then drag the ascx file from the solution explorer to the design window. If you swich back to markup view suddenly intelisense picks up all the properties. I am sure there is a better way but i have never found one that works.
Related
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.
I'm using ASP.Net for web development. I've situation here. say I've many HTML/ASP.Net controls on my page, and I've to set visibility of each control based on Logged-in user's role. for this I'm doing as follows:
Storing each Function-Name and Role combination is DB
During render, checking for role permission for each HTML/ASP.Net control.
e.g: Displaying something like this:
<asp:Label runat="server" id="Lbl1" visible='<%=CheckVisibility("Display","Admin")%>'>
public bool CheckVisibility(string FunctionName, string RoleName){
// checks for db entry and returns bool value
}
Problem is, I've to do this for all the Controls. Is there any other optimized approach available to do this? please help me
Given only your description of what you're trying to solve, I would suggest you create your own controls which inherit from the built-in ones and add some properties to them. Then the controls can do the visibility check themselves. Something like
namespace StackOverflowTest
{
public class Label : System.Web.UI.WebControls.Label
{
public string DisplayRoles { get; set; }
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
Visible = PermissionHelper.CheckPermission("Display", DisplayRoles.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
}
}
public static class PermissionHelper
{
public static bool CheckPermission(string function, string[] allowedRoles)
{
// TODO: hit some sort of cache, so you don't make a bajillion queries to the DB
return true;
}
}
}
And then if you put this in your web.config (sorry, can't figure out how to do proper XML formatting here): <system.web>
<pages>
<controls>
<add tagPrefix="sot" assembly="StackOverflowTest" namespace="StackOverflowTest"/>
</controls>
</pages>
</system.web>
You can add this to your markup:
<sot:Label ID="AdminLabel" runat="server" Text="Bind this in OnLoad/!IsPostback or something" DisplayRoles="Admin,Management" />
This is just one of the many ways you can do this. It all depends on the requirements, of course. But I'm pretty sure you'll need your own classes for controls to have the possibility of making it manageable.
C# code:
public enum previlageType
{
superAdminPrevilages=1,
partnerPrevilages = 2,
dealerPrevilages = 3,
customerPrevilages=4
}
if ((previlageType)Enum.Parse(typeof(previlageType), Session["previlageType"].ToString())== previlageType.partnerPrevilages)
{
accordion.Visible = false;
}
ASP code:
<div id="accordion" runat="server">
Hello World, I'l be Hidden if a person with partner previlages is logged in. BUt will be visible if superadmin or any person except Partner logs in.
</div>
Hope it helps
If you're using a master page, you could get all the child page controls on load (get contentplaceholders, then get contentplaceholder controls).
Then when you have all the control names, do a lookup on your table and set its visibility to false if needed.
(below is vb but the translation should be easy.)
For Each cp As String In Me.ContentPlaceHolders
For Each ctl As Control In Me.FindControl(cp).Controls
If adminonly.Contains(ctl.ID) Then
ctl.Visible = False
End If
Next
Next
On my current project I need to add a functionality that allows the user to view a thumbnail of their uploaded PDF. I've found a handy component that achieves this (the basic version is free, but it's enough for my current needs). Anyways, the control is pretty outdated (2010), therefore there doesn't seem to be MVC support. On the demos they depict usage of the control as such:
The View's Markup:
<form method="post" runat="server" enctype="multipart/form-data">
<asp:Panel ID="thumbnailsPanel" runat="server" />
</form>
The thumbnail control is instantiated via code, the byte array which represents the thumbnail is passed to the control and the control is added to thumbnailsPanel
<script runat="server">
protected void DisplayThumbs_Click( object sender, System.EventArgs e )
{
Thumbnail thumbnail = new Thumbnail();
thumbnail.SessionKey = sessionID;
thumbnail.Index = i;
thumbnailsPanel.Controls.Add( thumbnail );
}
</script>
Given that I can't declare a Thumbnail control in my razor view, how would I used this control in MVC? I've spent a few hours trying to make this control MVC friendly to no avail, the best I've come up with is to include a .ASPX view (not.cshtml) in my project and render the Thumbnail control on that view. Obviously this is not desirable.
So how would you go about using a ASPX server controls in MVC? Is the idea a bad one altogether and should not be practised?
I worked around it in a project of mine by reimplementing the control as a HtmlHelper. Assuming the control isn't too complicated then it should work for you too. Do this:
Dump the Control's source using Reflector
Massage the source so it actually compiles (as source from Reflector doesn't usually compile straight away)
Identify what state the control has. Convert the state from member properties into members of its own new ViewModel class.
Find the Render method and convert it to a HtmlHelper that uses ViewContext.Writer
For example:
public class FooControl : Control {
public String Message { get; set; }
public override void Render(HtmlTextWriter wtr) {
wtr.WriteLine("<p>");
wtr.WriteLine( message );
wtr.WriteLine("</p>");
}
}
Becomes this:
public class FooViewModel {
public String Message { get; set; }
}
// This method should exist in a static Extensions class for HtmlHelper
public static void Foo(this HtmlHelper html, FooViewModel model) {
HtmlTextWriter wtr = html.ViewContext.Writer;
wtr.WriteLine("<p>");
wtr.WriteLine( model.Message );
wtr.WriteLine("</p>");
}
I've written a templated user control, MinimalTemplate, which currently does nothing other than render the HTML passed into its "ContentTemplate" placeholder. I want Visual Studio 2008 to have the same intellisense features for MinimalTemplate that it has for built-in templated controls such as Repeater.
Possibly related: I can manually type out my ContentTemplate tags, and it will build and run properly, but I get a validation error. I have already deleted the contents of my ReflectedSchemas folder, as suggested in this question.
Complete source for Minimal Template:
MinimalTemplate.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="MinimalTemplate.ascx.cs" Inherits="MyProject.MinimalTemplate" %>
<asp:placeholder runat=server id="contentPlaceHolder" />
MinimalTemplate.ascx.cs
using System.Web.UI;
namespace MyProject
{
[ParseChildren(false)]
public partial class MinimalTemplate : System.Web.UI.UserControl
{
[TemplateContainer(typeof(MessageContainer))]
[TemplateInstance(TemplateInstance.Single)]
public ITemplate ContentTemplate
{ get; set; }
void Page_Init()
{
if (ContentTemplate != null)
{
MessageContainer container = new MessageContainer();
ContentTemplate.InstantiateIn(container);
contentPlaceHolder.Controls.Add(container);
}
}
public class MessageContainer : Control, INamingContainer { }
}
}
What changes can I make to my MinimalTemplate code so that Visual Studio will validate and autocomplete its ContentTemplate tag?
Related.
Add [PersistenceMode(PersistenceMode.InnerProperty)] to ContentTemplate's attribute list. After adding it and rebuilding, the validation error disappeared and "ContentTemplate" appeared as expected in the Intellisense dropdown.
During my investigation, I'm certain I tried adding this property two or three times to no effect, so I expect the VS validator is a bit flaky. It smacks of voodoo programming, but do a Clean/Rebuild All and wait a few seconds before seeing whether the validation error persists.
(Also, you don't need the ParseChildren attribute for this control.)
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.