Problem

The accordion control provided by the Ajax Control Toolkit is a great control to display expandable items in a list. Depending on the amount of data you want to display when an item is expanded, the page that contains the control can quickly become huge because you have to load in the page all the data shown in the expanded version of all the items.

Solution

Here comes the Load On Demand accordion control !

Instead of loading all the data up front, it fetches the data of the selected item only using Ajax.

imageimage

Overview

On your page, you will have two invisible panels, one that contains the html code you want to display whenever you are loading an item, and a second that is wrapped in an UpdatePanel control, that will retrieve the html code of the selected item. When the ajax request of the UpdatePanel finishes, you simply copy the content into the content panel of the selected item.

On the asp.net side

The code I use for the invisible loading panel:

<div id=”loadingPanel” class=”invisible”>
    <img src=’ajax-loader.gif’ /> Loading…
</div>

The code I use for the update panel:

<asp:UpdatePanel ID=”updatePanel” runat=”server”>
    <ContentTemplate>
        <p>Invisible update panel:
        <div class=”invisible”>
            <asp:Panel ID=”panelUpdated” runat=”server”>
               <uc1:DetailedPlayerView ID=”detailedPlayerView” runat=”server” />
            </asp:Panel>
            <asp:HiddenField ID=”hiddenPlayerId” runat=”server”/>
            <asp:Button ID=”btnUpdate” OnClick=”btnUpdate_PlayerView” runat=”server” Text=”Button” class=”invisible”/>
        </div>
        </p>
    </ContentTemplate>
</asp:UpdatePanel>

When a user clicks on the header of an item, a javascript function is called, performing the following actions:

– Save the selected panel in a javascript variable to refer to it when the callback function is called

– Store the selected data item id in an hidden field (to be able to display data specific to that id)

– Copy the content of the loading panel into the content panel of the selected item

– Click the button btnUpdate to update the UpdatePanel.

The javascript code looks like this:

var selectedContentPanel = “”;

function fetchContent(contentPanel, playerID)
{
    // save selected panel
    selectedContentPanel = contentPanel;
    // save playerId value in hidden field
    $get(“<%= hiddenPlayerId.ClientID%>”).value = playerID;
    // show the loading picture
    $get(contentPanel).innerHTML = $get(“loadingPanel”).innerHTML;
    // call postback
    $get(“<%= btnUpdate.ClientID%>”).click();
}

You need to subscribe to the endRequest events to be able to update the content panel based on the UpdatePanel content:

Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);

function EndRequestHandler(sender,args)
{
    // update selected panel with postback result
   $get(selectedContentPanel).innerHTML = $get(“<%= panelUpdated.ClientID %>”).innerHTML;
}

On the C# side

You can update the content of the UpdatePanel in the event handler of the Click event of the invisible button in the UpdatePanel

protected void btnUpdate_PlayerView(object sender, EventArgs e)
{
    var playerId = long.Parse(hiddenPlayerId.Value);
    var player = _players.Find(x => x.PlayerId == playerId);
    detailedPlayerView.Player = player;
}

Calling fetchContent

To be able to call the javascript fetchContent method when the header is clicked, you need to add a onClick attribute. Because I want to be able to pass the id of the content panel, I found it easier to add the onClick attribute on the C# side.

I subscribed to the ItemDataBound event of the Accordion control and handled it this way:

protected void accordionPlayers_ItemDataBound(object sender, AccordionItemEventArgs e)
{
    if (e.AccordionItem.ItemType == AccordionItemType.Content)
    {
        var player = (PlayerInfo)e.Item;
        var contentPanel = (Panel)e.AccordionItem.FindControl(“panelContent”);
        var headerPanel = (Panel)e.AccordionItem.Parent.Controls[0].FindControl(“panelHeader”);
        // add to the header panel an onclick attribute that passes the id of the player and the content panel
        var fetchContent = string.Format(“fetchContent(\”{0}\”, {1});”, contentPanel.ClientID, player.PlayerId);
        headerPanel.Attributes.Add(“onclick”, fetchContent);
    }
}

I hope you will find this blog post useful, don’t hesitate to leave a comment!

Cheers

Yesterday, I spent most of my day studying the MVC Framework Preview 3. To drive my research, I decided to try to redevelop a web site I previously wrote in ASP.NET WebForms. Because my web application is similar to the sample project of ScottGu (with products and categories), for the sake of brevity, I will keep his sample.

The intent of this blog post is to help new comers to have a quicker start, by exposing the difficulties I overcame yesterday.

Download link

First you need to install MVC Framework: http://go.microsoft.com/?LinkID=8955108

Using Html.ActionLink()

The Html class provides several handy helper functions that are a (very) light replacement to the Asp.Net controls.

One of them, ActionLink() builds a hyperlink, based on the given parameters.
In the Global.asax file, I have the following Route defined:

image

To be able to get a edit link on a product that points to http://localhost:56181/Products/Edit/5, we can useimage

Remark: in the third argument of ActionLink(), the name of the properties of the anonymous type should match the names used in the route (defined in the Global.asax).

For example, if I’d write:image

I would get the following link: http://localhost:56181/Products/Edit?productId=5

Cannot access a disposed object

I am using Linq to Sql to communicate with the database. To implement the List() action for the categories, I first wrote something like:

public ActionResult List()
{
    using (var db = new ProductsDataContext())
    {
        List<Category> list = db.Categories.ToList();
        return View(list);
    }
}

This works fine… until you call category.Products in the view. You would get the too famous Linq to Sql error “Cannot access a disposed object” because Lin q to Sql is trying to access the data context to fetch from the database the products of the category.

I first thought the view would be resolved during the “return View(list)”, before the data context is resolved. It seems it is done outside the List() method. So the easy workaround I found for this specific case was to use DataLoadOptions to load the related products of the category at the same time.

public ActionResult List()
{
    List<Category> list;
    using (var db = new ProductsDataContext())
    {
        var dlo = new DataLoadOptions();
        dlo.LoadWith<Category>(x => x.Products);
        db.LoadOptions = dlo;

        list = db.Categories.ToList();
    }
    return View(list);
}

(related external link)

How to submit a form?

To create a “edit product” page, like ScottGu’s example, we can use the Html class and the ViewData property:image

Remark: although the view Edit.aspx is called by giving a product id in the url, like “/Products/Edit/5”, I didn’t find an easy way to retrieve the parameter value from the view to use it in the form. the workaround is to put the product id in the ViewData in the controller class.

As we can see in the last screenshot, on submit the form will call the Save action. The Save action doesn’t require a view, we can save the data in the database from the controller and redirect to another action calling RedirectToAction:

return RedirectToAction("List", "Products");

Remark: I got some blank pages without explicit errors because I forgot to give a return parameter to the action methods in my controller. Do not forget to return a ActionResult in your action methods (RedirectToAction returns a RedirectToRouteResult object, this is a valid ActionResult to return)

Passing parameters to View UserControls

First, the following page is an interesting article about MVC compatible UserControls: link

To be able to pass more complex objects than strings to a view user controls, I used the Html.RenderUserControl() function, passing the data in the second argument:

image

Conclusion: when can I use MVC Framework?

Pros

  • Extensibility
  • Unit test friendly
  • Better control over produced html
  • SEO friendly (friendly urls for example)

Cons

(find more with question asked in forum)

At the moment, the MVC framework would be a risky choice for most of the projects I work on.

But because of the excellent extensibility of the MVC Framework, we can expect that the number of cons will be reduced over time thanks to the contributions of the community.

Today I had to undo the pending changes of my user from a different workspace and I used the following command (from a Visual Studio command prompt on the TFS server):

tf undo /workspace:workspacename /server:http://urltotfs:port /recursive “$/projectname”

Summary

I have written a Visual Studio macro that turns Visual Studio into a Snippet Editor. You simply select the piece of text you want to use in your snippet and the macro will create a snippet that will automatically be loaded by Visual Studio.

Download the latest macro here.

How to use it?

Write the snippet code from within Visual Studio as if you were writing code. Use the syntax $Anything$ to declare Intellisense literals. ($end$ and $selected$ are already used by Visual Studio)

Run the macro from the Macro Explorer:

I suggest you to associate it to a keyboard shortcut, like Alt+S:

The macro will prompt you for the name of the new snippet:

That’s it! You can now use your new snippet in Visual Studio.

How to install it?

Open the Macro Explorer by pressing Alt+F8 or through the Tools menu:

In the Macro Explorer, right-click on the Macros node, select “Load Macro Project…” and choose the vsmacros file you downloaded (here)

You should be able to see the “CreateSnippet” macro in the Macro Explorer:

Finally, you will need to edit the macro to fit your environment settings:

Change the author and snippetsPath variables. snippetPaths should be a path that has been added to the Macros Manager (the My Code Snippets folder exists by default)

Save the macro and you are good to go!

Thanks

Big thanks to Patrick Galluci who wrote this article about snippets. My macro is a simple improvement of his great macro.

The fact that sql2005 can host the CLR was a big selling point. I kind of always knew that it was possible to extend and create new SQL functions in C# but never really took the time to do it myself.

What do we want to do?

I’d like to be able to create a function that works like the .NET String.Join static method. Here is a sample query that would demonstrate the use our new aggregate function:

select PizzaRecipeId, dbo.strjoin(IngredientName)
from PizzaRecipe
group by PizzaRecipeId

This query would output something like:

1 Cheese, Chicken, BBQ Sauce
2 Cheese, Ham
3 Cheese, Chicken, Pineapple

Let’s do it 🙂

The first thing is to write the c# code.
Create a new class library project and add a new class called Concatenate. Our new class must:
– have a serializable attribute
– have the SqlUserDefinedAggregate attribute
– implement IBinarySerialize
– have the following 4 methods (there is no interface for them)
o public void Init()
o public void Accumulate(SqlString value)
o public void Merge(Concatenate other)
o public SqlString Terminate()

The implementation itself is straightforward, using a string builder to store the data:

[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined, MaxByteSize=8000)]
public class Concatenate : IBinarySerialize
{
private StringBuilder sb;

public void Init()
{
sb = new StringBuilder();
}

public void Accumulate(SqlString value)
{
if (sb.Length != 0)
{
sb.Append(“, “);
}

sb.Append(value);
}

public void Merge(Concatenate other)
{
sb.Append(other.sb);
}

public SqlString Terminate()
{
return new SqlString(sb.ToString());
}

#region IBinarySerialize Members

public void Read(BinaryReader r)
{
sb = new StringBuilder(r.ReadString());
}

public void Write(BinaryWriter w)
{
w.Write(sb.ToString());
}

#endregion
}

If the project compiles, let’s continue with the commands that will actually load the dll into sql server and reference the aggregate function:

create assembly clr_integration from ‘C:\dev\learning\sql2005\ \clr_integration\bin\debug\clr_integration.dll’ with permission_set = safe
GO

create aggregate strjoin(@input nvarchar(200)) returns nvarchar(max)
external name clr_integration.Concatenate

The permission_set is safe because the assembly does not perform any interop or contains any unsafe blocks.

Let’s create a table and some dummy data:

if (object_id(‘PizzaRecipe’) is not null) drop table PizzaRecipe
create table PizzaRecipe
(
PizzaRecipeId int NOT NULL,
IngredientName nvarchar(200)
)

insert PizzaRecipe values(1, ‘Cheese’)
insert PizzaRecipe values(1, ‘Chicken’)
insert PizzaRecipe values(1, ‘BBQ Sauce’)
insert PizzaRecipe values(2, ‘Cheese’)
insert PizzaRecipe values(2, ‘Ham’)
insert PizzaRecipe values(3, ‘Cheese’)
insert PizzaRecipe values(3, ‘Chicken’)
insert PizzaRecipe values(3, ‘Pinnaple’)

Everything should now be ready and it’s time to test it 🙂

Writing the aggregate function was actually simpler than what I imagined. It’s now just a matter of detecting when this type of feature can be used.

TFS: removing _svn folders

October 11, 2007

I recently had the need to clean up a project in TFS. When it was imported, little care had been put in making sure that only the required files would be on source control.

The most noticable folders that were to be removed from TFS were: ‘_svn’, ‘bin’ and ‘obj’.

As I’m not a big fan of doing things manually (yes, even if doing it manualy is faster – well in that case it was probably faster to write the script) I ended up trying to remember how to do those fancy FOR loops in DOS 🙂

First, build a list all the folders (or files) you want to remove:

dir /s /b _svn bin obj > files_to_delete

Creating that file is handy and you can edit it before running the following query:

for /f “delims=” %g in (files_to_delete.txt) do tf.exe delete “%g” /login:DOMAIN\login,password

note: make sure that tf.exe is in your path, or replace it with the full path.

you can now refresh your pending checkin in visual studio and check it in !

I have read quite a few times that exceptions were slow. I came to wonder about how slow they actually were? Is that big enough to care about it ? After all, hardware is pretty fast nowdays, isn’t it?

Well it turns out that throwing and catching an exception takes about 6ms on my machine (Core Duo, 2ghz)

I you want to know why it takes 6ms, have a look at this fantastic post 🙂

Now, a few milliseconds, it does not look like a long time, but think about a web server, handling hundreds of requests at the same time. If each request uses a few Int.Parse that fails(or whatever code that may throw exceptions) , that could end up being quite significant.

This is a simplified version of the code I used (removed the loop to get an average value):

private void Execute()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();

    ThrowAndCatchException();

    sw.Stop();

    Console.WriteLine(“Time taken: {0}ms”, sw.ElapsedMilliseconds);
}

private void ThrowAndCatchException()
{
    try
    {
        throw new Exception();
    }
    catch (Exception) { }
    {
    }
}

Have you ever wondered if visual studio can hex edit files ? Well it’s actually possible but not very intuitive…

Go to File -> Open -> file
Select the file that you want to open, the Open button state will change to enable
Click the small drop down list at the right of the Open button and select Open With

You will get a list of additional editors, choose Binary Editor 🙂

This morning I wanted to share TFS queries that I created in the “My Queries” folder with a team mate. I found that the best way of doing that was to save them as files (.wiq), send them by email and he just had to open them and adding them to his “My Queries” folder.

How to save a query to a file:

1) Right click on the query, select view query.
2) Go to “File” -> “Save QueryName As”
3) Select File

This will create a QueryName.wiq file.

How to add a .wiq file to your “My Queries” folder ?

1) Double click on the .wiq file
2) Go to “File” -> “Save QueryName As”
3) Select “My Query (only visible to me)”

Enjoy 🙂

I attended the Sydney’s Microsoft Ready Summit today. It was a nice event in the Darling Harbour convention centre, targeted to developers, IT professionals and IT decision makers. The developer sessions were about the new Windows Vista user experience and Office 2007.

I took a few notes about the things I thought were worth remembering. I hope that I will interest some people that did not make it to the event.

The developer keynote was presented by Andrew Coates who is a Microsoft evangelist and most of the other sessions were presented one of my Readify co worker: Mitch Denny.
We started with the Expression Interactive Designer. This is a lot like the Macromedia Flash editor, but Expression will generate XAML files. This new format (at the heart of WPF) should be the common language talked by both developers and interface designers (i.e. the same XAML file can also be opened in Visual Studio). It will hopefully reduce the number of cases where developers have to integrate a full screen bitmap, tiled into 200 tiny pieces J

A short look at PeerNet was the occasion to try finding legal uses of the P2P technology. Vista provides a new communication stack, what can we do with it ? A few examples were discussed : a collaborative meeting where an ad hoc network can be created using auto discovery features, a data synchronisation scenario where a Readify ultra mobile worker would keep all his devices up to date in an efficient way.

The search API was briefly overviewed by talking about the “New York Times reader” that integrates with the Vista search engine.

A humoristic history of icons (starting with the plain old 255 colours win95 style) introduced the “on the fly” generation of the high resolution Vista icons. It’s possible to write a custom preview handler for your own file format. No doubts that end users will quickly expect to be able to view something relevant about the files they are browsing within the Explorer window.

We had a look at the gadgets that can be added to the vista sidebar. They can be created in either HTML or in WPF. Gadget files are in fact just zip files. Anyone can rename a gadget and have a look at it. If you want to create one, it’ll probably be easier to start by modifying another one that does something similar. That’s a good way to learn how they work.

What would Vista be without the wonderful glass feature? Well first, it seems that whole point of the glass effect, beyond the fact that it’s nice looking, is that you get to focus to the important stuff inside the window. We had a look at how to use glass effect in your own application, using winforms or WPF. In both cases, a reference to an unmanaged API is required. Search for DwmExtendFramIntoClientArea and DwmApi.dll for examples. Warning: don’t misuse that functionality! We do not want to see new application using the glass effect everywhere; it would defeat the whole purpose J

A lot of effort has been put into redesigning the standard dialog windows. They are a lot simpler, with most options hidden from the user. The idea is that the dialogs now focus on what’s important. For example, the new open file dialog has got very few options displayed by default. The basic action performed by the dialog is to give a name to the file. Selecting a folder is not the primary concern anymore because the application would have probably set the current path to some meaningful folder and that anyway with the new search and filtering features of vista, saving all the documents in one single place should not be an issue anymore. All the new standard dialogs can as usal be extended (and it is even supposed to be easier than what it used to be). Search for the following keywords if you are interested: IFileDialog, IFileOpenDialog.

Last but not least, the new xml Office file format. The big change is that xml is now the default format when working with Office 2007 documents. Once again the files are just zip archives. You can rename your .docx to .zip and start to have a look inside by yourself. That’s pretty cool! Pictures are stored in their binary format for efficiency reason (base64 would make it 1.33 times bigger). It’s quite cool to be able to play directly with the list of images inside a PowerPoint document. You can manipulate the xml yourself or you can use a set of new dotnet classes (search for System.IO.Packaging). As far as compatibly is concerned, patches that can open/edit/save the new xml formats are already available for the previous versions (up to office 2000). There was a good question: what about password protected documents? The answer is that it is handled automatically by the zip format which supports encryption on a file by file basis.