I know there are loads of posts on this subject already, but in this one I’ll try to give some useful tips on this subject.

First, you need to add AJAX.Net entries to your web.config of the MOSS site. A very easy way to do this is by using the Ajaxify stsadm extensions.

Simply add the included wsp to your solutions and deploy. After that you can easily add the entries by running the stsadm –addajaxmoss command. Unfortunately you need to add one entry manually, but that is given as part of the output when you run the command.

Next you need to add a ScriptManager to the pages with the AJAX enabled webparts. You can off course modify the masterpage. I chose to create a base web part that checks if a ScriptManager has been added to the page and adds it if necessary:

protected override void OnInit(EventArgs e)
{
    base.OnInit(e);
    //Register the ScriptManager
    ScriptManager scriptManager = ScriptManager.GetCurrent(this.Page);
    if (scriptManager == null)
    {
        scriptManager = new ScriptManager();
        scriptManager.ID = "ScriptManager1";
        scriptManager.EnablePartialRendering = true;
        this.Page.Form.Controls.AddAt(0, scriptManager);
    }
}

Because MOSS needs a lot of JavaScript to function properly, this unfortunately requires another fix. You can read more about it here. I added the fix to the base webpart with the following code:

protected override void CreateChildControls()
{
    //Add fix according to http://msdn2.microsoft.com/en-us/library/bb861877.aspx
    EnsurePanelFix();
}

private void EnsurePanelFix()
{
    if (this.Page.Form!= null)
    {
        String fixupScript = @"
        _spBodyOnLoadFunctionNames.push(""_initFormActionAjax"");
        function _initFormActionAjax()
        {
            if (_spEscapedFormAction == document.forms[0].action)
            {
                document.forms[0]._initialAction = document.forms[0].action;
            }
        }
        var RestoreToOriginalFormActionCore = RestoreToOriginalFormAction;
        RestoreToOriginalFormAction = function()
        {
            if (_spOriginalFormAction != null)
            {
                RestoreToOriginalFormActionCore();
                document.forms[0]._initialAction = document.forms[0].action;
            }
        }";
        ScriptManager.RegisterStartupScript(this, typeof(BaseWebPart), "UpdatePanelFixup", fixupScript, true);
    }
}

Because all the content you want to include in an UpdatePanel needs to be added to the ControlTemplateContainer.Controls collection, it’s not possible to write out any html you need between controls in the UpdatePanel by using the standard HtmlTextWriter.WriteLine method in the Render method. The easiest way is to add LiteralControls to the controlcollection of the UpdatePanel (with thanks to my collegue Wouter Lemaire for giving me the tip). To make this even easier, I’ve added the following extension method to my project:

public static void AddLiteral(this UpdatePanel updatePanel, string html)
{
    Literal lit = new Literal();
    lit.Text = html + "\r\n";
    updatePanel.ContentTemplateContainer.Controls.Add(lit);
}

You can call this method from the CreateChildControls method in your web part like so:

updatePanel.AddLiteral("<table><tr>");
updatePanel.AddLiteral("<td></td><td>Ma</td><td>Di</td><td>Wo</td><td>Do</td><td>Vr</td></tr><tr>");
updatePanel.AddLiteral("<td>Ochtend</td><td>");

To indicate progress you need some form of visual feedback to the user. For this you need to set the ProgressTemplate of the UpdatePanel. For this you need to create a class that implements the ITemplate interface.

Then you implement the InstantiateIn method to create a template:

public void ITemplate.InstantiateIn(Control container)
{
    Label lbl = new Label();
    lbl.Text = "Progress….";
    container.Controls.Add(lbl);
}

Then add an object of this class to the ProgressTemplate property of the UpdatePanel:

updateProgress.ProgressTemplate = new ProgressTemplate();

When I saw the wp-cumulus plugin by Roy Tanck, I thought it would be a great idea to implement the same sort of functionality in Silverlight. It’s hardly original but allows me to learn some parts of the Silverlight framework.

The components behind it are quite simple:

  • Get (or send) the tags from your HTML page to the Silverlight usercontrol
  • Render the tags so it looks 3D
  • Create a method to rotate the tags based on the position of your mouse

Choosing a 3D library

The current version of Silverlight doesn’t include 3D functionality like WPF does through the Media3D namespace. Fortunately some developers implemented the same functionality in libraries for Silverlight. The main options I found were Kit3D and Axelerate3D. I decided to use the last one because that one mimics the RotateTransform3D class in WPF 3D the best (it contains a TryTransform method).

Rendering the tags

I decided to tackle the second item first, because if I wasn’t able to manage this, the other items wouldn’t be very useful.

To create a tag in 3D you need some basic functionality:

  • A way to store it’s x, y and z coordinates
  • A hyperlinkbutton to redirect to a page that shows all the items with that tag
  • A textblock to display the tag
public class Tag3D
{
    public Tag3D(double x, double y, double z, string text)
    {
        centerPoint = new Point3D(x, y, z);
        textBlock = new TextBlock();
        textBlock.Text = text;
        btnLink = new HyperlinkButton();
        btnLink.Content = textBlock;
    }
    public HyperlinkButton btnLink { get; set; }
    public TextBlock textBlock { get; set; }
    public Point3D centerPoint { get; set; }
}

Then we need a way to make it look like it’s rendered in 3D. We do that by changing the fontsize and the opacity of the text. For that I created a method Redraw:

public void Redraw(double xOffset, double yOffset)
{
    double posZ = centerPoint.Z + 200;
    btnLink.FontSize = 10 * (posZ / 100);
    double alpha = centerPoint.Z + 200;
    if (alpha > 255)
    alpha = 255;
    if (alpha < 0)
    alpha = 0;
    btnLink.Foreground = new SolidColorBrush(Color.FromArgb(Convert.ToByte(alpha), 0, 0, ));
    Canvas.SetLeft(btnLink, centerPoint.X + xOffset - (btnLink.ActualWidth / 2));
    Canvas.SetTop(btnLink, -centerPoint.Y + yOffset - (btnLink.ActualHeight/ 2));
    Canvas.SetZIndex(btnLink, Convert.ToInt32(centerPoint.Z));
}
Placing the tags

To distribute the tags evenly over the sphere, we need some math. Luckily someone was way ahead of me and posted a useful blogentry on this subject (this technique is also used in the wp-cumulus plugin).

The following method creates and places the tags in the canvas:

private void FillTags()
{
    tagBlocks = new List();
    string[] tags = new string[] { "Silverlight",
    "WPF",
    "3D",
    "Rotation",
    "SharePoint",
    ".Net",
    "C#",
    "Transform",
    "Blog",
    "TagCloud",
    "Tam Tam",
    "Axelerate3D",
    "MOSS",
    "Math"};
    double radius = RootCanvas.Width / 3;
    int max = tags.Length;
    double phi = 0;
    double theta = 0;
    for (int i = 1; i < max + 1; i++)
    {
        phi = Math.Acos(-1.0 + (2.0 * i - 1.0) / max);
        theta = Math.Sqrt(max * Math.PI) * phi;
        double x = radius * Math.Cos(theta) * Math.Sin(phi);
        double y = radius * Math.Sin(theta) * Math.Sin(phi);
        double z = radius * Math.Cos(phi);
        Tag3D tag = new Tag3D(x, y, z, tags[i -1]);
        tag.Redraw(RootCanvas.Width / 2, RootCanvas.Height / 2);
        RootCanvas.Children.Add(tag.btnLink);
        tagBlocks.Add(tag);
    }
}

At the moment the tags to render are hard-coded but we’ll sort that out in part 2.

Rotating the tags

To rotate the tags we will use the position of the mouse as a starting point. When the mousepointer is in the center the tagcloud will remain in the current position. Once the mouse is further away from the centerpoint we’ll increase the rotationspeed. The location of the mousepointer compared to the centerpoint will set the angle of the rotation.

First we will set the rotation when the tagcloud loads:

void TagCloud_Loaded(object sender, RoutedEventArgs e)
{
    FillTags();
    rotateTransform = new RotateTransform3D();
    rotateTransform.Rotation = new AxisAngleRotation3D(new Vector3D(1.0, 0.0, 0.0), 0);
    CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
    LayoutRoot.MouseEnter += new MouseEventHandler(LayoutRoot_MouseEnter);
    LayoutRoot.MouseLeave += new MouseEventHandler(LayoutRoot_MouseLeave);
}

Here we set the rotation angle to 0 and the rotationaxis to the x-axis. When the mouse moves, we’ll change those parameters, so the rotation will have an effect:

void LayoutRoot_MouseMove(object sender, MouseEventArgs e)
{
    Point mouseLocation = e.GetPosition(RootCanvas);
    double relativeX = mouseLocation.X - (RootCanvas.ActualWidth / 2);
    double relativeY = mouseLocation.Y - (RootCanvas.ActualHeight / 2);
    MouseX.Text = relativeX.ToString();
    MouseY.Text = relativeY.ToString();
    double speed = Math.Sqrt(Math.Pow(relativeX, 2) + Math.Pow(relativeY, 2)) / 170;
    RotationSpeed.Text = speed.ToString();
    rotateTransform.Rotation = new AxisAngleRotation3D(new Vector3D(relativeY, relativeX, 0), speed);
}

To trigger the movement, we have to capture the MouseEnter and MouseLeave events:

void LayoutRoot_MouseLeave(object sender, MouseEventArgs e) {
     LayoutRoot.MouseMove -= LayoutRoot_MouseMove;
     runRotation = false;
}
void LayoutRoot_MouseEnter(object sender, MouseEventArgs e) {
     LayoutRoot.MouseMove += new MouseEventHandler(LayoutRoot_MouseMove);
     runRotation = true;
}

Now that the rotationparameters are set we need to rotate the tags, or more precisely the centerpoint of the tag. To accomplish this we’ll make use of the Rendering event of the CompositionTarget object. This is called everytime the Silverlight plugin wants to render a new frame.

void CompositionTarget_Rendering(object sender, EventArgs e)
{
    if (runRotation)
    {
        if (((AxisAngleRotation3D)rotateTransform.Rotation).Angle > 0.05)
        RotateBlocks();
    }
}
private void RotateBlocks()
{
  foreach (Tag3D textBlock in tagBlocks)
  {
    Point3D newPoint;
    if (rotateTransform.TryTransform(textBlock.centerPoint, out newPoint))
    {
      textBlock.centerPoint = newPoint;
      textBlock.Redraw(RootCanvas.ActualWidth / 2, RootCanvas.ActualHeight / 2);
    }
  }
}

To relieve the CPU a bit, we’ll only rotate the tags if the rotation angle is higher than a threshold value. The actual transformation is accomplished by invoking the TryTransform method and passing it the current centerpoint of each tag.

At the moment the Silverlight control looks like this:

In the next part I’ll show you a way to dynamically set the tags, base their fontsize on the actual weight of the tag and actually use the hyperlink button.

In standard ASP.Net web form pages there’s only one form tag for the entire page. This unfortunately has some side effects on form submit behavior.

In a standard HTML all forms are contained in their own form tag. When the browser receives a enter key press for the form the form is submitted. Because of the single form in a ASP.Net web form this behavior will be broken and the first submit button on the page will always be triggered by the browser when pressing enter.

Microsoft has included a feature in ASP.Net 2.0 to overcome this problem. In short you add an attribute DefaultButton, with the ID of the button to trigger, to an ASP Panel control that wraps the form. Unfortunately this solution doesn’t work in Firefox.

To fix this, I decided to use jQuery. What we need to have is a way to identify the different forms on a page and connect them with the right submit button. So I added a fieldset tag around the single form:

writer.WriteLine("<div class=\"regular_forms\">");
writer.WriteLine("<fieldset class=\"clearfix\" defaultsubmitbutton=\"{0}\">", btnSearch.ClientID);

// form contents inserted here

writer.WriteLine("</fieldset></div>");

The control btnSearch is the one we want to trigger when a user presses the enter button.

To hook up the button to the form we use the following JavaScript/jQuery:

$(document).ready(function() {  
    $("fieldset[defaultsubmitbutton]").each(function() {  
        var submitbuttonid = $(this).attr("defaultsubmitbutton");  
        $("input[type='text'], input[type='password']", this).keydown(function(e) {  
            var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;  
            if (key == 13) {  
                e.preventDefault();  
                var button = $("#" + submitbuttonid).eq(0);  
                if (button.length > 0) {  
                    if (typeof (button.get(0).onclick) == 'function') {  
                        button.trigger('click');  
                    }  
                    else if (button.attr('href')) {  
                        window.location = button.attr('href'); } else {  
                        button.trigger('click');  
                    }  
                }  
            }  
        });  
    });  
});  

This script finds all fieldset elements containing the defaultsubmitbutton attribute, locates all textboxes and password fields within that fieldset and hooks up the keydown event. When the enter key is pressed (keycode 13) the default event is canceled and depending on the type of button the right postback method is triggered.

Sometimes you want to retrieve all tasks that are assigned to a user or to one of the SharePoint groups the user is a member of. This quite easy to accomplish through generation of a CAML query.

Just create an Or statement for tasks assigned to the user and loop through all of the groups in the SPUser object to add those to the query.

The resulting query will look like this:

<where>
	<or>
		<eq>
			<fieldRef Name='AssignedTo'/>
			<value Type='User'>Piet van Tul</value>
		</eq>
		<eq>
			<fieldRef Name='AssignedTo'/>
			<value Type='User'>Region Controllers</value>
		</eq>
	</or>
<where>

By using this query in the QueryOverride of a Content Query Web Part you have all the power of a Content Query combined with the power of query generation.

Most of the times we create and develop a custom design for the portals and websites we build.

This can actually lead to some rather odd JavaScript error messages saying some object is undefined when dragging and dropping web parts or using the list item edit menu.

Believe it or not, this is most of the times due to the custom style sheet and not to any custom JavaScript.

The reason for this is that the build in JavaScript of MOSS use a property offsetParent of a DOM-element to perform some “magic”. offsetParent returns a reference to the object which is the closest (nearest in the containment hierarchy) positioned containing element. In some of our custom designs this sometimes returns null and the script throws the “object undefined” error.

The easiest solution to prevent this, is to absolute-position the body by include the following statement in your css:

body { position: absolute !important; } 

This can off course lead to some css issues, so positioning a wrapper is also an option.