Allowing users to modify the layout of the page is a major benefit of using Web Parts, but programmatically controlling the addition and deletion of Web Parts also provides benefits. Although a major goal of ASP.NET 2.0 is reduction in the amount of code, there is certainly a place for code. For example, you might want to dynamically create and display a Web Part only if certain criteria were met, such membership in the Admin role or one or more items existing in a shopping cart.
Using WebPartsSample.aspx as a base, I created a similar page, named WebPartsDynamic.aspx. All the controls and layout in WebPartsDynamic.aspx are the same as in WebPartsSample.aspx, with the addition of two buttons, one to add a Web Part dynamically, and another to remove a Web Part dynamically. The final page is as shown in Figure 4-15.
Figure 4-15: WebPartsDynamic.aspx running in Browse mode
Clicking the Add Web Part button adds a Web Part to WebPartZone1, as shown in Figure 4-16.
Figure 4-16: WebPartsDynamic.aspx running in Browse mode after clicking Add Web Part
Clicking the Delete Web Part button removes the Web Part, resulting in a screen that looks exactly like Figure 4-15. The server-side code for WebPartsDynamic.aspx is shown in Listing 4-2.
Listing 4-2: WebPartsDynamic.aspx.cs
using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class WebPartsDynamic : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void RadioButtonList1_SelectedIndexChanged(object sender, EventArgs e) { if (this.RadioButtonList1.SelectedValue.ToLower() == "browse") { this.WebPartManager1.DisplayMode = WebPartManager.BrowseDisplayMode; } else if (this.RadioButtonList1.SelectedValue.ToLower() == "edit") { this.WebPartManager1.DisplayMode = WebPartManager.EditDisplayMode; } else // this.RadioButtonList1.SelectedValue.ToLower() == "catalog" { this.WebPartManager1.DisplayMode = WebPartManager.CatalogDisplayMode; } } protected void Button1_Click(object sender, EventArgs e) { TextBox tb = new TextBox(); tb.Text = "Hello!"; tb.ID = "DynamicWebPart"; GenericWebPart gwp = this.WebPartManager1.CreateWebPart(tb); this.WebPartManager1.AddWebPart(gwp, this.WebPartZone1, 0); } protected void Button2_Click(object sender, EventArgs e) { if (this.WebPartZone1.WebParts.Count > 0) { this.WebPartManager1.DeleteWebPart( this.WebPartZone1.WebParts[0]); } } }
The interesting part of this code are the two button click event handlers. Button1_Click is the event handler for the Add Web Part button. The CreateWebPart method of the WebPartManager class expects as a parameter an object that derives from the Control class. A text box certainly qualifies, and so I first created a new TextBox object, and then I set the Text property and the ID property. Controls cannot be directly added to a Web Part zone, although Visual Studio Design view makes it appear that they can. Behind the scenes, when you drag and drop a normal control onto a Web Part zone, a wrapper is created. That is what I did when I called the CreateWebPart method of the WebPartManager1 control. The return value from the CreateWebPart method is a GenericWebPart object. I stored the returned object in the variable gwp. Finally, I added the Web Part to WebPartZone1. The last parameter to the AddWebPart method is the index, and 0 means that the control will be added at the top of the Web Part zone.
Button2_Click is the event handler for the Delete Web Part button. First I checked the Count property of the WebParts collection on WebPartZone1. If it is greater than 0 (meaning that there is at least one Web Part in WebPartZone1), the first Web Part (at index 0) is deleted. Note that this code can delete any Web Part, not just the Web Parts added by using the Add Web Part button. There is no distinction between Web Parts created declaratively in markup and Web Parts created dynamically in code at runtime.
The WebPartManager control can also be used to prevent mode changes, as well as to prevent moving and closing of particular Web Parts. To prevent a change to Edit mode, you can use code similar to the following.
protected void WebPartManager1_DisplayModeChanging(object sender, WebPartDisplayModeCancelEventArgs e) { if (e.NewDisplayMode == WebPartManager.EditDisplayMode) { e.Cancel = true; e.NewDisplayMode = WebPartManager.BrowseDisplayMode; this.RadioButtonList1.SelectedIndex = 0; } }
This event handler is called just before the display mode changes. Initially, when I worked on this example, I simply set the Cancel property of the WebPartDisplayModeCancelEventArgs argument to true. However, I learned that this is not sufficient; you must set the NewDisplayMode property as well. In practice, this is not a terrible problem, and it might be useful if you wanted to cause one display mode change to trigger a different display mode. Normally, you might want to just disable whatever widget you use to change the display mode, rather than use an event handler to cancel a change. In the end, having events that can be handled is always best, because the developers of the framework can never anticipate all scenarios for all developers. The downside of preventing a mode change via handling the DisplayModeChanging event is that a round trip to the server is required.
Also, to keep the user interface in sync with the actual mode, I reset the RadioButton1 SelectedIndex property to 0 (meaning that Browse will be selected by default in the option button list).