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)
Related
I have created a web page that I use as a small dashboard to hold issue or no issue. It works great. The page uses an .aspx and .aspx.cs. I would like to be able to reuse the information on this page on other pages. My site already uses master pages and I have not been able to find an easy way to include this information.
How can I use an include from a page that has coding in the code behind easily?
Typically you use Web User Controls for this.
Web User Controls allow you to package up other controls into one that you can drop onto multiple pages. They are great for common UI items such as address entries, dashboards, etc. Basically anything that needs to be the same across multiple pages.
At the risk of seeming very obvious - do you mean usercontrols. These will allow you to reuse chunks of functionality across your site.
I guess this question falls into two categories: User Controls, and Code Reuse. Not sure which one you are after.
User Controls
If you are talking about the controls on your page you will want to create a common user control.
Code Reuse
You need to create a common class (whether it is static or not depends on how you intend to use it) and define functions within that class.
For instance, lets say you have a page that you want to print "Hello World!" on any aspx/.cs page.
You could do this
public static class MyClass
{
public string PrintHelloWorld()
{
return "Hello World!";
}
}
Then you call it from any of your pages like so:
MyClass.PrintHelloWorld();
Right click on the project > Add New Item...
Select User Control (.ascx)
Put your markup & code behind there.
Then you add that control in any other page (includding other controls [although I wouldn't recommend that])
It sounds like you may want to create an ascx User Control.
http://msdn.microsoft.com/en-us/library/ie/2x6sx01c.aspx
The Scenario: I have an asp.net website where I show a div popup on page load for taking a few user details. When a user inputs the details, or closes the popup, I set up a flag cookie so that the popup is not displayed again for the user. The div is in the MasterPage so that it is displayed no matter on which page a user lands first time. The div contains an UpdatePanel which has all the controls required for taking the details. This whole functionality is working fine.
The Problem: Now this div popup is not showing(by setting display:none) on subsequent postbacks(which I want), but the html markup is still loading with the page unnecessarily adding to the page size. What I would idealy want to do is: Check if flag cookie is set. If no, show the popup, else remove the popup's markup from the page.
Now since the div is not a server control, I cannot possibly remove it and the all the controls inside it. So, I thought of removing the UpdatePanel from the page:
protected void Page_Load(object sender, EventArgs e)
{
if (Request.Cookies["flag"] != null)
{
if (Page.Controls.Contains(updpnl_contact))
{
Page.Controls.Remove(updpnl_contact);
updpnl_contact.Dispose();
}
}
}
But I guess this tends to work with dynamically added controls only, and since the control is added at Design Time, it is not being removed.
Is there any way I can achieve this?
If you add a runat="server" attribute to your <div> element, it will be available in the code-behind. You'll need an id on it as well. Then you can just toggle the Visible property. If this property is false, the control won't be rendered to the client (i.e. no HTML markup).
What you're trying to do is not at all the usual workflow. I tend to think that it will not work as it would mess up control tree, maybe even corrupt the viewstate and so on.
As a possible solution, you can put it's visibility to hidden in the code behind. This, in the contrary to the usual 'gut feeling', doesn't work like the css propery 'display:none' for example - instead the control will not even be rendered into the page when it's not visible. This may be the workaround for you.
Happy coding.
A more efficient approach would be to create the panel as a UserControl and load it dynamically in codebehind when it's needed, then add it to your page. E.g, in code:
MyPopupControl popup = (MyPopupControl)Page.LoadControl("/path/to/usercontrol.ascx");
PopupPanel.Controls.Add(popup);
Where PopupPanel is an empty <asp:Panel>. Then, not even the markup will need to be loaded/processed except when its needed.
There is no reason that all the code you use to display and process this panel couldn't also be in the usercontrol, isolating it from the master page.
Can you build the panel dynamically, based on the cookie setting?
In Asp.Net is it possible to dynamically switch which user control gets loaded with the .aspx page.
Depending on the type of news story I would like to switch which control gets loaded.
Thanks
melt
Put a place holder in your page and in your code-behind file load the control on a if/then/else or switch/case logic method. That's the easiest way I see the implementation.
Use LoadControl(), which is an instance method on the Page class. Then simply add it to a container's Controls collection.
if (mytype=="news")
{
//load the required usercontol
ph.Controls.Add(LoadControl("~/usercontrols/news.ascx"));
}
else
{
ph.Controls.Add(LoadControl("~/usercontrols/somethingelse.ascx"));
}
With "ph" being an asp:PlaceHolder control.
I'm adding a User Control for each record pulled up in a data reader, here's the basic loop:
while (dr.Read())
{
ImageSelect imgSel = new ImageSelect(dr["Name"].ToString());
myPanel.Controls.Add(imgSel);
}
The problem is that there are no controls added to the page, I check the html output and there is my panel, with nothing in it.
I even stepped through the code in the debugger, and verified that myPanel.Controls gets a control added on each loop, with the count being 6, no errors, but then they dont show up on the page.
I've run the above code in the Page_Init and Page_Load events, both with the same result.
EDIT:
Ok so I've switched to using LoadControl("....ascx") to get my control instance, which is now working. But originally I was also passing in data via the controls constructor.. Is this still possible or do I just need to set them via get/sets?
EDIT 2:
Thanks to Freddy for pointing out that the LoadControl has an overload where you CAN pass in constructor params, see accepted answer.
EDIT 3:
After trying this method both with and without the constructor. I have found its better to just use setters for any properties I want the control to have versus trying to use the passed in object array for my constructor.
Update: As Steve pointed out, the overload of LoadControl that uses the type won't take into account the controls in the ascx. This is also mentioned in this answer: Dynamically Loading a UserControl with LoadControl Method (Type, object[]).
As I mentioned before, the get/set are more in line with the asp.net model, so I recommend using that with the LoadControl variation that receives the user control path. That said, the Steve's version is an interesting alternative: http://www.grumpydev.com/2009/01/05/passing-parameters-using-loadcontrol/.
My take is the LoadControl with type is meant to be used with web custom controls instead.
If it is an user control you should use LoadControl(usercontrolpath) to get the instance of the user control.
You can use a constructor by doing:
var name = dr["Name"].ToString();
var imgSel = LoadControl(typeof(ImageSelect), new object[]{ name });
myPanel.Controls.Add(imgSel);
Notice that depending on the project model you are using, you need to add a Reference to the aspx to use it with the typeof variation:
<%# Reference Control="~/somepath/myusercontrol.ascx" %>
Ps. I usually use the set/get for controls as I find them more in line with the asp.net model
To add UserControls you must call the LoadControl method passing in the path to the .ascx file. You can not create them by just instantiating the object the .ascx file inherits from.
A UserControl consists of both the markup and the class in the code behind. The markup contains a link to the class behind, but the class behind does not know where the markup lives and therefore can not be created on it's own.
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#!