Ensuring unique ID attribute for elements within ScriptControl - c#

I'm creating a control based on ScriptControl, and I'm overriding the Render method like this:
protected override void Render(HtmlTextWriter writer)
{
RenderBeginTag(writer);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
writer.Write("This is a test.");
writer.RenderEndTag();
RenderEndTag(writer);
}
My question is, what if I want to assign the div an ID attribute and have it be unique on the page, even if there are mulitple instances of my control?
I've seen other people's code that does this:
writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + "_divTest");
That will prevent naming conflicts between instances of my control, but what if I've already created a div elsewhere on the page that coincidentally has the same ID?
I've also heard about implementing INamingContainer. Would that apply here? How could I use it?
UPDATE:
I've worked around this by overriding CreateChildControls and adding actual controls, as opposed to rendering HTML directly. In that case INamingContainer does its job. However, I'm still curious if there's a way to solve my original problem (unique IDs for directly rendered elements).

INamingController is a marker interface. Implementing it will guarantee you unique ids for each instance of your control.
http://msdn.microsoft.com/en-us/library/system.web.ui.inamingcontainer.aspx
public class MyScriptControl: ScriptControl, INamingContainer {
}

You won't want to create instances of controls just to get unique Ids, there's useless overhead/complexity in that approach. The Control.ID property may not be unique, but the Control.ClientID property will be unique. Therefore
writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + "_divTest");
that attribute has to be unique unless you use the suffix "_divTest" twice in your custom control.

I am tried to realize your issue:
public class SC : ScriptControl
{
protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
return null;
}
protected override IEnumerable<ScriptReference> GetScriptReferences()
{
return null;
}
}
//... Page code
protected void Page_PreRender(object sender, EventArgs e)
{
var sc = new SC();
var sc1 = new SC();
Page.Form.Controls.Add(sc);
Page.Form.Controls.Add(sc1);
}
But sc and sc1 have different ClientID. So it is not ASP.NET issue. Look over your realization. May be you generate ids for divs before adding ScriptControls to Page, or may be you try to create 2 divs in the scope of one ScriptControl, or may be you create ScriptConrol2 dynamicly on async-posback and it have the same id as ScriptControl1, that not added dynamicly on postback.

Related

How to copy a complex property value from one user control to another as design time?

TL;DR;
How can I add copy-paste capability to a complex, multiple values property that will enable me to copy the property value from one user control and paste it to another at design time?
The long story
I have created a user control (StylableControl) that has a complex property called Style.
This property contains an instance of a class called StylableControlStyles, that contains multiple instances of a class called Style, where each one holds values such as BackColor, ForeColor, Image, Gradient (another class I've created) etc'.
I've also created a custom control designer to allow editing style property for the user control. It shows a form where each style class in the style property can be edited easily.
Now I want to provide the users of this control an easy way to copy the entire content of the Style property from one instance of the user control to another instance, at design time.
I could, of course, override the ToString() method of the StylableControlStyles object to create a string representation that will encapsulate all the data saved in this object, but that would create a hugh string and of course would need a lot of work parsing it in the class converter (currenty I'm just using an ExpandableObjectConverter).
I would like to avoid that if possible.
Following Ash's advice in the comments I've used a DesignerVerb to copy and paste the Style to and from a private static member of type Style of the control designer.
So in my control designer class I have:
private static ZControlStyle _CopiedStyle;
And have added these designer verbs:
_Verbs.Add(new DesignerVerb("Copy Styles", CopyStyle));
_Verbs.Add(new DesignerVerb("Paste Styles", PasteStyle));
And the methods for copy ans paste:
private void PasteStyle(object sender, EventArgs e)
{
if (_CopiedStyle != null)
{
var toggleButton = Control as ZToggleButton;
if (toggleButton != null)
{
toggleButton.Style.FromStyle(_CopiedStyle);
}
else
{
(Control as ZControl).Style.FromStyle(_CopiedStyle);
}
}
}
private void CopyStyle(object sender, EventArgs e)
{
var toggleButton = Control as ZToggleButton;
if (toggleButton != null)
{
_CopiedStyle = toggleButton.Style;
}
else
{
_CopiedStyle = (Control as ZControl)?.Style;
}
}

Access parent DIV of user-control (ascx) from its code behind

From within the code-behind of an ASP .NET web user-control (ascx) I need to get access (at runtime) to its parent, a div element. The aim is simple as to modify the visibility of the mentioned parent div.
I can not touch so much of the web-page code so I'd need a solution requiring only modifications in the user-control's code behind.
So in the HTML "part" of the code of the web page I have this:
<div id="parentDIV" runat="server">
<uc:MyUserControl ID="myUserControlInstance" runat="server" />
</div>
I'd like to do in the code behind of the user-control something like this:
this.Container.Visible = false;
Note that I'm not asking if it is a good practise or not to do this.
EDIT:
The user-control code behind does not "know" about the ID of the parent DIV.
I would hide it on the client. Decorate your user control container (div?) with a class like "awesomeUserControl". Then emit some javascript using the ScriptManager object to hide the parent like this:
ScriptManager.RegisterStartupScript(this, this.GetType(), "HideMyAwesomeUserControl", "$('.awesomeUserControl').parent().hide();", true);
The better way...
What you should do is to create a custom event in your user control, to which your container will subscribe - very much like subscribing to a button event, only this is your custom control. This event passes information to your container which can then discern from it what it needs, such as whether or not the div should/not be visible.
It might look something like:
protected void Page_Load(object sender, EventArgs e)
{
this.myuserControl.Update += new MyUserControlUpdateEventHandler(myuserControl_Update);
}
void myuserControl_Update(object sender, MyuserControlEventArgs e)
{
this.parentDiv.visible = !e.ShouldHideUI;
}
This method will decouple your parent with the user control, i.e. your user control doesn't have to have any knowledge at all of the parent's controls, nor should it.
If you are curious, here is a rough example of how your user control will define such an event:
public class MyuserControlEventArgs : EventArgs
{
public bool ShouldHideUI { get;set;}
public MyuserControlEventArgs (bool shouldHideUI)
{
this.ShouldHideUI = shouldHideUI;
}
}
public delegate void MyUserControlUpdateEventHandler(object sender, MyuserControlEventArgs e);
public event MyUserControlUpdateEventHandler Update;
protected void OnUpdate(MyuserControlEventArgs e)
{
if (Update!= null)
Update(this, e);
}
Your user control will simply need to call OnUpdate whenever it feels its subscribers need to know about it.
The Quick and Dirty way...
If you need quick and dirty, then try this (inside your user control):
TheParentControl parentControl = (TheParentControl)this.Parent;
parentControl.ParentDiv.Visible = true;
The key is to cast to the appropriate type (apparently your user control would know what type of parent it has), then set the parent's property. You might expose your div as a property in the parent control. Note, that parent could be ANY control (page, FooControl, BarControl, whatever) whose control collection your user control resides. Once you get a handle to the parent, you can even FindControl() to find a control by name.
You're almost there....
this.Parent.Visible = false;
When all controls get rendered, the HTML Parent child controls can be determined like below.
I am using while loop, so in case you add some other intermediate control, it may not give crash or unexpected results.
public Control ParentControl
{
get
{
Control ctl = this.Parent;
while (true)
{
if (ctl == null) break;
if (ctl.ID.Equals("parentDIV"))
{
break;
}
ctl = ctl.Parent;
}
return ctl;
}
}
if(ParentControl != null)
ParentControl.Visible = true|false;
Am I missing something, it sounds like you have a usercontrol and an aspx page (both have code behind's).
The user control appears to have been added to the aspx page, wrapped in a div. YOu've made the div control runat server (though you can still do this via htmlcontrol).
All you ned to do to manage the div is:
parentDIV.visible = false;
or whatever you need to do with the div.
Why does the UC code behind need to know about the APSX pages DIV, it doesn't.

Dynamically creating CustomValidator in server control

I have a custom server control which wraps a RadEditor (basically a textarea). I am trying to add a CustomValidator to it dynamically, but I keep getting this error on initial pageload
Unable to find control id 'RadEditor1' referenced by the
'ControlToValidate' property of ''.
This is the code I'm using inside my server control to create the CustomValidator:
protected override void OnInit(EventArgs e)
{
var validator = new CustomValidator();
validator.CssClass = "validator-error";
validator.Display = ValidatorDisplay.Dynamic;
validator.ControlToValidate = this.ID;
validator.Text = "You've exceeded the maximum allowed length for this field";
validator.ClientValidationFunction = "checkLength";
this.Controls.Add(validator);
base.OnInit(e);
}
The problem is that RadEditor implements INamingContainer, so ASP.NET winds up searching among your server control's children for a control named RadEditor1. Of course, it's unsuccessful because RadEditor1 doesn't have a child control named RadEditor1.
The trick I use is to choose a special ID like "." to mean the parent control itself:
protected override Control FindControl(string id, int pathOffset)
{
return (id == ".") ? this : base.FindControl(id, pathOffset);
}
Then use "." as the ControlToValidate:
validator.ControlToValidate = ".";

Multiple instances of server control attached programmatically not appearing?

In the code behind of my page I want to attach a label in multiple places. To achieve this and avoid creating mutliple instances of the same label I've tried:
Label lblNone = new Label();
lblNone.Text = "<br/> None. <br/>";
Master.mainContent.Controls.Add(lblNone);
Master.mainContent.Controls.Add(lblNone);
Master.mainContent.Controls.Add(lblNone);
For some reason I only see 1 instance of the "None." on my page?
Why is this?
You have no option.. you need to create one instance of Label for each control you want to see in the screen.
This is because of the behavior of the ControlCollection class.
it will not allow multiple adds of the same "reference".
When you add a control to one ControlCollection it is automatically removed from the previous so, even if you were adding your label to different ControlCollections it wouldn't work.
PS: By ControlCollection I mean the type of the property Master.mainContent.Controls
You might find it easier to create a method for this as so: -
protected void Page_Load(object sender, EventArgs e)
{
this.Controls.Add(CreateLiteral("text"));
this.Controls.Add(CreateLiteral("text"));
this.Controls.Add(CreateLiteral("text"));
}
private Literal CreateLiteral(string Content)
{
Literal L = new Literal();
L.Text = Content;
return L;
}
Thanks,
Phil.

Dynamically adding Content blocks to Masterpage fails after Master.FindControl

I've encountered an odd problem that doesn't make any sense to me. I am trying to dynamically set up MasterPage Content controls on a page. I have it working nicely with the following code:
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
MasterPageFile = "~/MasterPages/Default.master";
string existantContentPlaceHolderID = "ContentPlaceHolder1";
string nonExistantContentPlaceHolderID = "foo";
//Control c = Master.FindControl(existantContentPlaceHolderID);
//Control c1 = Master.FindControl(nonExistantContentPlaceHolderID);
TextBox t = new TextBox
{
Text = "Text"
};
ITemplate iTemplate = new GenericITemplate(container => container.Controls.Add(t));
AddContentTemplate(existantContentPlaceHolderID, iTemplate);
}
public delegate void InstantiateTemplateDelegate(Control container);
public class GenericITemplate : ITemplate
{
private readonly InstantiateTemplateDelegate m_instantiateTemplate;
public void InstantiateIn(Control container)
{
m_instantiateTemplate(container);
}
public GenericITemplate(InstantiateTemplateDelegate instantiateTemplate)
{
m_instantiateTemplate = instantiateTemplate;
}
}
This works great, except I want to be able to double-check that the contentPlaceHolderIDs exist on the MasterPage before calling AddContentTemplate as the Page will throw an error if you add a Content control that points to a non-existing ContentPlaceHolder.
The problem I am having is that in the above example when I call one of the commented Master.FindControl lines, the TextBox no longer renders.
Does anyone have any ideas why this might be... I cannot makes heads or tails of what is going on.
Thanks,
Max
The problem is that AddContentTemplate just records its parameters in a hashtable ready to be combined with the master page instance when it is created. Calling it after the master page has been created won't do anything, and reading the Master property causes the master page to be created.
The best way I can see around this is to create a separate instance of the master page with LoadControl, which you can inspect without affecting the page's own Master property...
MasterPage testMaster = (MasterPage) LoadControl( MasterPageFile );
Control c = testMaster.FindControl(existantContentPlaceHolderID);
There's some overhead in creating a second instance, but it's not immediately obvious to me whether it will be worth worrying about.

Categories

Resources