Add Attributes to root HTML Element of a Custom Control - c#

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.

Related

Render WebPart in a UserControl without containing DIV

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.

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.

Treeview control in SharePoint - doesn't refresh when I update web part properties

I'm trying to create a web part that contains a TreeView control. I've got a Web Part bool property called MyCheckbox and I use this to determine which nodes should appear in the Treeview.
The problem I'm having is that when I modify the property exposed in the Web Part Properties ("Modify Shared Web Part.."), the MyCheckBox bool, and hit 'OK', the Treeview doesn't refresh. However, if I then browse to the page, the treeview is updated.
I am declaring the class as follows, using the Treeview and its root node as member variables:
public class MyWebPart : System.Web.UI.WebControls.WebParts.WebPart
{
private TreeView tree = new TreeView();
private TreeNode rootNode;
[WebBrowsable(true)]
[Personalizable(PersonalizationScope.Shared)]
public bool MyCheckBox
{
get { return _myCheckBox; }
set { _myCheckBox = value; }
}
private bool __myCheckBox = false;
public MyWebPart()
{
}
public override void RenderControl(HtmlTextWriter writer)
{
tree.RenderControl(writer);
}
protected override void CreateChildControls()
{
rootNode = new TreeNode("ExampleRootNode");
for ( int x = 0; x < 3; x++)
{
TreeNode listNode = new TreeNode(x.ToString());
rootNode.ChildNodes.Add(listNode);
}
if (_myCheckBox)
{
TreeNode listNode = new TreeNode("Final entry");
rootNode.ChildNodes.Add(listNode);
}
tree.Nodes.Add(rootNode);
this.Controls.Add(tree);
}
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
}
}
I've tried checking postback, clearing the list, and a million other things. I'm sure I must be missing something!
This is due to the page / control lifecycle of ASP.NET. The writing of properties from the editorpart (the browsable property basically has an on the fly editorpart section created) to the webpart occurs at a later stage than when the CreateChildCOntrols of the webpart is called, namely at the processing of postback events.
As a reference, see this image:

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