Render WebPart in a UserControl without containing DIV - c#

I am building a usercontrol to place webparts in my pagelayouts.
What i'm basicly doing:
<uc:WebPartInclude WPName="WebPartName" runat="server"</uc:WebPartInclude>
And this all works fine, except for one thing: It now surrounds the content of the webpart with <div> ... </div>.
Since i'm a sucker for clean code, this div has got to go ^_^
public class WebPartInclude : Control
{
public string WPName = "";
private WebControl webPartControl = null;
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
webPartControl = (WebControl)System.Reflection.Assembly.GetExecutingAssembly().CreateInstance("full path..." + WPName);
}
protected override void Render(HtmlTextWriter writer)
{
base.RenderChildren (writer);
}
protected override void CreateChildControls()
{
if (webPartControl != null)
Controls.Add(webPartControl);
ChildControlsCreated = true;
}
public override ControlCollection Controls
{
get
{
EnsureChildControls();
return base.Controls;
}
}
}
So the webpart renders the following:
<div>
... webpart contents
</div>
And i want the surrounding divs to go, got any idea?

Well, since there was no way of fixing it in a nice way, i just used the HtmlTextWriter to write the contents to a string and strip the surrounding <div>...</div>.
Problem solved, just not in a nice way.

Related

Bypass Page.VerifyRenderingInServerForm

I am trying to render a Wizard control to a HTML string on the click of a Button (using Control.Render). I am already disabling event validation with the following, which works fine and enables me to render the entire Page to the string. I do this within the user control that contains the Wizard:
protected void Page_Init(object sender, EventArgs e)
{
if (Request.Form["__EVENTTARGET"] != null
&& Request.Form["__EVENTTARGET"] == btnPrint.ClientID.Replace("_", "$"))
{
Page.EnableEventValidation = false;
}
}
While this works, I'd like to render the Wizard control on its own. I understand that I can override Page.VerifyRenderingInServerForm in order to prevent the page from throwing an exception when I attempt to render this control on its own (without runat="server" form tags), like so:
public override void VerifyRenderingInServerForm(Control control)
{
// base.VerifyRenderingInServerForm(control);
}
However, I don't want to override this completely. Is there a way I can bypass this dynamically, either:
For the specific PostBack in which the button in question is clicked, or...
Specifically for the Wizard control?
How about something like:
public override void VerifyRenderingInServerForm(Control control)
{
if (!SkipVerifyRenderingInServerForm)
{
base.VerifyRenderingInServerForm(control);
}
}
public bool SkipVerifyRenderingInServerForm
{
get
{
object o = HttpContext.Current.Items["SkipVerifyRenderingInServerForm"];
return (o == null) ? false : (bool) o;
}
set
{
HttpContext.Current.Items["SkipVerifyRenderingInServerForm"] = value;
}
}
You could then set SkipVerifyRenderingInServerForm to true in your button click event handler.

Add Attributes to root HTML Element of a Custom Control

public class CustCtl : WebControl
{
protected override System.Web.UI.HtmlTextWriterTag TagKey
{
get
{
return HtmlTextWriterTag.Div;
}
}
}
With this bare bones control, it would render the root element as a Div tag. But how can I add attributes to that root HTML element that this control will render ... such as a style or id.
Thanks! =D
You would be able to do something like this within the OnPreRender event
public class CustCtl : WebControl
{
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
WebControl parent = Parent as WebControl;
if (parent != null)
{
parent.Attributes.Add("key", "value");
}
}
}
If you have your HtmlTextWriter, you can first add some attributes, before rendering a tag. For example:
public void writeDivWithStyle(HtmlTextWriter writer, string style)
{
writer.AddAttribute(HtmlTextWriterAttribute.Style, style);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
// more code here
writer.RenderEndTag(); // close the DIV
}
I found this method while looking at the API.
This worked just fine for me and seemed the most appropriate place to put it. Just simply override.
public override void RenderBeginTag(HtmlTextWriter writer)
{
writer.AddAttribute(HtmlTextWriterAttribute.Class, "[^_^]");
base.RenderBeginTag(writer);
}
You should be able to use:
CONTROL.Attribute.Add("..") //not the correct syntax
see this link
EDIT: sample usage
Control control = this.FindControl("body");
HtmlControl divControl = new HtmlGenericControl("div");
divControl.Attributes.Add("id","myid");
divControl.Attributes.Add("class","myclass");
control.Controls.Add(divControl);
[Edit, after all comments] Simply, Attributes.Add("key", "value"); in OnPreRender method works
I think that the most appropriate method or event to override is :
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
writer.AddAttribute("Key", "Value");
base.AddAttributesToRender(writer);
}
Check the msdn.

Custom Paging Repeater Control next and previous events happen after databind

I have a Custom Repeater control that inherits from Repeater and has paging functionality, however when I click the next page button the first time it refreshes the control but does not change the page, if I click it again after that it changes page perfectly.
I know what the issue is, when I click the next button it does a postback, then the data is bound to the repeater, and then after that the NextButton Event is handled.
Is there any way I can change the order of the page load events?? Or force the repeater to reload again after the event is handled??
I've included my Custom Repeater class bellow:
using System.Web.UI.WebControls;
using System.Web.UI;
using System.Data;
using System.Collections;
using System;
namespace ASPresentation.Controls
{
[ToolboxData("<cc:PagedRepeater runat=server></cc:PagedRepeater>")]
public class PagedRepeater : Repeater
{
public int PageSize { get; set; }
public int CurrentPageIndex
{
get
{
return Convert.ToInt16(Page.Session["ProjectIndex"]);
}
set
{
Page.Session.Add("ProjectIndex", value);
}
}
public PagedDataSource pagedData = new PagedDataSource();
LinkButton NextBtn = new LinkButton();
LinkButton PrevBtn = new LinkButton();
public bool IsLastPage
{
get
{
return pagedData.IsLastPage;
}
}
public bool IsFirstPage
{
get
{
return pagedData.IsFirstPage;
}
}
public override object DataSource
{
get
{
return base.DataSource;
}
set
{
pagedData.DataSource = (IEnumerable)value;
}
}
protected void NextButtonClick(object sender, EventArgs e)
{
if (!IsLastPage)
{
CurrentPageIndex++;
}
}
protected void PrevButtonClick(object sender, EventArgs e)
{
if (!IsFirstPage)
{
CurrentPageIndex--;
}
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
NextBtn.Text = "Next";
PrevBtn.Text = "Prev";
NextBtn.Click += new EventHandler(NextButtonClick);
PrevBtn.Click += new EventHandler(PrevButtonClick);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
base.Controls.Add(PrevBtn);
base.Controls.Add(NextBtn);
}
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
}
public override void DataBind()
{
pagedData.AllowPaging = true;
pagedData.PageSize = PageSize;
pagedData.CurrentPageIndex = CurrentPageIndex;
base.DataSource = pagedData;
base.DataBind();
}
}
}
A couple issues here that jump out at me.
One, why are you dynamically creating the prev/next button? Just put them in the ASCX. Show/hide them if you like based on whether your page index is first/last. If you must create them dynamically, do so in Init...
Two, do not store your page index in the session like that. What happens when you have two of these custom repeaters on one page? Use the ViewState. Key the string name to the control ID if necessary, but I think ViewState does this automatically(?).
Finally, what is triggering the DataBind? What event handler? It must be called from the Page that is hosting this control. If that's the case, then you also need to expose the Next/Prev clicks as events so that DataBind can be called in response to these events. This is how Microsoft's controls that handle paging work, such as the GridView. NextBtn.Click or PrevBtn.Click is guaranteed to be the last postback event handled.
You could handle the next/prev internally, but if you're going to do that you need to also call DataBind() in your code, so that it happens at the correct time.
Call "this.DataBind()" in the Page change functions.
This will mean you databind twice when changing pages but will work :-\

How to enable ajax when deriving from System.Web.UI.WebControls.WebControl on controls that are created inside WebControl

I've built a class that derives from System.Web.UI.WebControl. It basically renders pagination links (same as what you see on top of GridView when enabled) for use above a repeater.
I'm creating some anchor tags explicitly inside my nav control obviously, but they don't perform ajax postbacks. My understanding is that ajax requires POSTS to work right? Well, these would be GETs which I think is the problem.
Is there a way to achieve what I'm trying to do?
Thanks!
To take this advantage, you have to inherit the ICallbackEventHandler and implement its methods as follows.
public class CtlTest : WebControl, ICallbackEventHandler
{
private static readonly object EventClick = new object();
public CtlTest() : base(HtmlTextWriterTag.A) { }
public event EventHandler Click
{
add { base.Events.AddHandler(EventClick, value); }
remove { base.Events.RemoveHandler(EventClick, value); }
}
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Href, "javascript:" + this.Page.ClientScript.GetCallbackEventReference(this, null, "null", null));
}
protected override void RenderContents(HtmlTextWriter writer)
{
base.RenderContents(writer);
writer.Write("Submit Query");
}
protected virtual void OnClick(EventArgs e)
{
EventHandler handler = this.Events[EventClick] as EventHandler;
if (handler != null)
handler(this, e);
}
#region ICallbackEventHandler Members
string ICallbackEventHandler.GetCallbackResult()
{
return string.Empty;
}
void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument)
{
this.OnClick(EventArgs.Empty);
}
#endregion
}
Whereas you are working on a data pager control and it requires to update some portions of the page, it's better to write a non Ajax enabled control and put it and its relative controls within an UpdatePanel.
Ok, I figured it out. I simply made my class implement the IPostBackEventHandler. This makes your control fire an event when the user takes action on something. In my case, it's clicking a nav pagenumber: [1][2][3][4][5][Next >]
Then, inside my render where I create the Anchor tags, I add this to each href (pageStartRow is different for each):
PostBackOptions options = new PostBackOptions(this, pageStartRow.ToString());
writer.AddAttribute(HtmlTextWriterAttribute.Href, "javascript:" + Page.ClientScript.GetPostBackEventReference(options));
writer.RenderBeginTag(HtmlTextWriterTag.A);
The key is to pass something that uniquely identifies which link they clicked. This is done as the 2nd constructor parameter to the PostBackOptions class.
I then added the following items in my WebControl class:
// Defines the Click event.
public delegate void ClickHandler(object sender, GridPagingNavClickedEventArgs e);
public event ClickHandler Click;
//Invoke delegates registered with the Click event.
protected virtual void OnClick(GridPagingNavClickedEventArgs e)
{
if (Click != null)
{
Click(this, e);
}
}
public void RaisePostBackEvent(string eventArgument)
{
GridPagingNavClickedEventArgs e = new GridPagingNavClickedEventArgs(Convert.ToInt32(eventArgument));
OnClick(e);
}
The GridPagingNavClickedEventArgs contains a single item (pageNumber in my case).
Finally, in my aspx page (where I use the webcontrol), I do this in the Page_OnLoad:
gridNavTop.Click += new GridPagingNavigation.ClickHandler(gridNavTop_Click);
and this is the event code:
private void gridNavTop_Click(object sender, GridPagingNavClickedEventArgs e)
{
StartRow = e.PageStartRow;
}
As long as everything is inside an UpdatePanel, then it works perfectly!

Building a templated ASP.NET control

I'm playing around with a composite control that uses the ASP.NET templating system.
I want to be able to define a HeaderTemplate and a FooterTemplate in my markup, and programmatically add a UserControl between the two.
The markup I'm aiming for is something like this:
<asp:DropZone runat="server" ID="LeftZone">
<HeaderTemplate>
<h1>Getting started</h1>
</HeaderTemplate>
<FooterTemplate>
<h3>The end of it...</h3>
</FooterTemplate>
</asp:DropZone>
My DropZone class looks like this:
public class DropZone : Control, INamingContainer
{
private ITemplate headerTemplate;
private ITemplate footerTemplate;
[DefaultValue((string)null),
Browsable(false),
PersistenceMode(PersistenceMode.InnerProperty),
TemplateInstance(TemplateInstance.Single)]
public virtual ITemplate HeaderTemplate
{
get { return headerTemplate; }
set { headerTemplate = value; }
}
[DefaultValue((string)null),
Browsable(false),
PersistenceMode(PersistenceMode.InnerProperty),
TemplateInstance(TemplateInstance.Single)]
public ITemplate FooterTemplate
{
get { return footerTemplate; }
set { footerTemplate = value; }
}
protected override void OnInit(EventArgs e)
{
EnsureChildControls();
base.OnInit(e);
}
private void AppendTemplate(ITemplate template, Control container)
{
if (template == null) return;
var ph = new PlaceHolder();
container.Controls.Add(ph);
template.InstantiateIn(ph);
}
protected override void CreateChildControls()
{
Controls.Clear();
AppendTemplate(HeaderTemplate, this);
Control helloWorld = Page.LoadControl("~/WebParts/HelloWorld.ascx");
if (helloWorld != null)
{
Controls.Add(helloWorld);
}
AppendTemplate(FooterTemplate, this);
ChildControlsCreated = true;
base.CreateChildControls();
}
}
However, this does not work as the ITemplate fields are never instantiated.
Any help or guidance would be highly appreciated.
UPDATE: I had to derive my custom control from CompositeControl to get things work as expected.
See (for instance) Templated Server Control Example, and MSDN Search for "asp.net templated controls".

Categories

Resources