I try to simply add a div wrapper around every control of type <asp:DropDownList> at a global level (if possible). At this point I have solved it with a asp skin adding "default-select" class to all <asp:DropDownList> and then jquery just adding a div for each of them.
$j(".default-select").wrap("<div class='myClass'></div>");
Now, my question. Is it possible to add this div wrapper from the code-behind instead of relying on a javascript.
Control Adapter:
I know this should be possible by writing a control adapter that override <asp:DropDownList> render method (as described here: Dropdownlist control with <optgroup>s for asp.net (webforms)?). However, I just want to add a wrapping div, not rewrite the entire rendering of the <asp:DropDownList> control (which I have to do if i override the method?). Any suggestions? Maybe there is a way to just add something to the existing adapter??
Custom User Control: Another solution would be to build a custom <mycustom:DropDownList> with the wrapping, but, this would force me to replace every instance of <asp:DropDownList> trough the whole project (large project). I rather just change the original control some how so that my styling applies everywhere.
So summary: Is there an easy way to just make all <asp:DropDownList> render as:
<div class="myClass">
<select><option...></select>
</div>
instead of just:
<select><option...></select>
My first attempt (on Page load): I tried to add this code in the Page_load method but I don't find any way to render that div out?
var j = 0;
foreach (DropDownList control in Page.Controls.OfType<DropDownList>())
{
HtmlGenericControl div = new HtmlGenericControl();
div.ID = "div" +j;
div.TagName = "div";
div.Attributes["class"] = "myClass";
div.Controls.Add(control); // or control.Controls.Add(div); but this wouldn't wrap it.
j++;
}
Your solution just about works. Server control can only exist within the scope of a server form, you will need to perform a recursive search on the page or look directly in the form controls collection. Once you have the DropDownList and wrapped it around a div container it will need to be added to the controls collection.
Also, I think it better to perform this in OnPreInit.
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
var j = 0;
foreach (DropDownList control in form1.Controls.OfType<DropDownList>().ToList())
{
var div = new HtmlGenericControl();
div.ID = "div" + j;
div.TagName = "div";
div.Attributes["class"] = "myClass";
div.Controls.Add(control); // or control.Controls.Add(div); but this wouldn't wrap it.
j++;
form1.Controls.Add(div);
}
}
I hope this is helpful. Let me know how you get on.
Either way what you're wanting is custom and you have to code for it. So IMO, the best and simplest option is the custom control. You may have to spend some time refactoring references to replace your <asp:DropDownList>, but in all the time you've spent trying another way, you could've been done by now. :)
I've learned the hard way that keeping it simple is usually the best way to go.
Related
I am making a table for schedule and putting div's on the table. People can make new appointments by clicking on one of the div .
I am changing the activeview when a div is clicked. I need to embed information in the div's so I can use it in the database and I want to know which div is clicked.
How can I solve this? Here is my code:
HtmlGenericControl div = new HtmlGenericControl("DIV");
div.Style.Add(HtmlTextWriterStyle.Width, "60px");
div.Style.Add(HtmlTextWriterStyle.MarginTop, "1px");
div.Style.Add(HtmlTextWriterStyle.BackgroundColor, "green");
div.Style.Add(HtmlTextWriterStyle.Height, last * 40 + "px");
div.Attributes.Add("onclick", "somefunction()");
div.Style.Add("cursor", "pointer");
bul.Controls.Add(div);
bul is a table cell where I put a DIV.
Don't forget to set an ID in your div server control
see this url which explains you how reference your server control in javascript:
http://encosia.com/2007/08/08/robust-aspnet-control-referencing-in-javascript/
Excerpt from article:
$get('<%= yourdiv.ClientID %>')
<script>
alert('yourdiv has a value of: ' + $get('<%= yourdiv.ClientID %>').value);
</script>
IMHO, you need to rethink about your approach.
I think you will need to seperate the solution into 3 different parts:
1- Server-side code: which will get the information about current appointments, and to save the new appointment that your audience may create.
2- Markup code: which should contain the basic building blocks for your markup, and not to generate everything from your server-side code, as #Senfo has suggested.
3- You should use some client-side code (Javascript) in order to switch between the normal-view mode, and the create-view mode.
4- if you don't want to use client-side code, you should decorate your div with 2 attributes in order to be able to deal with them in your C# code:
ID (which should be unique per every div)
Runat="Server" attribute
I know that my solution is not complete, but considering that this is a homework question, I think it's better to guide you to the correct path, but to let you move on according to these principles.
I think you can make it, and you should now create a much cleaner solution.
if you still can't figure it out, I will try to give you an example, but my advice is to take a deep breath, and start by your own :)
> .answer {
width:20px;
height:20px;
border-color:Red;
border:1px;
background-color:Green;
HtmlGenericControl div = new HtmlGenericControl("DIV");
div.ID = "testDiv";
div.Attributes.Add("class", "answer");
div.Attributes.Add("onclick", "somefunction(this)");
this.Controls.Add(div); // this code shlould be on pageload
<script language="javascript">
function somefunction(obj)
{
alert(obj.id);
}
How can I access a control on an aspx page from the cs file in a programmatic way?
For instance, if I have a set of asp:Panel controls each with an ID named by a city (id="atlanta", id="chicago", id="pittsburgh", etc.) and then in the cs I grab a value from the database to match up to the control names what would I use?
I tried to use FindControl() as shown and it returns null.
aspx page:
<asp:Panel ID="atlanta" runat="server" Visible="false"></asp:Panel>
cs file:
controlName = storeLocation.City.ToLower();
Panel cityPanel = (Panel)FindControl(controlName);
cityPanel.Visible = true;
I suppose FindControl() is really for use in cases like Repeaters or Grids where you pass in the ItemTemaplate. In my case its just a simple content page with a content tag with a bunch of panels in it.
FindControl() isn't recursive, which may be your problem. However, there are many implementations of a recursive version, such as this one.
If you are creating the panels dynamically and you want access to them later in the page lifecycle, you can add them all to a Dictionary<string, Panel> where the ID is the key.
You can use this extension method to find controls recursively:
public static class ControlExtension
{
public static IEnumerable<Control> GetAllControls(this Control parent)
{
foreach (Control control in parent.Controls)
{
yield return control;
foreach (Control child in control.GetAllControls())
{
yield return child;
}
}
}
}
And then in your code behind page you could do something like this if you prefix your labels:
IEnumerable<Control> city_controls = this.GetAllControls().Where(x => x.Id.Contains("city_"))
or a single control:
var city = this.GetAllControls().Single(x => x.Id = "atlanta");
Where the this is your ASPX code behind page.
It also works on controls such as panels, so if you want to find all controls inside a panel etc.
If you know the containing control you may be able to use the FindControl() function of that control to find it. This method will be much faster and safer than using a recursive method.
Microsoft did not include a recursive FindControl() function for a reason, it will slow your page down over time as more controls get added to the page. If that doesn't work use a recursive function like other responses suggested.
This should work for you since the panels are nested in the asp:Content control.
Panel cityPanel =
(Panel)Master.FindControl("ContentPlaceHolderId").FindControl(controlName);
Where off course, ContentPlaceHolderId is the Id of the corresponding ContentPlaceHolder in the Master page you are implementing.
i have setup a profanity filter with bad words in a XML file and have the following function to run on my page to replace the words:
BadWordFilter.Instance.GetCleanString(TextBox1.Text);
i'm about to go through my entire site now wrapping that function around every little text variable one by one and it's going to be a huge pain in the butt
i'm hoping there's a way that i could just set my masterpage to automatically run all text through this thing on any page_load, so that the effect would be site-wide instantly. is this possible?
much appreciated for any help
One quick tip I have is to use the tag mapping feature of asp.net for this:
Create a custom textbox class derived from the TextBox class
Override the get/set Text property & in the get part, return the cleaned string
Use tag mapping feature in the web.config file to replace all TextBox classes with your custom text box class & everything should work really well.
This link has a sample implementation which uses the HTMLEncode, but you get the idea: http://www.devwebpro.co.uk/devwebprouk-46-20071010ASPNETTagMapping.html
HTH.
I realize you said Page_Load(), but I suspect this will do what you need.
In the Page.PreRender event, walk through the controls:
/* inside Page_PreRender() handler...*/
if (user_options.filterBadWords == true)
{
FilterControls(this);
}
/* this does the real work*/
private void FilterControls(Control ctrl)
{
foreach (Control c in ctrl.Controls)
{
if (c.GetType().ToString() == "System.Web.UI.WebControls.TextBox")
{
TextBox t = (TextBox)c;
/* do your thing */
t.Text = BadWordsFilter(t.Text);
}
if (c.HasControls())
FilterControls(c);
}
}
This is a hack, which will get you through your current problem: overriding the TextBox control is ultimately a better solution.
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)
Summary
Hi All,
OK, further into my adventures with custom controls...
In summary, here is that I have learned of three main "classes" of custom controls. Please feel free to correct me if any of this is wrong!
UserControls - Which inherit from UserControl and are contained within an ASCX file. These are pretty limited in what they can do, but are a quick and light way to get some UI commonality with designer support.
Custom Composite Controls - These are controls that inherit from WebControl where you add pre-existing controls to the control within the CreateChildControls method. This provides great flexibility, but lack of designer support without additional coding. They are highly portable though since they can be compiled into a DLL.
Custom Rendered Controls - Similar to Custom Composite Controls, these are added to a Web Control Library project. The rendering of the control is completely controlled by the programmer by overriding the Render method.
My Thoughts..
OK, so while playing with custom composites, I found the following:
You have little/no control over the HTML output making it difficult to "debug".
The CreateChildControls (and subsequent methods) can get real busy with Controls.Add(myControl) everywhere.
I found rendering tables (be it for layout or content) to be considerably awkward.
The Question(s)..
So, I admit, I am new to this so I could be way off-base with some of my points noted above..
Do you use Composites?
Do you have any neat tricks to control the HTML output?
Do you just say "to hell with it" and go ahead and create a custom rendered control?
Its something I am keen to get really firm in my mind since I know how much good control development can cut overall development time.
I look forward to your answers ^_^
I say go ahead with the custom rendered control. I find that in most cases the composite can be easier done and used in a UserControl, but anything beyond that and you'd need to have a finer degree of control (pun unintended) to merit your own rendering strategy.
There maybe controls that are simple enough to merit a composite (e.g., a textbox combined with a javascript/dhtml based datepicker, for example) but beyond that one example, it looks like custom rendered controls are the way to go.
Here's another extension method that I use for custom rendering:
public static void WriteControls
(this HtmlTextWriter o, string format, params object[] args)
{
const string delimiter = "<2E01A260-BD39-47d0-8C5E-0DF814FDF9DC>";
var controls = new Dictionary<string,Control>();
for(int i =0; i < args.Length; ++i)
{
var c = args[i] as Control;
if (c==null) continue;
var guid = Guid.NewGuid().ToString();
controls[guid] = c;
args[i] = delimiter+guid+delimiter;
}
var _strings = string.Format(format, args)
.Split(new string[]{delimiter},
StringSplitOptions.None);
foreach(var s in _strings)
{
if (controls.ContainsKey(s))
controls[s].RenderControl(o);
else
o.Write(s);
}
}
Then, to render a custom composite in the RenderContents() method I write this:
protected override void RenderContents(HtmlTextWriter o)
{
o.WriteControls
(#"<table>
<tr>
<td>{0}</td>
<td>{1}</td>
</tr>
</table>"
,Text
,control1);
}
Rob, you are right. The approach I mentioned is kind of a hybrid. The advantage of having ascx files around is that on every project I've seen, designers would feel most comfortable with editing actual markup and with the ascx you and a designer can work separately. If you don't plan on actual CSS/markup/design changes on the controls themselves later, you can go with a custom rendered control. As I said, my approach is only relevant for more complicated scenarios (and these are probably where you need a designer :))
I often use composite controls. Instead of overriding Render or RenderContents, just assign each Control a CssClass and use stylesheets. For multiple Controls.Add, I use an extension method:
//Controls.Add(c1, c2, c3)
static void Add(this ControlCollection coll, params Control[] controls)
{ foreach(Control control in controls) coll.Add(control);
}
For quick and dirty rendering, I use something like this:
writer.Render(#"<table>
<tr><td>{0}</td></tr>
<tr>
<td>", Text);
control1.RenderControl(writer);
writer.Render("</td></tr></table>");
For initializing control properties, I use property initializer syntax:
childControl = new Control { ID="Foo"
, CssClass="class1"
, CausesValidation=true;
};
Using custom composite controls has a point in a situation where you have a large web application and want to reuse large chunks in many places. Then you would only add child controls of the ones you are developing instead of repeating yourself.
On a large project I've worked recently what we did is the following:
Every composite control has a container. Used as a wrapped for everything inside the control.
Every composite control has a template. An ascx file (without the <%Control%> directive) which only contains the markup for the template.
The container (being a control in itself) is initialized from the template.
The container exposes properties for all other controls in the template.
You only use this.Controls.Add([the_container]) in your composite control.
In fact you need a base class that would take care of initializing a container with the specified template and also throw exceptions when a control is not found in the template. Of course this is likely to be an overkill in a small application. If you don't have reused code and markup and only want to write simple controls, you're better off using User Controls.
You might be able to make use of this technique to make design-time easier:
http://aspadvice.com/blogs/ssmith/archive/2007/10/19/Render-User-Control-as-String-Template.aspx
Basically you create an instance of a user control at runtime using the LoadControl method, then hand it a statebag of some kind, then attach it to the control tree. So your composite control would actually function like more of a controller, and the .ascx file would be like a view.
This would save you the trouble of having to instantiate the entire control tree and style the control in C#!