Monthly Archives: January 2006

Yesterday I managed to get Outlook "12" running. I had some trouble in getting it installed earlier. The new version is very unstable on my machine. It repeatedly crashed on closing. After a couple of crashes a new application showed up: Microsoft Office Diagnostics. It offered to check my machine for possible problems. This is what is looked like:

Screenshot of Microsoft Office Diagnostics

It reported two conflicting versions of Outlook. That was a bit strange, because I had only one other Outlook version installed. Outlook 2003 was no longer able to download mail. Also ActiveSync was no longer able to synchronize calendar items and contacts with my PocketPC. So Outlook "12" had to go.

After deinstalling Outlook "12", Outlook 2003 auto-repaired on start up and is working fine again. Phew...

1 Comment

Did you notice that VB.NET has way more code snippets out-of-the-box than C# in Visual Studio 2005?

This can be fixed though. Through Scott Guthrie's blog post on code snippets I found a link to download C# versions of those VB snippets. You can download snippets for certain categories only or you can download all snippets at once.

After downloading and installing, I added the snippets to Visual C# through the Code Snippet Manager. I had to add the directory %USERPROFILE%My DocumentsMSDNVisual C# 2005 Code Snippets. Read this MSDN page on how to manage code snippets.

This is what gets added if you add all categories:

Screenshot of all newly added code snippet categories in C#

1 Comment

Better late than never, but today the DVD with Beta 1 of Office "12" that was promised at PDC05 arrived.

I decided not to go with the default "upgrade" option, because that removes all Office 2003 components. Installing side-by-side went pretty smooth. The installation stalls at about 90% in the progress bar, so it has the usual Microsoft  Progress Bar Syndrome. That's okay because I am used to it 😉

The only strange thing was that Outlook was selected to be installed at the top level in the list of install options. But it wasn't present after installation. I reran setup and manually selected the main Outlook "12" components. After that the version 12 binaries were present. But this new Outlook version quits on startup after complaining that MAPI cannot load mspst.dll. I guess Outlook doesn't support running side-by-side with an older version. Too bad, I like living on the edge, but handing over my PSTs to a beta 1 version is a bit over the edge for me.

Since the PDC I have been reading the blog of Jensen Harris. He tells a lot about the history of Office and where the product suite is going. It's amazing how much usability work has gone into Office "12" and how refreshing the new UI is. I am hooked already!

1 Comment

Ever since the November 2005 CTP of WinFX I have had trouble running any Windows Presentation Foundation (WPF) sample application on my machine. Every WPF program would crash on startup with weird BAML related exceptions. Even freshly created WPF programs in Visual Studio 2005 with a single empty form refused to run. I wasn't able to find a solution at the time.

Last week Microsoft released the January 2006 CTP together with the first public release of Microsoft Expression Interactive Designer aka "Sparkle". Guess what, "Sparkle" crashed on startup on my machine as well. That was to be expected, since "Sparkle" is a 100% WPF managed code application.

Because I saw great demos of this "Avalon" design tool at PDC05, I badly wanted to be able to run this application. So I had to invest some time in fixing my problems. 

Luckily, our ASP.NET 2.0 application for Windows Media Center is done. After two months of hard work I have time to look into WinFX again. Expect more details about that Media Center project as soon as it goes live. Well it is semi-live already, meaning you can access it on the Internet if you know the URL, but I cannot divulge that information just yet.

This time Google led me to this blog entry by Nick Kramer. Nick describes that WPF scans every assembly in the Global Assembly Cache (GAC) and chokes if it cannot read one of those assemblies. The troublemaker on my machine turned out to be the Microsoft Enterprise Instrumentation Framework. It tries to load the .NET 1.0 version of System.Management, which I don't have installed on my machine. Removing Enterprise Instrumentation fixed my problem.

So I can finally run WPF applications again.

1 Comment

Scott Guthrie is blogging a lot of useful ASP.NET 2.0 articles lately. Today I looked at his DataListPaging sample. It shows how you can create a real word application with not a lot of code.

For instance, it declaratively binds query string parameters for a database query through the ObjectDataSource control. The Products.aspx page in the sample has the following code:

    <SelectParameters>
        <asp:QueryStringParameter Name="CategoryId" QueryStringField="CategoryId" DefaultValue="0" />
        <asp:QueryStringParameter Name="PageIndex" QueryStringField="PageIndex" DefaultValue="0" />
        (…)
    </SelectParameters>

A URL requesting the page might look like products.aspx?categoryId=6&pageIndex=2.

When trying out the sample, the hacker in me changed the value of the categoryId parameter to xxx. This made the application crash with a FormatException because the value xxx cannot be converted to Int32. Not so good. I wondered how much effort it would take to add parameter validation.

After digging through the MSDN documentation it quickly became obvious that there is no declarative way to do this. You can provide a default value for the parameter for when it is absent of empty. And you can even specify that a parameter should be of type Int32, but the page crashes nonetheless. I found in the MSDN docs that the appropriate way to add parameter validation is by handling events like Selecting on the ObjectDataSource.

I noticed that Scott explicitly fetched the parameters from the query string in his ProductDataSource_Selected method in the code-behind file for the page. This duplicates the declarative way of reading these parameters. This method also used casts to Int32 without error handling.

I added a event handler

            <asp:ObjectDataSource (…) OnSelecting="ProductDataSource_Selecting">

to the code-behind for the page:

    protected void ProductDataSource_Selecting(object sender, ObjectDataSourceSelectingEventArgs e)
    {
        EnsureParameterIsInteger(e.InputParameters, "pageIndex", out _pageIndex);
        EnsureParameterIsInteger(e.InputParameters, "categoryId", out _categoryId);
    }

This event handler calls this method to do the dirty work:

    private void EnsureParameterIsInteger(IOrderedDictionary inputParameters, string parameterName, out int parameterValue) 
    {
        bool isInteger = int.TryParse( (string) inputParameters[parameterName], out parameterValue);
        if (!isInteger)
        {
            inputParameters[parameterName] = ProductDataSource.SelectParameters[parameterName].DefaultValue;
            parameterValue = int.Parse( (string) inputParameters[parameterName]);
        }
    }

When the parameter value cannot be cast to an Int32 I use the default parameter value specified declaratively (see above). Notice I added two private fields to the page to store the values of the parameters so that the ProductDataSource_Selected method doesn’t have to read them from the query string:

    private int _pageIndex;
    private int _categoryId;

This also saves me from having to pass the values  explicitly to the UpdateNextPrevLinks and UpdatePagerLocation methods. With this both the declarative code and the code in the code-behind benefit from the parameter validation.

So fortunately it doesn’t take a lot of effort to add parameter validation. At first I feared it couldn’t be done when using declarative parameters, ASP.NET 2.0 proves to be flexible enough to do it.

For completeness, here is the entire content of the adapted code-behind Products.aspx.cs:

using System;
using System.Data;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Collections.Specialized;
 
public partial class Products : System.Web.UI.Page
{
    private int _pageIndex;
    private int _categoryId;
 
    protected void ProductDataSource_Selected(object sender, ObjectDataSourceStatusEventArgs e)
    {
        // Retrieve output parameter values returned from calling the "ProductsTableAdapter.GetProductsByCategoryId" 
        // method invoked by the ObjectDataSource control
        int productCount = (int)e.OutputParameters["CategoryProductCount"];
        string categoryName = (string)e.OutputParameters["CategoryName"];
 
        // Retrieve pageSize pulled from ObjectDataSource parameter
        int pageSize = Int32.Parse(ProductDataSource.SelectParameters["NumRows"].DefaultValue);
 
        // Update various page elements with data values
        UpdateTitles(categoryName);
        UpdatePagerLocation(pageSize, productCount);
        UpdateNextPrevLinks(pageSize, productCount);
    }
 
    void UpdateTitles(string title)
    {
        ProductHeader.Text = title;
        Page.Title = "Products: " + title;
    }
 
    void UpdatePagerLocation(int pageSize, int productCount)
    {
        int currentStartRow = (_pageIndex * pageSize) + 1;
        int currentEndRow = (_pageIndex * pageSize) + pageSize;
 
        if (currentEndRow > productCount)
            currentEndRow = productCount;
 
        PagerLocation.Text = currentStartRow + "-" + currentEndRow + " of " + productCount + " products";
    }
 
    void UpdateNextPrevLinks(int pageSize, int productCount)
    {
 
        string navigationFormat = "products.aspx?categoryId={0}&pageIndex={1}";
 
        PreviousPageNav.HRef = String.Format(navigationFormat, _categoryId, _pageIndex - 1);
        PreviousPageNav.Visible = (_pageIndex > 0) ? true : false;
 
        NextPageNav.HRef = String.Format(navigationFormat, _categoryId, _pageIndex + 1);
        NextPageNav.Visible = (_pageIndex + 1) * pageSize < productCount ? true : false;
    }
 
    protected void ProductDataSource_Selecting(object sender, ObjectDataSourceSelectingEventArgs e)
    {
        EnsureParameterIsInteger(e.InputParameters, "pageIndex", out _pageIndex);
        EnsureParameterIsInteger(e.InputParameters, "categoryId", out _categoryId);
    }
 
    private void EnsureParameterIsInteger(IOrderedDictionary inputParameters, string parameterName, out int parameterValue)
    {
        bool isInteger = int.TryParse((string)inputParameters[parameterName], out parameterValue);
        if (!isInteger)
        {
            inputParameters[parameterName] = ProductDataSource.SelectParameters[parameterName].DefaultValue;
            parameterValue = int.Parse((string)inputParameters[parameterName]);
        }
    }
}