Simple user control for conditionally rendering nested HTML - c#

What I would like to do, is be able to pass two attributes to a user control, a ListName and a Permission, like so:
<uc:check id="uc" List="Shared Documents" Permission="OpenItems" runat="server">
<!-- have some HTML content here that is rendered if the permission is true -->
</uc:check>
Then in the actual check user control, have something similar to:
<%# Control language="C#" ClassName="check" %>
<%
// determine permission magic placeholder
if (DoesUserHavePermissions(perm))
{
// render nested HTML content
}
else
{
// abort rendering as to not show nested HTML content
}
%>
I have read the page on creating a templated control on MSDN, and while that would work - it really seems to be a bit overkill for what I am trying to do. Is there a control that already renders content based on a boolean expression or a simpler template example?
http://msdn.microsoft.com/en-us/library/36574bf6.aspx
Update:
The following code can be used in the ascx to model a very simple version of this:
<%# Control Language="C#" ClassName="PermissionCheck" %>
<%# Import Namespace="System.ComponentModel" %>
<script runat="server">
void Page_Init()
{
if (Allowed != null)
{
Panel container = new Panel();
Allowed.InstantiateIn(container);
PermissionBasedMessage.Controls.Add(container);
}
}
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate Allowed { get; set; }
</script>
<asp:Placeholder runat="server" ID="PermissionBasedMessage" />
Note: I oversimplified the check in the Page_Init method for this sample code. Additional logic checks can be added as needed.
And reference it in the calling HTML page:
<%# Register src="PermissionCheck.ascx" tagname="PermissionCheck" tagprefix="uc1" %>
<uc1:PermissionCheck ID="PermissionCheck1" runat="server">
<Allowed>Allowed Access</Allowed>
</uc1:PermissionCheck>

You could create a custom control instead of a user control: derive from the asp.net panel, add your two properties, then only render the control if the user has the required permission. E.g. something like this:
The control (put this in App_Code for example):
namespace MyControls
{
public class MyPanel : Panel
{
public string Permission { get; set; }
public string List { get; set; }
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
if (UserHasPermission()) base.Render(writer);
}
}
}
Using the control:
<%# Page ... %>
<%# Register Namespace="MyControls" TagPrefix="mc" %>
<html>
...
<mc:MyPanel runat="server" List="Shared Documents" Permission="OpenItems">
put content and/or other controls here
</mc:MyPanel>
...

Why don't you extend the LiteralControl, add properties for your settings, then render html to the .Value of the LieralControl? Seems pretty simple and a lot less of a headache than using Templated controls

The other answers are good for the generic form of your question, but for checking permissions SPSecurityTrimmedControl might do what you need.

Wrap your content with a place holder control and set the control's visibility to true or false (controls that have .Visible = false won't render any html)
<asp:PlaceHolder id="phWrapper" runat="server">
...
</asp:PlaceHolder>
Then in your code-behind set phWrapper.Visible = DoesUserHavePermissions(perm);
Hope that helps!

Related

Passing <%=value%> in user control (.ascx)

This is the ascx control in .aspx
<Menu:MNU ID="PMPHeaderMenu" runat="server" HiLiter="<%=h%>"></Menu:MNU>
in aspx.cs I have
public int h = 1;
....
....
h = 5;
in ascx.cs I have the HiLiter Property
public string HiLiter { get; set; }
When I debug I get the value as <%=h%> for HiLiter when I expect it to be 5.
How will I pass the server side set value to the user control?
You can't use <%=%> for controls with runat="server" for setting properties, <%=%> is similar like Response.Write.
<%# %> (Data-binding expressions) can be used to fill control properties, but the control should be inside a data-binding container like GridView, DetailsView, FormView and Repeater.
In your case you should set the value of the property from the code behind (aspx.cs) page like following.
PMPHeaderMenu.HiLiter = h;
this.DataBind();

ASP.NET Generic UserControl with Html Content Placeholder

This question might have already been asked, but I could not find any references to it so I apologize if it seems like a duplicate question.
What I'm trying to do is create a generic DialogBox as an ASP.NET UserControl; which would contain all the script required to create the dialog using jQuery. The dialog has a fixed set of buttons, but I'd like to be able to let the user define the content when they create the dialog. Suppose this is the markup for the user control:
<head>
<script type="text/javascript">
// jQuery script to create the dialog
</script>
</head>
<body>
<div runat="server" id="divContainer">
<!--Html Content Placeholder. What goes here?-->
</div>
</body>
And the code-behind:
[ParseChildren(true, "Contents")]
public partial class UCDialogBox : ExtendedUserControl
{
protected void Page_Load(object sender, EventArgs e)
{
Page.DataBind();
}
public List<Control> Contents { get; set; }
public DialogType Type { get; set; }
public string Title { get; set; }
}
And on the actual page after registering the control, I would like to be able to do something like this:
<uc:DialogBox runat="server">
<div>
<label>Hello World</label>
</div>
</uc:DialogBox>
The problem with this is, List<Control> only allows for ASP.NET controls. Normal HTML controls (such as what I have above) won't work.
Question 1: What type should I use to allow any HTML control to be nested inside the user control? I tried System.Web.UI.HtmlControls.HtmlControl but that didn't work either (ASP.NET says The element 'div' cannot be nested within the element 'dialogbox').
Question 2 What would I put as an HTML content placeholder on the user control which can be bound to the Contents property on the code behind? Something like
<SomePlaceholderControl DataSource="<%# Contents %>" />
Any help is appreciated.
Oddly enough, putting HTML controls inside the body of the user control doesn't cause a run-time error. In fact, the controls come across just fine. I guess it's just the designer that whines about it.
As for the placeholder, I didn't have to use any specific control; I simply used an HtmlTextWriter to render the controls into a well-formatted HTML string inside a method that gets called in the markup:
<div runat="server" id="divContainer">
<%# RenderContents() %>
</div>
And the code-behind method:
public string RenderContents()
{
StringWriter writer = new StringWriter();
HtmlTextWriter htmlWriter = new HtmlTextWriter(writer);
foreach (var control in Contents)
{
control.RenderControl(htmlWriter);
}
return writer.ToString();
}
It works just fine.
I am not sure whether this is the correct approach, I have added a ITemplate inside the Dialog control and placed a HtmlGenericControl container which is Div control with runat = "server".
Now you can add the html controls and text inside this container.
<cc1:UCDialogBox ID="dialog" runat="server">
<HtmlPlaceHolder>
<div runat="server">
<h1>Title</h1>
<h2>Description</h2>
<div>Modal Content</div>
</div>
</HtmlPlaceHolder>
</cc1:UCDialogBox>
In custom server control you can get the container div content as innerHtml or innerText
public class UCDialogBox : WebControl, INamingContainer
{
private ITemplate htmlPlaceHolder = null;
[
Browsable(false),
DefaultValue(null),
Description("Add your html contorls"),
PersistenceMode(PersistenceMode.InnerProperty)
]
public virtual ITemplate HtmlPlaceHolder
{
get
{
return htmlPlaceHolder;
}
set
{
htmlPlaceHolder = value;
}
}
protected override void Render(HtmlTextWriter output)
{
HtmlGenericControl placeholder = new HtmlGenericControl();
htmlPlaceHolder.InstantiateIn(placeholder);
var html = placeholder.Controls[1] as System.Web.UI.HtmlControls.HtmlGenericControl;
var result = html.InnerHtml.Replace("\r", "").Replace("\n", "").Trim();
output.Write(result);
}
}

How do I get access to master page properties via user control markup?

I have been searching the internet and most I find resolves the issue of accessing the master page properties from the user control's code behind. But I am unable to find a solution where the user control can have access to the master page's properties within the markup.
Background:
The master page dynamically adds user control onto the page.
The master page has two properties which the user control needs to access via markup.
Here is some code to represent my problem:
Master page's code behind properties:
public IModule Module
{
get
{
return MyContext.Current.Module;
}
}
public IDictionary<string, object> Arguments
{
get
{
return MyContext.Current.Arguments;
}
}
Master page dynamically adds to control in code behind (it HAS to be dynamically added in master page's code behind):
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (!(Page is VehicleForm) && !(Page is VsrManageForm) && !(Page is VpbManageForm))
{
MenuTab view = (MenuTab)this.LoadView(plhMenu, "~/Controls/MenuTab.ascx", "", MyContext.Current.Module);
}
}
User control's markup:
<web:FlowLink class="tools__lnk" arguments="<%# Arguments %>" id="flowLink1" runat="server" contextmodule='<%# Module %>' flowcall="FavouritesView" title="" rel="nofollow" ClientIDMode="Omitted">Shortlist</web:FlowLink>
<web:FlowLink class="tools__lnk" arguments="<%# Arguments %>" id="flowLink2" runat="server" contextmodule='<%# Module %>' flowcall="CompareView" title="" rel="nofollow" ClientIDMode="Omitted">Compare</web:FlowLink>
<web:FlowLink class="tools__lnk" arguments="<%# Arguments %>" id="flowLink5" runat="server" contextmodule='<%# Module %>' flowcall="UserView" title="" rel="nofollow" ClientIDMode="Omitted">Account</web:FlowLink>
Error:
Compiler Error Message: CS0103: The name 'Arguments' does not exist in the current context
Question:
How do I access <%# Arguments %> and <%# Module %> master page properties from the user control?
It might be possbile (have not tested it though) to do something like this:
arguments="<%# ((MasterPageType)this.Page.Master).Arguments %>"
Although it does not look right. You might want to redesign the way you control gets the data. Or atthe very least do the same somewhere in code behind and verify whether a current masterpage is of an expected type.
Update. The final solution that OP used incorporated ideas above, and resulted in having properties like below declared in the control:
public IDictionary<string, object> Arguments
{
get
{
MasterPageType master = this.Page.Master as MasterPageType;
if (master != null)
{
return master.Arguments;
}
else
{
return null;
}
}
}

Execute html from database in ASP.NET page

Any suggestions to load a html page saved in the database?
html:
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>xxx</title>
</head>
<body>
<form id="Form1" runat="server" method="post">
<ext:ResourceManager ID="ResourceManager1" runat="server" />
</form>
</body>
codebehind:
protected override void OnInit(EventArgs e)
{
this.IniciarFormulario();
using (ServicoECMClient proxy = new ServicoECMClient())
{
tipoDocumento = proxy.ObterTipoDocumento(int.Parse(tipoDocumentoID));
}
if (tipoDocumento == null)
{
throw new ApplicationException();
}
this.Page.Header.InnerHtml = tipoDocumento.estilo; //css
this.Page.Form.InnerHtml = tipoDocumento.form; // form
base.OnInit(e);
}
I can not retrieve the form values.
Look:
foreach (System.Web.UI.Control controle in this.Form1.Controls)
{
if (controle.GetType().Name == "HtmlInputText" || controle.GetType().Name == "HtmlInputSelect"
|| controle.GetType().Name == "HtmlInputRadio" || controle.GetType().Name == "HtmlInputTextCheckbox")
{
if (!string.IsNullOrEmpty(this.Request[controle.ClientID]))
{
documento_indice documentoIndice = new documento_indice();
documentoIndice.id_indice = int.Parse(controle.ClientID.Split('_')[1]);
documentoIndice.valor = this.Request[controle.ClientID];
documentoIndice.timestamp = DateTime.Now;
documentos_indices.Add(documentoIndice);
}
}
}
Controls is empty. => this.Form1.Controls
Any Suggestion?
There's another better way to do?
Thanks.
The short answer is, yes you can get this functionality to work. We provide all of our customer-specific customizations in a manner similar to this.
The long answer is that it will require some restructuring of your application and HTML.
The easiest way that we found to implement this is through UserControls. The basic approach is:
1) Create your page content that is stored in the DB as a UserControl, i.e.
<%# Control Language="vb" AutoEventWireup="false" %>
<input id="txtTest" type="text" runat="server" />
2) When you extract it from the DB, store it in a file on disk with an ascx extension (say content.ascx for now).
3) Modify your main page to add a div that runs at the server that the ascx will be loaded into:
<div id="divContent" runat="server">
</div>
4) In page init, load the control into the div and initialize it:
Dim oControl As Control
' Load a user control
oControl = Me.LoadControl("content.ascx")
If oControl IsNot Nothing Then
' Ensure viewstate is enabled
oControl.EnableViewState = True
' Set properties as required
oControl.ID = "ContentControl"
' Clear the existing control(s) from the content container
Me.divContent.Controls.Clear()
' And add the new one
Me.divContent.Controls.Add(oControl)
End If
5) Access the collection of controls contained in the div control.
Note that on a page postback the controls will not have content loaded into them until the page load event is fired due to the standard page lifecycle.
I have verified that codebehind is not required and this works correctly exactly as described above.
Unless you add the controls to the controls collection by hand (a tricky business) you're going to need to read your form values the old-fashioned way:
Request.Form["txtSomething"]
That means you can get the string values contained in your controls if you know their names, but not much else.

Content is not allowed between the opening and closing tags for user control

I want to build a user control suppose MyDiv.ascx.
This control renders the div tag and do few more code behind stuff like adding few attributes etc which is not a matter of concern here.
The problem is I want text between the opening and closing tags of the user control. Eg:
The text goes here with some other HTML tags.
So when do something like this I get a parsing error while running the website.
Also VS2008 warns me by saying "Content is not allowed between the opening and closing tags for element MyDiv".
Question 1: Can I do something like this i.e. text/markup between opening
and closing tags of a user control?
Question 2: If yes, how
The suggested solutions did not work for me. I found the following solutions:
Either make your user control inherit from Panel instead of only UserControl, or if you have more than one content like in my case, make your content fields be PlaceHolders instead of simple Controls.
The [PersistenceMode(PersistenceMode.InnerProperty)] is added to avoid XHTML validation warning.
public partial class DrawerControl : UserControl
{
[PersistenceMode(PersistenceMode.InnerProperty)]
public PlaceHolder BodyContent { get; set; }
[PersistenceMode(PersistenceMode.InnerProperty)]
public PlaceHolder GripContent { get; set; }
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
phBodyContent.Controls.Add(BodyContent);
phGripContent.Controls.Add(GripContent);
}
}
phBodyContentand phGripContent being PlaceHolders.
This way I can use my control with any content in ASPX:
<local:Drawer ID="ctlDrawer" runat="server">
<BodyContent>
<!--Insert any ASP content here-->
</BodyContent>
<GripContent>
<!--Insert any ASP content here-->
</GripContent>
</local:Drawer>
I believe you just need to apply a couple of attributes to the control:
[ParseChildren(false)]
[PersistChildren(true)]
public class MyDiv : UserControl
{
...
You may then need to override AddedControl - I'm not sure.
Put it this way - that's what works for the one and only user control I've ever written :)
I also wanted to create a custom control with "innerHtml". This is what I ended up with (based partially on some of the earlier answers/comments)...
div.ascx.cs:
[ParseChildren(true, "Text")] //Store inner content in Text property
public partial class div : System.Web.UI.UserControl
{
public string Text { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
litText.Text = Text; //Render it however you want
}
}
div.ascx:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="div.ascx.cs" Inherits="TestApp.Controls.div" %>
<div>
<asp:Literal ID="litText" runat="server" />
</div>
Test page:
<%# register src="~/Controls/div.ascx" tagname="div" tagprefix="uc" %>
<uc:div ID="div1" runat="server">Test data</uc:div>
I found this:
ASP.Net: User control with content area, it's clearly possible but I need some details
Works like a charm, but I wish I could suppress the design time message, content is not allowed between opening and closing tags, but it works at run time.
[ParseChildren(true, "Content")]
[PersistChildren(false)]
public partial class CollapsiblePanelControl : UserControl
{
private Control content;
// add the content
this.MainContent.Controls.Add(content);
// if this is not a post back
if (!this.IsPostBack)
{
// set to true;
this.Expanded = true;
}
}
The markup is like this:
<asp:Panel ID="CollapsiblePanelMainPanel" runat="server" CssClass="collapsiblepanel">
<asp:Panel ID="CollapsibleHeaderPanel" runat="server" CssClass="collapsibleheaderpanel">
<asp:ImageButton ID="CollapseButton" ImageUrl="~/Images/BlueArrowDown.png" runat="server" OnClick="ExpandButton_Click" CssClass="expandbutton" />
<asp:Label ID="CollapsiblePanelHeaderLabel" runat="server" Text="Collapsed" CssClass="collapsiblelabel"></asp:Label>
</asp:Panel>
<asp:Panel ID="MainContent" runat="server">
</asp:Panel>
</asp:Panel>
And then in the client:
<dc:CollapsiblePanelControl ID="CheckOnMePanel" runat="server" CssClass="checkonmepanel" EnableViewState="true"
CollapsedHeight="20px" ExpandedHeight="300px" Expanded="true" HeaderText="Check On Me Email Service" >
<Content>
...[Your Content Goes Here]
</Content>
</dc:CollapsiblePanelControl>
Add a Text property to your control and linked this text property to a label run at server that will be between the opening and closing div.
You might want to be careful, what if you put a server control in the content area...
You might just want to make a control inherit from a panel and override any methods you need to adjust? Might be easier or harder depending on what you need to customize
public class MyDiv : Panel
{
}

Categories

Resources