Nesting a webpart within another webpart - c#

Does anyone know if there is a way within Kentico CMS to nest a webpart within another webpart? I did a little research and I don't see many results on the topic so it seems the short answer is no, but maybe there is a workaround? On the project I am working on it is a requirement that the content author is able to place a hamburger menu on the page and then add other content within that hamburger menu via drag and drop. How can this be achieved within Kentico?
Thanks.

Typically a web part consists of user controls and not nested web parts. Take a look at the CMSRepeater for instance. It sets properties of a user control created by Kentico.
For what you're explaining it seems like you want a custom web part with widget zones in it. Those widget zones will allow you to drag and drop content in place (what content I have no idea really because you can't drag and drop content in Kentico, only areas/widgets).
For navigation, you might want to look at creating it based on the content tree but maybe a hamburger menu is not for navigation in your case.

The solution was to add a widget zone within the web-part.

Circling back to this post. Although adding a <cms:CMSEditableRegion /> tag to the page did indeed allow me to nest a Widget within a Webpart zone, it seemed a bit unnecessary for the user to have to switch between the design tab and page tab in order to achieve adding a nested component, not to mention having to register every webpart as a widget; quite unnecessary. I noticed Kentico's built in Layout webpart allowed for nested webparts so I looked at the code and was finally able to figure out how to implemented nesting a webpart within another webpart!
Ensure your webpart's code-behind inherits from CMSAbstractLayoutWebPart
Add the following method to your code-behind:
protected override void PrepareLayout()
{
StartLayout();
Append("<div");
Append(" style=\"width: ", "100%", "\"");
if (IsDesign)
{
Append(" id=\"", ShortClientID, "_env\">");
Append("<table class=\"LayoutTable\" cellspacing=\"0\" style=\"width: 100%;\">");
if (ViewModeIsDesign())
{
Append("<tr><td class=\"LayoutHeader\" colspan=\"2\">");
// Add header container
AddHeaderContainer();
Append("</td></tr>");
}
Append("<tr><td id=\"", ShortClientID, "_info\" style=\"width: 100%;\">");
}
else
{
Append(">");
}
// Add the tabs
var acc = new CMSAccordion();
acc.ID = ID + "acc";
AddControl(acc);
if (IsDesign)
{
Append("</td>");
if (AllowDesignMode)
{
// Width resizer
Append("<td class=\"HorizontalResizer\" onmousedown=\"" + GetHorizontalResizerScript("env", "Width", false, "info") + " return false;\"> </td>");
}
Append("</tr>");
}
// Pane headers
string[] headers = TextHelper.EnsureLineEndings("HEADER", "\n").Split('\n');
// Create new pane
var pane = new CMSAccordionPane();
pane.ID = ID + "pane";
pane.Header = new TextTransformationTemplate(string.Empty);
acc.Panes.Add(pane);
pane.WebPartZone = AddZone(ID + "-ContentArea", ID + "-ContentArea", pane.ContentContainer);
acc.SelectedIndex = 1;
if (IsDesign)
{
if (AllowDesignMode)
{
Append("<tr><td class=\"LayoutFooter cms-bootstrap\" colspan=\"2\"><div class=\"LayoutFooterContent\">");
// Pane actions
Append("<div class=\"LayoutLeftActions\">");
Append("</div></div></td></tr>");
}
Append("</table>");
}
Append("</div>");
FinishLayout();
}

Related

Reuse umbraco grid with custom viewmodel

I'm new to the new umbraco grid functionality and I have a question about it.
I have a page with an article where I use the grid functionality to show some image and text besides that image. This is working fine.
In the properties of that page I have put a checkbox for saying that the article is for sale or not, when checked the product should automatically appear on a for sale page.
I can get the product that is for sale on that page, but now I also want to show the image and text on that page. I've created a List with a custom viewmodel to hold the content for every product that is for sale. The problem is with providing the JObject to the GetGridHtml function. This function wants a string and I'm providing the property which results in an error.
What is the best way to get the content for the grid to show on that separate page?
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
var home = CurrentPage.Site();
List<ForSaleItemsViewModel> forSaleItemsVM = new List<ForSaleItemsViewModel>();
if(home.Children.Any())
{
foreach(var childPage in home.Children)
{
if(childPage.Children.Any())
{
foreach(var child in childPage.Children)
{
if(child.HasProperty("ForSale") && child.ForSale)
{
forSaleItemsVM.Add(new ForSaleItemsViewModel
{
ID = 1,
Content = child.Content,
Sold = child.Sold
});
}
}
}
}
}
}
<div class="container">
#foreach(var item in forSaleItemsVM)
{
if (item.Sold)
{
<span>SOLD!</span>
}
CurrentPage.GetGridHtml(item.Content.ToString(), "bootstrap3");
}
</div>
According to the Umbraco forum it is not possible to do this. I got this answer:
If you see the documentation for best pratices for the grid layout
https://our.umbraco.org/Documentation/Using-Umbraco/Backoffice-Overview/Property-Editors/Built-in-Property-Editors-v7/Grid-Layout-Best-Practices
there is a chapter about the limitations for the grid layout
https://our.umbraco.org/Documentation/Using-Umbraco/Backoffice-Overview/Property-Editors/Built-in-Property-Editors-v7/Grid-Layout-Best-Practices#Limitations.
And of the limitions of using the grid layout is that it´s not for
reused content.
It says - There is no managed api to drill into the grid content and
target specific cell content - so a grid layout is not a recommended
storage of reusable content - it simply wasn't designed for this
scenario. If you wish to reuse content in multiple pages, it is still
recommended that you store these pieces of content as seperate content
nodes, so they can be stored, cached and queried as usual.

Disabling ASP.net treeview checkboxes

How would you guys conditionally disable checkboxes in an asp treeview?
For instance, if an application user does not have a certain permission, disable that permission entry checkbox in a permissions treeview.
Here's what i'm looking for, this is the equivaqlent in a winform app (the checkboxes are disabled where the text is grayed out):
I saw other solutions where the click event on the checkboxes is intercepted and ignored. I would prefer a solution where the checkboxes are simply set to disabled.
I'm looking for a C# solution but will be happy with a C#/Javascript solution.
Thanks!
Ok, found a fairly clean solution to this:
in code-behind:
TreeNode newNode = new TreeNode(permission.ToString());
newNode.SelectAction = TreeNodeSelectAction.None; // no Link
if (shouldDisableCheckbox)
{
// Set a class so disabled nodes can be formatted thru CSS
// and be identifiable as disabled in Javascript.
newNode.Text = "<span class=disabledTreeviewNode>" + newNode.Text +"</span>";
}
nodes.Add (newNode);
in Javascript, scan all treeview nodes for those that have that className and disable the checkboxes associated to them:
// Called via a startup script created in Code Behind.
// Disables all treeview checkboxes that have a text with a class=disabledTreeviewNode.
// treeviewID is the ClientID of the treeView
function DisableCheckBoxes(treeviewID)
{
TREEVIEW_ID = treeviewID;
var treeView = document.getElementById(TREEVIEW_ID);
if (treeView)
{
var childCheckBoxes = treeView.getElementsByTagName("input");
for (var i = 0; i < childCheckBoxes.length; i++)
{
var textSpan = GetCheckBoxTextSpan(childCheckBoxes[i]);
if (textSpan.firstChild)
if (textSpan.firstChild.className == "disabledTreeviewNode")
childCheckBoxes[i].disabled = true;
}
}
}
function GetCheckBoxTextSpan(checkBox)
{
// Set label text to node name
var parentDiv = checkBox.parentNode;
var nodeSpan = parentDiv.getElementsByTagName("span");
return nodeSpan[0];
}
Sadly, I don't have enough reputation to be able to comment directly on zukanta's answer which is a bit of a pain, but I had to make a modification in the javascript to make this work:
if (textSpan.firstChild)
if (textSpan.className == "disabledTreeviewNode")
childCheckBoxes[i].disabled = true;
i.e. replace textSpan.firstChild.ClassName with textSpan.ClassName
Also worth pointing out that the JavaScript will error out unless all of your tree nodes in the treeview that you are addressing have a
<span></span>
in them. You get a null reference at
if (textSpan.firstChild)
and no subsequent nodes are processed.
I got around this point by adding a span with class=enabledTreeviewNode to all tree nodes that I didn't want disabled.
You could also handle the exception in the JavaScript, I guess.
Hope this helps someone who stumbles across this (otherwise excellent) solution later on.
You could use security trimming to not show items that the user doesn't have access to. I don't know of any way to have the items displayed but not active. Disabling checkboxes on the client side only could create a security hole.
Walkthrough: Filtering Site-Map Nodes Based on Security Roles
ASP.NET Site-Map Security Trimming
The OP was looking for conditional disable but I just want to use the TreeView to display historical audit data, logs of when items were switched on.
All the checkboxes on my page should be disabled. It took me some time to find this elegant jQuery solution. I hope it helps anyone with a similar issue.
Add the code below to your script section. As all input boxes will be disabled, there's no need to make any changes at all to your codebehind.
<script type="text/javascript">
$(document).ready(function () {
$("input:checkbox").each(function () {
$(this).prop('disabled', true);
});
});
</script>

User Controls required an ID in postback?

I add some div into a panel on server side, when the page is generated, and I add a ID for each one :
HtmlGenericControl divContainerInside = new HtmlGenericControl("div");
divContainerInside.ID = "inside_" + m_oIDCategoria + "_" + numero;
than, on postback (after re-creating them), I cycle them :
foreach (HtmlGenericControl divInside in myPanel.Controls.OfType<HtmlGenericControl>())
{
Response.Write(divInside.ID);
}
all is ok! But, if I remove that divContainerInside.ID when I generate it, I get a NullException cycling them. Why?
I guess you get NullException when you try to read the ID, which you haven't set.
If you change your code like this, you'll get the value:
foreach (HtmlGenericControl divInside in myPanel.Controls.OfType<HtmlGenericControl>())
{
Response.Write(divInside.ClientID);
}
PS: I don't know if you have got this line of code:
myPanel.Controls.Add(divContainerInside);
If you want to find out more about web controls you can read this article and this.
You cant add a control to the page using response.Write you need to add it to the Pages or another controls control collection like below:
Page.Controls.Add(divInside);

Page Title from a User Control possible?

I have a Search.aspx page which calls UCSearch control. UCSearch control does everything like getting what is being searched and what should be displayed, etc. I am trying to give the title to the page. As i dont have any info to write the code in the aspx page, i am thinking to write it in the control. But it is not displaying me when i tried using Page.Title in control. What am i doing wrong?? This is in Asp.net and C#.
Page.Title = "Search Results for Newark, NY";
Thanks in advance!!
Does not:
this.Page.Title = "My beautiful title";
work?
You should be able to get to the ASPX using the Parent property of the control. Cast that property to a Page (it's a WebControl or something similarly generic), then set its Title property. If you have a hierarchy of master pages or are nesting this control in other controls, you may need to traverse the Parent hierarchy for a few more levels.
You could also fire an event from your user control, passing the Title that you would like to display. You could then handle this event on the page, and set the title.
This does require a small amount of code in your aspx page, however, at least now the user control does not care where the title goes, the parent of the control can worry about it. If ever you want to change where this title goes, or even put it in multiple places, you don't have to change the user control. Let me know if you want a sample, I'll add it.
If you don't like that idea, then cederlof's answer will work, I just tested it.
protected void helloBtn_Click(object sender, EventArgs e)
{
this.Page.Title = "hello from control";
}
** You can use naming container to find the parent control of the current control. Through this way you can move through the page hierarchy.
Quickwatch will help you a lot in figuring out the things and building the statement for quick casting. Do some more research on naming container.
var container = userControl.NamingContainer;
if(container is Page)
{
Page p = container as Page;
p.Title = "Your Title";
}
**
Above is not the exact solution, but can help you it you can usercontrol directly on the page. Unless you need to iterate through the page controls. This was just for an quick help.

SharePoint AJAX C# web part - visual layout of UpdatePanel controls

I'm creating a SharePoint web part in C# that is using an UpdatePanel for some AJAX magic. Everything is working fine, but I'd like to know how to lay out my controls visually (without using SharePoint Designer). I just have two dropdownlists, some labels, a button, and a textbox. I am creating them within the overridden CreateChildControls. Thanks!
add a container panel around your controls and give it a class. Add the panel to the UpdatePanel's container. Add all other controls to the new Panel's Controls.
You can now use css to do your styling, using the container panel's CssClass as reference.
in code:
protected override CreateChildControls()
{
// .. creation of updatepanel, say upd1
Panel container = new Panel{CssClass = "webpartContainer"};
upd1.ContentTemplateContainer.Controls.Add(container);
container.Controls.Add(dropdown1); // etc. etc.
}
The Css:
.webpartContainer
{
/* if needed add some style here also */
}
.webpartContainer select
{
/* add style */
}
.webpartContainer .specificClass
{
/* give controls a class of their own in CreateChildControls
if controls of the same type need different styling
(i.e. you have more than 1 select that need to look different) */
}
there are a couple of ways you can lay them out. you can add a Table object and add rows, cells, etc. and add your controls to the cells.
Alternately, you can override the RenderContents method and output HTML directly to the write that is passed in as a parameter. If you do this method (its probably less work and more efficient then using the Table objects), you should use a StringBuilder to build your HTML then output the results to the writer. This method should gain you some performance.
Sadly, there is no visual WYSIWIG editor for this method.
Unfortunately, there is no visual designer for web parts that are created programmatically.
You can use a user control and the SmartPart web part from codeplex to gain advantage of the visual designer for .ascx user controls.
You can use ASCX files in web parts.. just load it from your webpart class in CreateChildControls like so:
var control = Page.LoadControl("/_CONTROLTEMPLATES/yourproject/your.ascx");
Controls.Add(control);
This way you can use the normal way with Visual Studio to layout your webpart. Much nicer than building HTML in code which is a pain to say the least.
(this is also much better than using SmartPart which causes issues with the trustlevel and deployment)

Categories