Monday, July 28, 2008

Apparently not so Cuil after all...

One of the top stories on Google News this morning was a new search engine called Cuil (pronounced "cool") that is supposed to have a larger search index than Google. Although the search results come out in a pretty Web 2.0 looking layout, I will probably still opt for scrolling down a list of search results. I also found a few other things that raised a few eyebrows.

Broken Contact Us Link - One thing that kind of surprised me was the fact that the Contact Us page resulted in an "Oop! We couldn't find that page." error. I recently saw the same thing on a web page of a company I was being interviewed. Of all the page links that could be broken, you'd think that this one would be pretty important.

No Address Recognition - I also tried typing in my current address, but the search engine simply hung for a few minutes after finally displaying a message that it couldn't find any results.

No Detailed Preferences - The preferences are simply Safe Search (on / off) or Typing Suggestions (on /off). I'd like to see more choices than this.

cuil

cuil2

You can also visit my post labeled "A Few Reason's I will keep using Google Search over Windows Live Search."

Wednesday, July 16, 2008

.NET 3.5 - Extension Methods Magic!

One of my favorite new features about .NET 3.5 is the ability to create Extension Methods. Essentially this provides you with the ability to extend a given type with your own custom methods. Let's think about what you would need to do to retrieve a control value from a ASP.NET FormView. First you need to use the Controls collection or FindControl method to return the control. Then, you need to cast the control to the specific control in question. Finally, you have to cast it to the type you need returned. The following is an example of this:

int woodchucksTotal = Convert.ToInt32(((Label)WoodChuckerForm.FindControl("WoodchucksTotal")).Text);

This syntax is very error prone, tedious, and hard to debug if you are doing it with a large number of controls. Alternatively, and with very little work, you can use extension methods and have syntax like this instead:

int? woodchucksTotal = WoodChuckerForm.ControlToInt32("WoodchucksTotal");

This allows me to simplify my syntax and almost cut it in half and decrease the number of parentheses from 8 to 2. I have also provided means to return a nullable typed integer. This can be helpful if, for example, you'd prefer to insert a null instead of a zero in a database. To be able to implement extension methods, you simply need to use the following two simple steps:

Implement an Extension Method

  1. Create a non-nested static class.
  2. Add static methods that have first parameters use the this modifier with the type being extended.

Sample C# Code

public static class WebControlUtil
{
    public static string ControlToString(this FormView formView, string controlName)
    {
        WebControl ctrl = (WebControl)formView.FindControl(controlName);
        switch (ctrl.GetType().ToString())
        {
            case "System.Web.UI.WebControls.TextBox":
                ctrlText = ((TextBox)ctrl).Text;
                break;
            case "System.Web.UI.WebControls.CheckBox":
                ctrlText = ((CheckBox)ctrl).Checked.ToString();
                break;
            case "System.Web.UI.WebControls.DropDownList":
                ctrlText = ((DropDownList)ctrl).Text;
                break;
        }
        return ctrlText;
    }

    public static bool ControlToBool(this FormView formView, string controlName)
    {
        WebControl ctrl = (WebControl)formView.FindControl(controlName);
        bool ctrlBool = false;
        switch (ctrl.GetType().ToString())
        {
            case "System.Web.UI.WebControls.CheckBox":
                ctrlBool = ((CheckBox)ctrl).Checked;
                break;
        }
        return ctrlBool;
    }

    public static int? ControlToInt32(this FormView formView, string controlName)
    {
        WebControl ctrl = (WebControl)formView.FindControl(controlName);
        int? ctrlInt32 = null;
        switch (ctrl.GetType().ToString())
        {
            case "System.Web.UI.WebControls.TextBox":
                if (((TextBox)ctrl).Text != "")
                {
                    ctrlInt32 = Convert.ToInt32(((TextBox)ctrl).Text);
                }
                break;
            case "System.Web.UI.WebControls.DropDownList":
                ctrlInt32 = Convert.ToInt32(((DropDownList)ctrl).SelectedValue);
                break;
        }
        return ctrlInt32;
    }

    public static DateTime? ControlToDate(this FormView formView, string controlName)
    {
        WebControl ctrl = (WebControl)formView.FindControl(controlName);
        DateTime? ctrlDate = null;
        switch (ctrl.GetType().ToString())
        {
            case "System.Web.UI.WebControls.TextBox":
                if (((TextBox)ctrl).Text != "")
                {
                    ctrlDate = Convert.ToDateTime(((TextBox)ctrl).Text);
                }
                break;
        }
        return ctrlDate;
    }
}

Five Things You Didn't Know About Me

This blog has been strictly technical, but I was feeling gutsy and decided to include a few personal items here just for kicks. With that being said, here are five things about me that you didn't know about me:



funeral_home

In order to pay for college, I lived and worked at a funeral home in downtown Seattle for three years.

We were located right on Pine Street and could see Pike's Place Market from the building. To get to our apartment, you had to climb the back stairs and walk through a rather large coffin display room. Legend has it (as I later confirmed), that services for both Bruce and Brandon Lee were held at this same funeral home. We have many experiences, including tear gas in our apartment during the demonstrations at the WTO Ministerial Round in 1999. Incidentally, I was interning for the Japanese Consulate at the time, specifically for the WTO! Sadly to say, the funeral home got sold to a law firm a few years ago, but leases out the chapel to a bar actually called Chapel. It's on the corner of Melrose and Pine and a great place for a martini and feeling morbid, or in my case nostalgic!


greyhound

I have traveled all the way across the U.S. via train, plane, automobile, and Greyhound bus.

I was seventeen during my four day trip from Groton, CT to San Diego, CA on the Greyhound bus, which was about the distance of 3,000 miles. I somehow subsisted on peanut butter, jam, twenty bucks, and the ticket price of a whopping $69.
Tip: If at all possible, try to avoid the St. Louis bus depot at three in the morning. It can be a very scary place. Also, I highly recommend the train if you want to enjoy seeing the back roads of America. That way you won't have to stop every ten minutes for driver smoke breaks and wonder if every seat has been urinated on. In addition, if you can afford it, I highly recommend a sleeper car as sleeping in the chairs get a little uncomfortable after a few days.

猿も木から落ちる

I can speak and read Japanese and was once a medical interpreter.

I was mostly self taught and actually tested into fourth year Japanese at the University of Washington without ever having taken a class. I ended up excelling and took most of the fourth year classes and some graduate level classes. Due to credit limitations, I ultimately ended up with a Japanese minor along with my major in International Studies, with a focus on East Asia. I have a continued interest in Japan, but it has definitely become more of a hobby. And by the way, I don't do karate or origami, and only watch manga occasionally.
Note: The translation from the Japanese text on the left is from a famous kotowaza or Japanese folk saying: "Even monkeys fall from trees."

bloodplasma

I donated plasma for money during college to make ends meet.

Since I was paying for college on my own, times could be tough and tough times called for desperate measures. However, I drew the line with organ donations and pharmaceutical testing.
Tip (not actually recommended): When it comes to plasma banks, you are required to wait a few days before coming back. I guess there's some silly rule about the fact that they are sucking all this blood out of you and don't want you to die. :) Anyways, competing plasma banks don't always check each others records. This means that if you are swift on your feet, you can hit up a couple a week and buy all the sweet ramen and cabbage you can eat!
oppenheimer

I have a love for reading, especially history and contemporary history.

The last book I read was American Prometheus: The Triumph and Tragedy of J. Robert Oppenheimer, by Kai Bird and Martin Sherwin. I also recently finished Guns, Germs, and Steel, by Jared Diamond and Mao: The Unknown Story, by Jung Chang and Jon Halliday. I highly recommend the first two, but you really need to be prepared and have a strong stomach for the third.
I am currently re-reading the classic Catch-22 by Joseph Heller (along with my normal array of geek books). Some of my favorite author's are Don Delillo, Hermann Hesse, Philip Roth, Ralph Ellison, and David Sedaris (can't wait until his new one comes out in paperback).

Note: I have to give some credit to Scott Hanselman's ComputerZen blog post for encouraging to throw some personal touches to my otherwise mundane technical blog.

Thursday, July 10, 2008

SharePoint 2007 - Using .NET 3.5 AJAX / AjaxControlToolkit

One of the things Microsoft advertised with the release of SharePoint 2007 SP1, was the fact that SharePoint would now jive with AJAX. Although they had previously provided instructions on how to do it before SP1, it was never officially supported.* Unfortunately, once SP1 was released, it wasn't clear on how to get AJAX to work or what you should do if you had followed their previous instructions.

After much troubleshooting, I was finally able to find an easy solution. Essentially, if you had followed the previous instructions that added a couple dozen entries to your web.config, you could now simply use assembly bindings to redirect your old AJAX 1.1 assemblies to the new AJAX 3.5 assemblies.

Instructions

1.) Follow the instructions in the following SharePoint Team Blog post:

Integrating ASP.NET AJAX with SharePoint

2.) Add the following assembly bindings to your SharePoint web.config. (This will redirect your AJAX 1.1 assemblies to use AJAX 3.5.)

<configuration>
   <runtime>
      <assemblyBinding>
         <dependentAssembly>
            <assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35"/>
        <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
         </dependentAssembly>
         <dependentAssembly>
        <assemblyIdentity name="System.Web.Extensions.Design" publicKeyToken="31bf3856ad364e35"/>
        <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
         </dependentAssembly>
      </assemblyBinding>
   </runtime>
</configuration>

I also highly recommend Daniel Larson' blog if you are trying to troubleshoot SharePoint with AJAX.

Daniel Larson's Developer Blog

Update: I am including all web.config settings below. There are a whopping 8 updates!

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<
configuration>
<
configSections>
<!--
AJAX.NET SUPPORT 1 of 8 START-->
<
sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<
sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<
section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication" />
<
sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<
section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="Everywhere" />
<
section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication" />
<
section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication" />
</
sectionGroup>
</
sectionGroup>
</
sectionGroup>
<!--
AJAX.NET SUPPORT 1 of 8 END-->
</
configSections>
<
SharePoint>
<
SafeControls>
<!--
AJAX.NET SUPPORT 2 of 8 START-->
<
SafeControl Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Namespace="System.Web.UI" TypeName="*" Safe="True" />
<!--
AJAX.NET SUPPORT 2 of 8 END-->
</
SafeControls>
</
SharePoint>
<
system.web>
<
httpHandlers>
<!--
AJAX.NET SUPPORT 3 of 8 START-->
<
remove verb="*" path="*.asmx" />
<
add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<
add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<
add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false" />
<!--
AJAX.NET SUPPORT 3 of 8 END-->
</
httpHandlers>
<
httpModules>
<!--
AJAX.NET SUPPORT 4 of 8 START-->
<
add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<!--
AJAX.NET SUPPORT 4 of 8 END-->
</
httpModules>
<
compilation batch="false" debug="true">
<
assemblies>
<!--
AJAX.NET SUPPORT 5 of 8 END-->
<
add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<
add assembly="System.Web.Extensions.Design, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<!--
AJAX.NET SUPPORT 5 of 8 START-->
</
assemblies>
</
compilation>
<
pages enableSessionState="false" enableViewState="true" enableViewStateMac="true" validateRequest="false" pageParserFilterType="Microsoft.SharePoint.ApplicationRuntime.SPPageParserFilter, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" asyncTimeout="7">
<!--
AJAX.NET SUPPORT 6 of 8 START-->
<
controls>
<
add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<
add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<
add namespace="AjaxControlToolkit" assembly="AjaxControlToolkit, Version=3.0.20229.28221, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e" tagPrefix="ajax" />
</
controls>
<!--
AJAX.NET SUPPORT 6 of 8 END-->
</
pages>
<
trust level="Full" originUrl="" />
</
system.web>
<!--
AJAX.NET SUPPORT 7 of 8 START-->
<
runtime>
<
assemblyBinding>
<
dependentAssembly>
<
assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35"/>
<
bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</
dependentAssembly>
<
dependentAssembly>
<
assemblyIdentity name="System.Web.Extensions.Design" publicKeyToken="31bf3856ad364e35"/>
<
bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</
dependentAssembly>
</
assemblyBinding>
</
runtime>
<!--
AJAX.NET SUPPORT 7 of 8 END-->
<!--
AJAX.NET SUPPORT 8 of 8 START-->
<
system.web.extensions>
<
scripting>
<
webServices>
<
jsonSerialization maxJsonLength="500000">
<
converters>
</
converters>
</
jsonSerialization>
</
webServices>
<
scriptResourceHandler enableCompression="false" enableCaching="true" />
</
scripting>
</
system.web.extensions>
<
system.webServer>
<
validation validateIntegratedModeConfiguration="false" />
<
modules>
<
add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</
modules>
<
handlers>
<
remove name="WebServiceHandlerFactory-ISAPI-2.0" />
<
add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<
add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, &#xD;&#xA; System.Web.Extensions, Version=3.5.0.0, Culture=neutral, &#xD;&#xA; PublicKeyToken=31bf3856ad364e35" />
<
add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, &#xD;&#xA; System.Web.Extensions, Version=3.5.0.0, Culture=neutral, &#xD;&#xA; PublicKeyToken=31bf3856ad364e35" />
</
handlers>
</
system.webServer>
<!--
AJAX.NET SUPPORT 8 of 8 END-->
</
configuration>



* Mike Fitzmaurice was Microsoft's SharePoint Developer Evangelist at the time and is now at Nintex.

Wednesday, July 9, 2008

SharePoint - Web Part Custom Properties

I recently got asked by a colleague about this and thought I'd write up a quick blog post on this. Creating custom properties for a web part can be done very easily with only a few steps. This simple example creates custom properties, uses the values to populate controls, and then adds those controls to the web part.

  1. Create a class that inherits from Microsoft.SharePoint.WebPartPages.WebPart.
  2. Add public properties that have the following attributes: Personalizable, WebBrowsable, WebDisplayName. (Option: You can also add Category and WebDescription attributes.) You will notice that depending on what data type you use, you can get different controls to appear. For example, a string will render a TextBox, a bool will render a CheckBox, and an enum will render a DropDownBox.
  3. Override the CreateChildControls method and then use the properties to populate control values which are then added to the web part control. (Option: You can also override Render or RenderContents, but I prefer using CreateChildControls.)

Web Part in Action

WebPartCustomProperties

Source Code

using Microsoft.SharePoint;
using System;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;

namespace Acme.Web
{
public class MyWebPart : Microsoft.SharePoint.WebPartPages.WebPart
{
public enum FavoriteColor { Red, Yellow, Blue, Green, Orange, Purple }

[Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
Category("Custom Properties"), WebDisplayName("Motto"),
WebDescription("Personal Motto")]
public string Motto { get; set; }

[Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
Category("Custom Properties"), WebDisplayName("Favorite Color"),
WebDescription("Favorite Color")]
public FavoriteColor MyFavoriteColor { get; set; }

[Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
Category("Custom Properties"), WebDisplayName("Vegetarian"),
WebDescription("Are you vegetarian?")]
public bool Vegetarian { get; set; }

public MyWebPart()
{
this.ExportMode = WebPartExportMode.All;
}

protected override void CreateChildControls()
{
try
{
base.CreateChildControls();
this.Controls.Clear();
Label lblMotto = new Label();
lblMotto.Text = Motto;
Label lblFavoriteColor = new Label();
lblFavoriteColor.Text = "My favorite color is " +
Enum.GetName(typeof(FavoriteColor), MyFavoriteColor).ToLower() + ".";
Label lblVegetarian = new Label();
if (Vegetarian)
lblVegetarian.Text = "I am a vegetarian.";
else
lblVegetarian.Text = "I am not a vegetarian.";
this.Controls.Add(lblMotto);
this.Controls.Add(new LiteralControl("<br />"));
this.Controls.Add(lblFavoriteColor);
this.Controls.Add(new LiteralControl("<br />"));
this.Controls.Add(lblVegetarian);
}
catch (Exception ex)
{
Label label = new Label();
label.Text = "Error - " + ex.Message;
this.Controls.Add(label);
}
}
}
}

For more information, see the System.Web.UI.WebControls.WebParts namespace on MSDN.