Tips and Tricks for working with the WPF and Silverlight Designer in Visual Studio 2010

Tip 1: Install Silverlight 4 Tools even if you are building WPF apps 

The Silverlight 4 Tools for Visual Studio 2010 contain many features and several bug fixes that are useful for WPF developers too (see this blog post for details of the features) – you should install it even if you are not working on Silverlight applications.

Tip 2: The Properties Window works when just editing XAML too. 

The Properties Window has a lot of useful features such as the Binding and Resource Pickers, Go To Definition, and various useful editors like the Brush Editor. Lots of people assume the only time you can use these features is when you’re working on the Design pane – but that’s not true! Open up the Properties Window next time you are in XAML view for your Page or Window, and you’ll see that the XAML element your cursor is in is shown in the Properties Window, so you can use those features there too. You can see this tip illustrated in this video.

Tip 3: Getting the Properties Window to work when working with Resource Dictionaries and App.xaml 

Closely linked to Tip 1 – for reasons of performance, by design the WPF and Silverlight Designer does not load the Properties Window (or the design view) for app.xaml and resource dictionary XAML files.

However if you want to make use of the useful Properties Window features (such as the Go To Definition feature for instance) when in such a file, all you need to do is activate the design pane (use the splitter bar controls to activate split view, then click anywhere on the grey “Intentionally Left Blank” design pane screen). Then you can shift the splitter so that only a tiny part of the designer is visible to avoid wasting screen real-estate.

The designer will remember this setting on a per-file basis, so next time you open that file, it will activate the designer too. You can see this tip illustrated in this video.

Tip 4: Use Sample Data for a happier design experience in your templates 

You may have had the frustrating experience of attempting to edit an item template with no data in it – it is nearly impossible to work out how your design will actually look based on the design-time experience, especially when your design sizes to the content it is bound to. Fortunately it is now possible to add sample data to your application. We show you how in this video, and you should also check out Sample Data in the Visual Studio 2010 WPF and Silverlight Designer for more details. This not only makes this sort of design easier, it can also help you avoid typos in your data binding statements by enabling the use of the Binding Builder.

Tip 5: Quick way to add the d: namespace for Sample Data 

When working with Sample Data, you will need the d: namespace in your XAML. Entering the exact namespace and other required document heading definitions such as mc:ignorable for this is a pain – but you don’t have to!

If you need to add these definitions to a Page, Window or UserControl in the designer:

  • Select the root-most item (see Tip 8 below for selection tips)
  • Click the root size mode button twice

Clicking the root size mode button twice, will insert the necessary XAML header information but leave your design’s root width/height behavior unaltered. For detailed information about the root size mode button please see this blog post.

Tip 5: Pressing Alt key while move/resize hides the snaplines: 

For a complex form, move/resize of an element on the designer surface might result in a lot of snaplines being shown. If you don’t want to see them, you can easily turn them OFF by pressing and holding the ALT key during drag move/resize.

Tip 6: Pressing the Spacebar lets you Pan the Design Surface with the Mouse 

When you have a large design or are zoomed in, it can be very helpful to be able to pan with the mouse to get to the area you want to view. When you’re over the Design Surface, just press the Spacebar and move your mouse to get panning.

Tip 8: Easy ways to select hard-to-select things on the design surface 

Most of the time you can just click on an object to select it. Upon selection you’ll notice that the element is also selected in the XAML Editor, Properties Window and Document Outline.

However there are tricky scenarios where this isn’t always possible to click on an object to select it. In those cases, these tips will come in handy:

Use Tab Key To Go To The Next Control On Design Surface

When a control on the design surface is selected, pressing the Tab key will change selection to the next control in the tabbing order.  Pressing SHIFT+TAB will select the previous control in the tabbing order.

Pressing ESC Key or right click and use the Selection menu to Select Parent Control

With a control selected on the design surface, pressing the ESC key will cause selection to be changed it its parent control.  Continued pressing will continue to change selection to the next parent control. There’s also a right-click menu action that lets you quickly select parent items of the selected item.

Use the Document Outline or the Path Control to view the XAML tree structure and select objects

The Document Outline window has a very useful view of the XAML tree and can be used to select any point in the tree. The Path Control at the bottom of the XAML editor also allows selection of parent controls (and children via the button at its right-hand end).

Tip 9: Use Bring to Front / Send to Back to set tab order within a panel 

Because WPF and Silverlight use the Document Order (the order in which elements appear in the XAML) to set both Z Order AND Tab Order in a document by default, you can use the right click context menu to move items in a form forward and backward in the Tab Order as well as changing their Z-order. This is significantly easier than individually setting the Tab Order of items one by one.

Tip 10: Use the new multi-monitor support to open my XAML and Design panes in separate windows 

Visual Studio 2010 has great multi-monitor support with tear-off windows etc. We would like to add real support for tearing off the two parts of the split view of the WPF and Silverlight Designer in a future release – meanwhile there are a couple of workarounds:

If your goal is multi-monitor with code on the right and design surface on the left (for instance), then you can maximize VS2010 across both screens, set the splitter to be VERTICAL (there are buttons in the splitter bar that let you do this), then place the splitter at the join between the two screens (quickest approach)

If you want more flexibility, you can:

  • Open a second tab group (right click on a tab and open new Vertical Tab Group)
  • Open the XAML as normal in one of the tab groups
  • Open the XAML again using “Open With”…”Source Code Editor”
  • Put the XAML and the designer in different tab groups, maximize the designer in the first, and now you’ve got full, tearable-window, multi-monitor friendly goodness for your XAML and designer views


Autor: Karl Shifflett


Creating an ASP.NET report using Visual Studio 2010 – Part 3

We continue building our report in this three part series.

Creating an ASP.NET report using Visual Studio 2010 – Part 1

Creating an ASP.NET report using Visual Studio 2010 – Part 2

Adding the ReportViewer control and filter drop downs.Open the source code for index.aspx and add a ScriptManager control. This control is required for the ReportViewer control. Add a DropDownList for the categories and suppliers. Add the ReportViewer control. The markup after these steps is shown below.


    <asp:ScriptManager ID="smScriptManager" runat="server">


    <div id="searchFilter">

        Filter by: Category :

        <asp:DropDownList ID="ddlCategories" runat="server" />

        and Supplier :

        <asp:DropDownList ID="ddlSuppliers" runat="server" />


    <rsweb:ReportViewer ID="rvProducts" runat="server">




The design view for index.aspx is shown below. The dropdowns will display the categories and suppliers in the database. The report will be filtered by the selections in the dropdowns. You will see how to do this in the next steps.
Attaching the RDLC to the ReportViewer control by clicking on the top right of the control, going to Report Viewer tasks and selecting Products.rdlc.  
Resize the ReportViewer control by dragging at the bottom right corner. I set mine to 800px x 500px. You can set this value in source view also.


Defining the data sources.
We will now define the Data Source used to populate the report. Go back to the “ReportViewer Tasks” and select “Choose Data Sources”
Select a “New data source..”

Select “Object” and name your Data Source ID “odsProducts”

In the next screen, choose “ProductRepository” as your business object.
Choose “GetProductsProjected” in the next screen.

The method requires a SupplierID and CategoryID. We will have the data source use the selected values of the drop down lists we defined earlier. Set the parameter source to be of type “Control” and set the ControlIDs to be ddlSuppliers and ddlCategories respectively. Your screen will look like this:
We are now going to define the data source for our drop downs. Select the ddlCategory drop down and pick “Choose Data Source”.

Pick “Object” and give it an id “odsCategories”
In the next screen, choose “ProductRepository”
Select the GetCategories() method in the next screen.
Select “CategoryName” and “CategoryID” in the next screen. We are done defining the data source for the Category drop down.
Perform the same steps for the Suppliers drop down.

Select each dropdown and set the AppendDataBoundItems to true and AutoPostback to true.
The AppendDataBoundItems is needed because we are going to insert an “All“ list item with a value of empty. This will be the first item in each drop down list. Go to each drop down and add this list item markup as shown below.

Double click on each drop down in the designer and add the following code in the code behind. This along with the “Autopostback= true” attribute refreshes the report anytime the selection in the drop down is changed.

protected void ddlCategories_SelectedIndexChanged(object sender, EventArgs e)





protected void ddlSuppliers_SelectedIndexChanged(object sender, EventArgs e)




Compile your report and run the page. You should see the report rendered. Note that the tool bar in the ReportViewer control gives you a couple of options including the ability to export the data to Excel, PDF or word.



Through this three part series, we did the following:

  • Created a data layer for use by our RDLC.
  • Created an RDLC using the report wizard and define a dataset for the report.
  • Used the report design surface to design our report including adding a chart.
  • Used the ReportViewer control to attach the RDLC.
  • Connected our ReportWiewer to a data source and take parameter values from the drop downlists.
  • Used AutoPostBack to refresh the reports when the dropdown selection was changed.

RDLCs allow you to create interactive reports including drill downs and grouping. For even more advanced reports you can use Microsoft® SQL Server™ Reporting Services with RDLs. With RDLs, the report is rendered on the report server instead of the web server. Another nice thing about RDLs is that you can define a parameter list for the report and it gets rendered automatically for you. RDLCs and RDLs both have their advantages and its best to compare them and choose the right one for your requirements.

Creating an ASP.NET report using Visual Studio 2010 – Part 2

We continue building our report in this three part series.

Creating the Client Report Definition file (RDLC)image 
Right click on the RDLC folder, select “Add new item..” and add an “RDLC” name of “Products”. We will use the “Report Wizard” to walk us through the steps of creating the RDLC.
In the next dialog, give the dataset a name called “ProductDataSet”. Change the data source to “NorthwindReports.DAL” and select “ProductRepository(GetProductsProjected)”.

The “Data Source” may show up empty. To get it populated, make sure your project is compiled and there is an index.aspx file in the root folder. This may be a bug.
The fields that are returned from the method are shown on the right. Click next.
Drag and drop the ProductName, CategoryName, UnitPrice and Discontinued into the Values container. Note that you can create much more complex grouping using this UI. Click Next.


Most of the selections on this screen are grayed out because we did not choose a grouping in the previous screen. Click next.
Choose a style for your report. Click next.
The report graphic design surface is now visible. Right click on the report and add a page header and page footer.
With the report design surface active, drag and drop a TextBox from the tool box to the page header. Drag one more textbox to the page header. We will use the text boxes to add some header text as shown in the next figure.

You can change the font size and other properties of the textboxes using the formatting tool bar (marked in red). You can also resize the columns by moving your cursor in between columns and dragging.

Adding Expressions

Add two more text boxes to the page footer. We will use these to add the time the report was generated and page numbers. Right click on the first textbox in the page footer and select “Expression”.
Add the following expression for the print date (note the = sign at the left of the expression in the dialog below)

"© Northwind Traders " & Format(Now(),"MM/dd/yyyy hh:mm tt")

Right click on the second text box and add the following for the page count.

Globals.PageNumber & " of " & Globals.TotalPages

Formatting the page footer is complete.
We are now going to format the “Unit Price” column so it displays the number in currency format.  Right click on the [UnitPrice] column (not header) and select “Text Box Properties..”

Under “Number”, select “Currency”. Hit OK.

Adding a chart

With the design surface active, go to the toolbox and drag and drop a chart control. You will need to move the product list table down first to make space for the chart contorl. The document can also be resized by dragging on the corner or at the page header/footer separator.

In the next dialog, pick the first chart type. This can be changed later if needed. Click OK. The chart gets added to the design surface.
Click on the blue bars in the chart (not legend). This will bring up drop locations for dropping the fields. Drag and drop the UnitPrice and CategoryName into the top (y axis) and bottom (x axis) as shown below. This will give us the total unit prices for a given category. That is the best I could come up with as far as what report to render, sorry 🙂 Delete the legend area to get more screen estate.
Resize the chart to your liking. Change the header, x axis and y axis text by double clicking on those areas.

We made it this far. Let’s impress the client by adding a gradient to the bar graph 🙂 Right click on the blue bar and select “Series properties”.

Under “Fill”, add a color and secondary color and select the Gradient style.

We are done designing our report. In the next section you will see how to add the report to the report viewer control, bind to the data and make it refresh when the filter criteria are changed.

Creating an ASP.NET report using Visual Studio 2010 – Part 1

Creating an ASP.NET report using Visual Studio 2010 – Part 2

Creating an ASP.NET report using Visual Studio 2010 – Part 3

Add a folder called “RDLC”. This will hold our RDLC report.

Creating an ASP.NET report using Visual Studio 2010 – Part 1

This tutorial walks you through creating an ASP.NET report based on the Northwind sample database. It shows how to add a client report definition file (RDLC), create a dataset for the RDLC, define queries using LINQ to Entities, design the report and add a ReportViewer web control to render the report in a ASP.NET web page. The report will have a chart control. The result can be filtered by two drop downs at the top..

At the end of the walkthrough, you should have a UI like the following.  As shown, there is a product list report with an embedded chart. The chart shows the sum of Unit price for a given category. It is possible to filter the results by Category and Supplier. The drop downs auto post back when the selection is changed.  This demo uses Visual Studio 2010 RTM.

This post is split into three parts. The last part has the sample code attached.


Lets start by creating a new ASP.NET empty web application called “NorthwindReports”
Creating the Data Access Layer (DAL)

Add a web form called index.aspx to the root directory. You do this by right clicking on the NorthwindReports web project and selecting “Add item..”
Create a folder called “DAL”. We will store all our data access methods and any data transfer objects in here.


Right click on the DAL folder and add a ADO.NET Entity data model called Northwind.


Select “Generate from database” and click Next.

Create a connection to your database containing the Northwind sample database and click Next.

From the table list, select Categories, Products and Suppliers and click next.

Our Entity data model gets created and looks like this:

Adding data transfer objects

Right click on the DAL folder and add a ProductViewModel. Add the following code. This class contains properties we need to render our report.

public class ProductViewModel


    public int? ProductID { get; set; }

    public string ProductName { get; set; }

    public System.Nullable<decimal> UnitPrice { get; set; }

    public string CategoryName { get; set; }

    public int? CategoryID { get; set; }

    public int? SupplierID { get; set; }

    public bool Discontinued { get; set; }


Add a SupplierViewModel class. This will be used to render the supplier DropDownlist.

public class SupplierViewModel


    public string CompanyName { get; set; }

    public int SupplierID { get; set; }


Add a CategoryViewModel class.

public class CategoryViewModel


    public string CategoryName { get; set; }

    public int CategoryID { get; set; }


Create an IProductRepository interface. This will contain the signatures of all the methods we need when accessing the entity model.  This step is not needed but follows the repository pattern.

interface IProductRepository


        IQueryable<Product> GetProducts();

        IQueryable<ProductViewModel> GetProductsProjected(int? supplierID, int? categoryID);

        IQueryable<SupplierViewModel> GetSuppliers();

        IQueryable<CategoryViewModel> GetCategories();



Create a ProductRepository class that implements the IProductReposity above. The methods available in this class are as follows:

  • GetProducts – returns an IQueryable of all products.
  • GetProductsProjected – returns an IQueryable of ProductViewModel. The method filters all the products based on SupplierId and CategoryId if any. It then projects the result into the ProductViewModel.
  • GetSuppliers() – returns an IQueryable of all suppliers projected into a SupplierViewModel
  • GetCategories() – returns an IQueryable of all categories projected into a CategoryViewModel 
public class ProductRepository : IProductRepository


    /// <summary>

    /// IQueryable of all Products

    /// </summary>

    /// <returns></returns>

    public IQueryable<Product> GetProducts()


        var dataContext = new NorthwindEntities();

        var products = from p in dataContext.Products

                       select p;

        return products;



    /// <summary>

    /// IQueryable of Projects projected

    /// into the ProductViewModel class

    /// </summary>

    /// <returns></returns>

    public IQueryable<ProductViewModel> GetProductsProjected(int? supplierID, int? categoryID)


        var projectedProducts = from p in GetProducts()

                                select new ProductViewModel


                                    ProductID = p.ProductID,

                                    ProductName = p.ProductName,

                                    UnitPrice = p.UnitPrice,

                                    CategoryName = p.Category.CategoryName,

                                    CategoryID = p.CategoryID,

                                    SupplierID = p.SupplierID,

                                    Discontinued = p.Discontinued


        // Filter on SupplierID

        if (supplierID.HasValue)


            projectedProducts = projectedProducts.Where(a => a.SupplierID == supplierID);



        // Filter on CategoryID

        if (categoryID.HasValue)


            projectedProducts = projectedProducts.Where(a => a.CategoryID == categoryID);



        return projectedProducts;




    public IQueryable<SupplierViewModel> GetSuppliers()


        var dataContext = new NorthwindEntities();

        var suppliers = from s in dataContext.Suppliers

                        select new SupplierViewModel


                            SupplierID = s.SupplierID,

                            CompanyName = s.CompanyName


        return suppliers;



    public IQueryable<CategoryViewModel> GetCategories()


        var dataContext = new NorthwindEntities();

        var categories = from c in dataContext.Categories

                         select new CategoryViewModel


                             CategoryID = c.CategoryID,

                             CategoryName = c.CategoryName


        return categories;



Your solution explorer should look like the following.

Build your project and make sure you don’t get any errors.

In the next part, we will see how to create the client report definition file using the Report Wizard

Creating an ASP.NET report using Visual Studio 2010 – Part 1

Creating an ASP.NET report using Visual Studio 2010 – Part 2

Creating an ASP.NET report using Visual Studio 2010 – Part 3

JQuery Autocomplete

Autocomplete é um recurso muito interessante a ser usado em formulários web quando precisamos de um campo que de início seria um dropdownlist, mas, que se torna inviável pelo número de registros a ser carregado. Quem já não se deparou com a situação de ter que carregar mais de 50 u 100 registros em uma dropdownlist?

Segue abaixo um recurso que estamos utilizando bastante em formulários web em nossos desenvolvimentos na fábrica de softwares da DANRESA Consultoria de Informatica e que tem agradado nossos clientes com uma boa experiência de usuário.


Autocomplete, when added to an input field, enables users to quickly find and select from a pre-populated list of values as they type, leveraging searching and filtering.

By giving an Autocomplete field focus or entering something into it, the plugin starts searching for entries that match and displays a list of values to choose from. By entering more characters, the user can filter down the list to better matches.

This can be used to enter previous selected values, for example you could use Autocomplete for entering tags, to complete an address, you could enter a city name and get the zip code, or maybe enter email addresses from an address book.

You can pull data in from a local and/or a remote source: Local is good for small data sets (like an address book with 50 entries), remote is necessary for big data sets, like a database with hundreds or millions of entries to select from.

Autocomplete can be customized to work with various data sources, by just specifying the source option. A data source can be:

  • an Array with local data
  • a String, specifying a URL
  • a Callback

The local data can be a simple Array of Strings, or it contains Objects for each item in the array, with either a label or value property or both. The label property is displayed in the suggestion menu. The value will be inserted into the input element after the user selected something from the menu. If just one property is specified, it will be used for both, eg. if you provide only value-properties, the value will also be used as the label.

When a String is used, the Autocomplete plugin expects that string to point to a URL resource that will return JSON data. It can be on the same host or on a different one (must provide JSONP). The request parameter “term” gets added to that URL. The data itself can be in the same format as the local data described above.

The third variation, the callback, provides the most flexibility, and can be used to connect any data source to Autocomplete. The callback gets two arguments:

  • A request object, with a single property called “term”, which refers to the value currently in the text input. For example, when the user entered “new yo” in a city field, the Autocomplete term will equal “new yo”.
  • A response callback, which expects a single argument to contain the data to suggest to the user. This data should be filtered based on the provided term, and can be in any of the formats described above for simple local data (String-Array or Object-Array with label/value/both properties).

The demos all focus on different variations of the source-option – look for the one that matches your use case, and take a look at the code.

 Exemplo de Código

Faça um teste em: 

<meta charset="UTF-8" />

<script type="text/javascript">
$(function() {
function log(message) {
$("#log").attr("scrollTop", 0);

source: function(request, response) {
url: "",
dataType: "jsonp",
data: {
featureClass: "P",
style: "full",
maxRows: 12,
name_startsWith: request.term
success: function(data) {
response($.map(data.geonames, function(item) {
return {
label: + (item.adminName1 ? ", " + item.adminName1 : "") + ", " + item.countryName,
minLength: 2,
select: function(event, ui) {
log(ui.item ? ("Selected: " + ui.item.label) : "Nothing selected, input was " + this.value);
open: function() {
close: function() {
.ui-autocomplete-loading { background: url(indicator.gif) no-repeat right; }
#city { width: 25em; }


<label for="city">Your city: </label>
<input id="city" />
Powered by <a href=""></a>

<div style="margin-top:2em; font-family:Arial">
<div id="log" style="height: 200px; width: 300px; overflow: auto;"></div>

</div><!-- End demo -->

The Autocomplete widgets provides suggestions while you type into the field. Here the suggestions are cities, displayed when at least two characters are entered into the field.
In this case, the datasource is the <a href=""> webservice</a>. While only the city name itself ends up in the input after selecting an element, more info is displayed in the suggestions to help find the right entry. That data is also available in callbacks, as illustrated by the Result area below the input.
</div><!-- End demo-description -->

Animação de um Carro com CSS3

Animação de um Carro com CSS3

Com CSS3 será possível criar pequenas animações através de HTML e CSS usando a propriedade transition. Partindo desse princípio, o desenvolvedor Koller Jürgen criou uma simples animação de um carro que “passeia” pela tela quando o usuário coloca o mouse sobre ele. O código é muito simples e bastam apenas 2 imagens para obter o resultado final:

1.<a href="#">
2.    <img src="car.jpg" /> <!--Imagem do carro-->
3.    <img src="gear.png" /> <!--Imagem da roda-->
4.    <img src="gear.png" /> <!--Imagem da roda-->

Após basta definir algumas regras no CSS:

01./* Velocidade da animacao */
03.    -webkit-transition: all 3.1s ease-in-out;
04.    transition: all 3.1s ease-in-out;
07./* Animacao do Carro */
08.a:hover img:nth-of-type(1) {
09.    -webkit-transform: translate(700px,0px) rotate(0deg);
10.    transform: translate(700px,0px) rotate(0deg);
13./* Animacao das rodas */
14.a:hover img {
15.    -webkit-transform: translate(700px,0px) rotate(1000deg);
16.    transform: translate(700px,0px) rotate(1000deg);

Agora o resultado final. Artigo original: CSS3 Car Animation

Galeria de imagens com CSS3

Galeria de imagens com CSS3

Nesse tutorial vou mostrar como desenvolver uma galeria de imagens com a pseudo-classe :target das CSS3. A Pseudo-classe :target tem como função alterar os estilos do elemento alvo, já fiz outros tutoriais sobre o assunto, portanto, não vou me alongar explicando os benefícios de utilização dessa pseudo-seletor.

Eu não sou o autor original dessa galeria, acabei a encontrando no site VirtuousWeb e acabei efetuando algumas alterações para que a mesma funcionasse no Internet Explorer.

Dando continuidade ao tutorial, o HTML é bem simples e não tem segredo, basta você observar a estrutura abaixo utilizada para o tutorial:

01.<div id="container">
02.    <div id="galeria">
03.        <div id="bloc_img">
04.            <img src="img/foto1.jpg" alt="Foto 1" title="Foto 1" />
05.            <div id="img_1"><img src="img/foto1.jpg" alt="Foto 1" title="Foto 1" /></div>
06.            <div id="img_2"><img src="img/foto2.jpg" alt="Foto 2" title="Foto 2" /></div>
07.            <div id="img_3"><img src="img/foto3.jpg" alt="Foto 3" title="Foto 3" /></div>
08.        </div>
09.        <div id="bloc_bt">
10.            <a href="#img_1" class="bt_1"><img src="img/foto1-thumb.jpg" alt="Foto 1" title="Foto 1" /></a>
11.            <a href="#img_2" class="bt_2"><img src="img/foto2-thumb.jpg" alt="Foto 2" title="Foto 2" /></a>
12.            <a href="#img_3" class="bt_3"><img src="img/foto3-thumb.jpg" alt="Foto 3" title="Foto 3" /></a>
13.        </div>
14.    </div>

A div com o id “bloc_img” é onde devem ficar as imagens maiores e a div com o id “bloc_bt” é onde ficam os thumbnails das imagens. Se repararem existe uma imagem que é filha direta da div com o id “bloc_img”, ela será utilizada como imagem default da galeria, essa opção foi implementada por mim e não existia na galeria original.

Agora vamos as regras CSS:

01.#container {
02.    width:720px;
03.    margin:50px auto;
05.#galeria {
06.    background-color:#eee;
07.    border-bottom:3px solid #aaa;
08.    padding:10px;
09.    overflow:hidden;
10.    width:700px;
12.#bloc_img {
13.    width:700px;
14.    height:500px;
15.    margin-bottom:10px;
17.#bloc_img > img {position:absolute}
18.#img_1, #img_2, #img_3 {
19.    width:700px;
20.    height:500px;
21.    display:none;
22.    position:relative;
23.    z-index:2;
25.#img_1:target, #img_2:target, #img_3:target {display:block}
26.a.bt_1, a.bt_2, a.bt_3 {
27.    float:left;
28.    width:226px;
29.    height:130px;
30.    margin-right:11px;
32.a.bt_3 {margin-right:0}

O código acima nada mais faz do que ao usuário clicar nos thumbs das imagens aplicar display:block ao seu alvo. Vejam a demonstração. Se você reparar no código-fonte, verá que existe um JavaScript desenvolvido por Peter Ryan para habilitar o funcionamento da pseudo-classe :target no Internet Explorer.