Friday, 10 October 2008

Ajax Web Parts Part 4 - Dynamic Web Part Connections

Posts In This Series:

Introduction - ASP.NET AJAX and Web Parts
Ajax Web Parts Part 1 - Drag and Drop
Ajax Web Parts Part 2 - Web Part Connections
Ajax Web Parts Part 3 - Dynamic Web Parts
Ajax Web Parts Part 4 - Dynamic Web Part Connections


Download the source - Part 1
Download the source - Part 2
Download the source - Part 3
Download the source - Part 4




Introduction

Currently the dynamically created Web Parts (described in part 3) cannot be moved between zones. In addition, doing a postback results in more dynamically generated Web Parts appearing. This occurs because the Web Part Manager assigns each dynamically created Web Part a random ID when its AddWebPart() function is called. To fix this problem we need a Web Part Manager that doesn’t assign random IDs and, luckily for us, Lee Dale has created just what we’re after.

Modifying Lee’s code slightly, so that it derives from the Preview DLL’s WebPartManager, rather than the standard WebPartManager (to ensure cross browser drag and drop), gives the following:


namespace AjaxWebParts.Controls
{
public class DynamicWebPartManager
: Microsoft.Web.Preview.UI.Controls.WebParts.WebPartManager
{
public void AddDynamicWebPart(WebPart webPart,
Microsoft.Web.Preview.UI.Controls.WebParts.WebPartZone zone)
{
Internals.AddWebPart(webPart);
Internals.SetZoneID(webPart, zone.ID);
}
}
}


To use this new Web Part Manager we need to make the following changes:

In our DynamicWebParts class, make the following changes:

1. change the WebPartManager alias at the top of the line to become the following:


using WebPartManager = AjaxWebParts.Controls.DynamicWebPartManager;


2. change the addition of the dynamically created Web Part to the Web Part Zone to use the DynamicWebPartManger’s function. So the current


return itsManager.AddWebPart(genericWebPart, zone, 0);


is replaced by:


itsManager.AddDynamicWebPart(genericWebPart, zone);
return genericWebPart;


In the default.aspx page the following changes are required:

1. Add the following to the set of namespace registrations:


<%@ Register Namespace="AjaxWebParts.Controls" TagPrefix="awp" %>


2. Change the reference to the Web Part Manager from “cc1:WebPartManager” to “awp:DynamicWebPartManager”

After these changes have been made everything should now work as it should: the dynamically created Web Parts should now be able to be dragged between zones and multiple Web Parts should not appear when postbacks occur. All that is required now is to implement the connection between the dynamic Web Parts.



Creating Dynamic Web Part Connections

Once dynamic Web Parts exist, with non-random ID values, creating the connections between them is relatively straightforward. We simply need to get the connection point from each Web Part and then, if a connection can be made between the Web Parts, simply create a connection that joins the two connection points.


We therefore need to add a new function to our DynamicWebParts class to create the Web Part connection, as follows:








Finally we need to call this function from our default.aspx.cs code-behind file. The Page_Init() function now becomes:






Now, when the Web Page is run, the dynamically generated Web Parts will be connected. Typing a value into the dynamic provider's text box and clicking the button will result in the value being displayed in the dynamically created consumer Web Part.




Creating Dynamic Web Part Zones

Now that we can dynamically create Web Parts, lets move on to create the rest of the page dynamically, starting with the Web Part Zones.
This is very straightforward – it’s simply a case of creating an instance of a WebPartZone and adding it to the page (note that the WebPartZone that gets created is from the Preview DLL, due to the alias that was declared).

The DynamicWebParts class member function therefore appears as follows:




Similarly, we can create the WebPartManager:




and the UpdatePanel:



Since all the page components can now be created dynamically, the static components in the page markup are no longer required. This page therefore simplifies down to:



Note that a PlaceHolder component has been added to give us somewhere to add our dynamic components.

In the Default.aspx.cs code behind file the Page_Init function now becomes:



And, since the page markup no longer defines a WebPartManager, this needs to obtained dynamically when needed. So the Page_load function now changes to become:




Click here to see this in action.


Putting this all together results in an ASP.NET AJAX page, featuring 2 Web Part Zones and 2 dragable, connected, Web Parts. Everything has now been created dynamically and is therefore just what’s required for populating tab pages.

Tuesday, 7 October 2008

Ajax Web Parts Part 3 - Dynamic Web Parts

Posts In This Series:

Introduction - ASP.NET AJAX and Web Parts
Ajax Web Parts Part 1 - Drag and Drop
Ajax Web Parts Part 2 - Web Part Connections
Ajax Web Parts Part 3 - Dynamic Web Parts
Ajax Web Parts Part 4 - Dynamic Web Part Connections


Download the source - Part 1
Download the source - Part 2
Download the source - Part 3
Download the source - Part 4




Introduction

So far, in this series on Ajax Web Parts, all the components have been created at design time. The Web Part Zones, the Web Parts themselves, and the connections between them, have all been added statically to the page markup.

In order to achieve our ultimate goal, of movable Web Parts in a user created tab framework, we now need to change to using dynamically created components:

  • Web Part Zones need to be dynamically created so that, eventually, they can be used to populate each tab page.

  • Web Parts and their connections need to be generated at run time to populate the Web Part Zones.

Therefore, we will now investigate how the previous project can be changed to add these dynamic components.



Creating Dynamic Page Components

The easiest way to create dynamic page components is by copying static components – that is, firstly create the required components in the ASP.NET markup and then, once this is working, copy the structure to create the same components dynamically.
We will use this approach to replace the Web Part components that were statically created in the last posts, with a new class that can dynamically generate the same components.



A Dynamic Web Part Class

Rather than simply adding everything to the web form’s code-behind file, we will create a class to create the dynamic components. So first of all, we need to add a new class to the project as follows:


  1. Right-click on the project’s “app_code” node and choose “Add New Item”.
  2. In the dialog that appears choose to add a new class and give this the name “DynamicWebParts.cs”.
  3. Once the class has been added to the project, change the constructor so that it takes an instance of the page and add a property to hold a reference to this value. This is required to give us something to add our components to. The page comes from the System.Web.UI namespace, so a “using” line needs to be added for this.


At this stage the class should look as follows:




In addition to a page, to which the Web Parts can be added, the other thing that is required to create dynamic Web Parts is a Web Part Manager. For now we will simply pass through the static Web Part Manager that exists on the page and store this into a property as follows:



private WebPartManager itsManager;
public WebPartManager Manager
{
set { itsManager = value; }
}


Namespace Selection

If you remember from the previous posts, to get cross-browser drag and drop support we needed to use the Web Part Manager from the Microsoft Web Preview release. Therefore we need to use the same version here in our class. Therefore, at the top of the page the following “using” statement is required:


using Microsoft.Web.Preview.UI.Controls.WebParts;


This namespace is used to give us the Web Part Manager and Web Part Zones needed for cross-browser drag and drop.
However, the Web Parts themselves still come from the standard System.Web.UI.WebControls.WebParts namespace. Therefore, to avoid a clash, we need to explicitly state that the Web Part Manager and Web Part Zones come from Preview DLL. This can be done by specifying the full namespace every time we refer to the WebPartManager and WebPartZones. However this is rather cumbersome.

A more elegant approach is to add an alias so that both WebPartManager and WebPartZone will always reference the Preview DLL's versions. To do this the following lines should be added:




using WebPartManager = Microsoft.Web.Preview.UI.Controls.WebParts.WebPartManager;
using WebPartZone = Microsoft.Web.Preview.UI.Controls.WebParts.WebPartZone;


(Another approach that can be used to specify which DLL to use, when referring to a control, is to use tag mapping in the web.config file. However, this will only work for controls declared in markup and is therefore not appropriate for dynamically created controls. See David Barkol's blog for details)



Dynamic Web Parts

Everything is now in place to allow us to dynamically add some Web Parts to the page. As a first step, let’s replace the static Provider and Consumer Web Parts that were created in the last part of the series. Both of these Web Parts were formed by taking User Controls and adding them to Web Part Zones to create Web Parts. We will use a similar approach when dynamically generating the Web Parts. The function to take a User Control and add it to a Web Part Zone is shown below:





Notice how the steps required to dynamically add a Web Part closely resemble those used to statically add one:

· once again a User Control is loaded (using the Page’s LoadControl function)
· a Web Part is then created (by calling the Web Part Manager’s CreateWebPart function) and added to a Zone (using the Web Part Manager’s AddWebPart function).

Other points to note here are:
1. We’ve created a unique ID for control by simply taking the title supplied for the Web Part and removing any spaces from it.
2. To get the Web Part Zone we need to search the Web Part Manager’s list of Zones until the supplied name is found.
3. The Web Part Manager’s AddWebPart function takes a copy of the supplied Web Part and returns a reference to the copy. Therefore it is this that needs to be returned to get access to the Web Part that actually exists on the page (rather than the Web Part that was created using CreateWebPart).


All that’s required now is to actually use this new function to programmatically generate some Web Parts. Therefore, back in the Default.aspx.cs code behind file, add the following:




This creates an instance of our new class, sets the Web Part Manager to the one that exists in the markup and then creates 2 Web Parts – a Provider, which is added to the first Web Part Zone and a Consumer, which is added to the second Web Part Zone.

Running this should give the following output:





Click here to see this in action.

Note, that at this stage, the dynamic Web Parts cannot be moved between zones. Trying to move them results in more Web Parts appearing. This is a result of the way in which the standard Web Part Manager assigns IDs to the Web Parts and we'll look at this more closely in the next post.


Conclusion

Both statically and dynamically created Web Parts now exist on the page. The statically created Web Parts have a connection between them; any text typed into the input field of the provider Web Part will appear in the label of the consumer Web Part when the button is clicked. The dynamically created Web Parts are not yet connected and, as we'll see in the next part of this series, creating this connection is not as straightforward as it should be (this seems to be a recurring theme!). In addition, drag and drop doesn't yet work for the dynamically created Web Parts - this is something that will also be resolved in the next post.