ASP.NET Generic UserControl with Html Content Placeholder - c#

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);
}
}

Related

MVC View User Control with JavaScript. How implement popup?

I have a small project which I have a page with a drop-down menu. When the drop-down is selected a user control should popup.
This is an image of what I'm hoping to acomplish:
I have a .ascx file which needs to be loaded. Where would the entry point be added to have this control pop-up?
Here is the implementation. I have started with. This project is not using the code behind and is instead using javascript to implement controls.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<AdministratorConsole.Models.TaskActionController.TaskActionViewModel>" %>
<script language="javascript" type="text/javascript">
<!-- Some Funtions -->
</script>
<div class="main_popup_container">
<div class="task-action-header">Action Type: Hyperlink PDFs</div>
<div class="task-configuration-header">Configuration<span id="HyperlinkCopyTestResults" class="one-line-test-results"></span></div>
I have started a method to try to get this .ascx to pop up from the root folder without any luck. What do I need to get this MVC view user control to popup
and execute?
class PageLoad : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Load control from file
Control load = (Control)Page.LoadControl(#"~/PDFHyperlink.ascx");
}
}
I also have a Interface which has and Execute GetName and TestConfiguration to run some test on user inpute and execute my code to hyperlink PDFs.
public bool Execute(TaskActionConfig action, TaskActionAPI.Task t, out string message)
{
message = GetName();
string configMessage = "Test Message";
bool result = TestConfiguration(action, out configMessage);
// Run hyperlinking tool.
HyperlinkingTool(t);
return true;
}
// **************************GetName**************************
public string GetName()
{
string HyperlinkPDFs = "Hyperlink PDFs";
return HyperlinkPDFs;
}
// **************************TestConfiguration**************************
public bool TestConfiguration(TaskActionConfig action, out string message)
{
Dictionary<string, string> parameter = action.Parameters;
bool result = true;
message = "Config Message";
return result;
}
The method GetName() needs to return the same name as the .ascx file. This will register the .ascx file and load it.

ASP.net get content page to change master page control

Master page:
<form runat="server">
<Scirra:MainMenu runat="server" ID="MainMenu" TopTabSelected="home" SubTabSelected="link2" />
<asp:ContentPlaceHolder id="MainContent" runat="server">
snip
Content page:
Master.MainMenu.TopTabSelected = "forum";
I know I'm probably doing this wrong, but is this possible? I want to change a parameter of that control. It says 'inaccessible due to protection level'.
You should provide a public property f.e MenuTabSelected in your MasterPage that Gets/Sets this property of your Menu.
public string MenuTabSelected {
get { return MainMenu.TopTabSelected; }
set { MainMenu.TopTabSelected = value; }
}
Then you can access it in this way:
((YourMasterPage)Master).MenuTabSelected = "forum";
where YourMasterPage is the type of your MasterPage.
The compiler error is thrown because you want to access a private or protected control from outside of your MasterPage-Class. This would only be allowed if it would be public, what is not recommended. You have more control if you do it the way i suggested :)
find menu items in content page and change its value
protected void Page_Load(object sender, EventArgs e)
{
Menu mainMenu = (Menu)Page.Master.FindControl("NavigationMenu");
MenuItem menuMaterials = mainMenu.FindItem("Materials");
if (menuMaterials.Value == "Materials")
{
menuMaterials.Value = "NO materials";
menuMaterials.Text = "No materials";
}
}

Simple user control for conditionally rendering nested HTML

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!

Why does my ASP.Net custom control render two id attributes on my first child control?

I'm writing a small ASP.Net custom control inheriting from CompositeControl. The control is just a panel containing two subpanels with a label in each subpanel. When rendered, I am seeing in the HTML source that the first child control of my custom control gets two id attributes - the first being the id of the custom control itself and the second being the ID property I assign to the first child control. Why is this happening?
Code:
[ToolboxData("<{0}:MessageBox runat=server></{0}:MessageBox>")]
public class MessageBox : CompositeControl {
private Panel _MessageHeaderContainer = null;
private Label _MessageHeaderLabel = null;
private Panel _MessageDetailsContainer = null;
private Label _MessageDetailsLabel = null;
protected override HtmlTextWriterTag TagKey {
get {
return HtmlTextWriterTag.Div;
}
}
protected override void CreateChildControls() {
// Message header area.
_MessageHeaderContainer = new Panel();
_MessageHeaderContainer.ID = "HeaderContainer";
_MessageHeaderContainer.CssClass = "__MessageBox_Container";
this.Controls.Add(_MessageHeaderContainer);
// Message header text.
_MessageHeaderLabel = new Label();
_MessageHeaderLabel.ID = "HeaderLabel";
_MessageHeaderLabel.Text = "[ Header ]";
_MessageHeaderContainer.Controls.Add(_MessageHeaderLabel);
// Message details area.
_MessageDetailsContainer = new Panel();
_MessageDetailsContainer.ID = "DetailsContainer";
this.Controls.Add(_MessageDetailsContainer);
// Message details text.
_MessageDetailsLabel = new Label();
_MessageDetailsLabel.ID = "DetailsLabel";
_MessageDetailsLabel.Text = "[ Details ]";
_MessageDetailsContainer.Controls.Add(_MessageDetailsLabel);
}
protected override void RenderContents(HtmlTextWriter output) {
AddAttributesToRender(output);
// Render the box.
_MessageHeaderContainer.RenderControl(output);
_MessageDetailsContainer.RenderControl(output);
}
}
Usage in ASPX page:
<cc:MessageBox ID="ctlMessageBox" runat="server" />
HTML output:
<div id="ctl00_ctl00_ctlMessageBox">
<div id="ctl00_ctl00_ctlMessageBox" id="ctl00_ctl00_ctlMessageBox_HeaderContainer" class="__MessageBox_Container">
<span id="ctl00_ctl00_ctlMessageBox_HeaderLabel">[ Header ]</span>
</div><div id="ctl00_ctl00_ctlMessageBox_DetailsContainer">
<span id="ctl00_ctl00_ctlMessageBox_DetailsLabel">[ Details ]</span>
</div>
</div>
You can fix this by removing the RenderContents method entirely - don't replace it with anything. By default, composite controls render all their child controls. If you do this, your control produces clean markup:
<div id="test">
<div id="test_HeaderContainer" class="__MessageBox_Container">
<span id="test_HeaderLabel">[ Header ]</span>
</div>
<div id="test_DetailsContainer">
<span id="test_DetailsLabel">[ Details ]</span>
</div>
</div>
As you've noted, the AddAttributesToRender method is the culprit - it's a convenience method renders all the top-level control's attributes (whether the current context is the top-level control or not). The only reason you would call this is if you were taking full control of the rendering process yourself and wanted to render all the attributes for the top-level control in a single statement.
This is the ID ASP.NET assigns the Custom Control as a whole. If you wrap your Custom Control in a Div or Span (within the Custom Control code itself, not in the markup) the ID will be assigned to that instead of your first inner "control".
Something like this:
protected override void Render(HtmlTextWriter writer)
{
AddAttributesToRender(writer);
//must render tag or first inner control will get two IDs
writer.RenderBeginTag(HtmlTextWriterTag.Span);
//render child controls here...
writer.RenderEndTag();
}

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