Windows Presentation Foundation

Hello, WPF

WPF from Scratch
Navigation Applications
Content Model
Layout
Controls
Data Binding
Dependency Properties
Resources
Styles and Control Templates
Graphics
Application Deployment
Where Are We?

Layout

Introduction
Layout Basics
DockPanel
StackPanel
Grid
Canvas
Viewbox
Text Layout
Common Layout Properties
When Content Doesn't Fit
Custom Layout
Where Are We?

Controls

Introduction
What Are Controls?
Handling Input
Built-In Controls
Where Are We?

Data Binding

Introduction
Without Data Binding
Data Binding
Binding to List Data
Data Sources
Master-Detail Binding
Where Are We?

Styles and Control Templates

Introduction
Without Styles
Inline Styles
Named Styles
Element-Typed Styles
Data Templates and Styles
Triggers
Control Templates
Where Are We?

Resources

Introduction
Creating and Using Resources
Resources and Styles
Binary Resources
Global Applications
Where Are We?

Graphics

Introduction
Graphics Fundamentals
Shapes
Brushes and Pens
Transformations
Visual-Layer Programming
Video and 3-D
Where Are We?

Animation

Animation Fundamentals
Timelines
Storyboards
Key Frame Animations
Creating Animations Procedurally
Where Are We?

Custom Controls

Introduction
Custom Control Basics
Choosing a Base Class
Custom Functionality
Templates
Default Visuals
Where Are We?

ClickOnce Deployment

A Brief History of Windows Deployment
ClickOnce: Local Install
The Pieces of ClickOnce
Publish Properties
Deploying Updates
ClickOnce: Express Applications
Choosing Local Install versus Express
Signing ClickOnce Applications
Programming for ClickOnce
Security Considerations
Where Are We?

Grid

Grid

Consider the document properties dialog shown in Figure 2-11. This is from the Microsoft SDK help viewer application, and we are going to add a similar dialog to our document viewer. Notice how the main area of the form is arranged as two columns. The column on the left contains labels, and the column in the middle contains information.

Figure 2-11. Document properties dialog


Achieving this kind of layout with a StackPanel is difficult, because it is not designed with two-dimensional alignment in mind. We could try to use nesting: Example 2-7 shows a vertical StackPanel with three rows, each with a horizontal StackPanel.

Example 2-7. Ineffective use of StackPanel
<StackPanel Orientation="Vertical">
    <StackPanel Orientation="Horizontal">
        <TextBlock>Protocol:</TextBlock>
        <TextBlock>Unknown Protocol</TextBlock>

    </StackPanel>
    <StackPanel Orientation="Horizontal">
        <TextBlock>Type:</TextBlock>
        <TextBlock>Not available</TextBlock>
    </StackPanel>

    <StackPanel Orientation="Horizontal">
        <TextBlock>Connection:</TextBlock>
        <TextBlock>Not encrypted</TextBlock>
    </StackPanel>
</StackPanel>


The result, shown in Figure 2-12, is not what we want at all. Each row has been arranged independently, and there is no consistency.

Figure 2-12. Inappropriate use of StackPanel


The Grid panel solves this problem. Rather than working a single row or a single column at a time, it aligns all elements into a grid that covers the whole area of the panel. This allows consistent positioning from one row to the next. Example 2-8 shows the same elements as Example 2-7, but arranged with a Grid rather than StackPanels.

Example 2-8. Grid Layout
<Grid ShowGridLines="True">
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>

    <TextBlock Grid.Column="0" Grid.Row="0">Protocol:</TextBlock>

    <TextBlock Grid.Column="1" Grid.Row="0">Unknown Protocol</TextBlock>
    <TextBlock Grid.Column="0" Grid.Row="1">Type:</TextBlock>
    <TextBlock Grid.Column="1" Grid.Row="1">Not available</TextBlock>
    <TextBlock Grid.Column="0" Grid.Row="2">Connection:</TextBlock>

    <TextBlock Grid.Column="1" Grid.Row="2">Not encrypted</TextBlock>

</Grid>

The Grid needs to know how many columns and rows we require, and we indicate this by specifying a series of ColumnDefinition and RowDefinition elements at the start. This may seem rather verbosea simple pair of properties on the Grid itself might seem like a simpler solution. However, you will typically want to control the characteristics of each column and row independently, so in practice, it makes sense to have elements representing them.

Notice that each element in the grid has its column and row specified explicitly using attached properties. This is mandatory: without these, everything ends up in column 0, row 0. (Grid uses a zero-based numbering scheme, so these correspond to the top-left corner.)

Grid, Element Order, and Z Order

You might be wondering why the Grid doesn't simply put items into the grid in the order in which they appearthis would remove the need for the Grid.Row and Grid.Column attached properties. There are a couple of reasons why it doesn't work this way.

Grid cells can be empty. If the grid's children simply filled the cells in order, you would need to put placeholders of some kind to indicate blank cells. But since elements indicate their grid position, you can leave cells empty simply by providing no content for that cell.

Cells can also contain multiple elements. In this case, the order in which the relevant elements are listed in the markup determines which appears "on top." Elements that appear later in the document are drawn over those that appear earlier. The order in which overlapping elements are drawn is usually referred to as the Z order . This is because the x- and y-axes are traditionally the ones used for drawing onscreen, so the z-axis would logically be used to determine how overlapping elements are ordered.

In general, panels that allow their children to overlap (e.g., Grid and Canvas) rely on the order in which elements appear in the XAML to determine the Z order.


Figure 2-13 shows the result of Example 2-8. This example has lines showing the grid outline, because we enabled the ShowGridLines property. You would not normally do this on a finalized designthis feature is intended to make it easy to see how the Grid has divided up the available space. With grid lines displayed, it is clear that the Grid has made all the columns the same width and all the rows the same height.

Figure 2-13. Grid layout


This default "one size fits all" behavior is useful when you want all the items in the grid to be the same size, but it's not what we want here. It would make more sense for the column on the left to be wide enough to contain the labels and for the column on the right to be allocated the remaining space. Fortunately, the Grid provides a variety of options for managing column width and row height.

2.4.1. Grid Column Widths and Row Heights

The column widths and row heights in a Grid are configured using the ColumnDefinition and RowDefinition elements. There are three options: fixed size, automatic size, and proportional sizing.

Fixed sizing is the simplest to understand, but often the most effort to use, as you end up having to do all of the work yourself. You can specify the Width of a column or the Height of a row in logical pixels. (A logical pixel is 1/96th of an inch. WPF's coordinate system is described in Chapter 7.) Example 2-9 shows a modified version of the column definitions in Example 2-8, specifying a fixed width for the first column.

Example 2-9. Fixed column width
...
<Grid.ColumnDefinitions>
    <ColumnDefinition Width="50" />
    <ColumnDefinition />
</Grid.ColumnDefinitions>
...

Figure 2-14 illustrates the main problem with using fixed column widths . If you make the column too narrow, the contents will simply be cropped. Fixed widths and heights may seem an attractive idea because they give you complete control, but in practice they tend to be inconvenient. If you change the text or the font, you will need to modify the sizes to match. Localization of strings will also require the sizes to be changed. (See Chapter 6 for more information about localization.) So, in practice, fixed widths and heights are not what you will normally want to use.

Figure 2-14. Fixed-width column truncation


The most appropriate sizing strategy for our label column will be automatic sizing. This tells the Grid to make the column wide enough to contain the widest element. Example 2-10 shows a modified version of the column and row definitions from Example 2-8, specifying automatic width for the first column and automatic heights for all of the rows.

Example 2-10. Automatic width and height
...
<Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto" />
    <ColumnDefinition />
</Grid.ColumnDefinitions>

<Grid.RowDefinitions>

    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
</Grid.RowDefinitions>
...

This is not quite right yetas you can see from Figure 2-15, the Grid has not left any space around the text, so the results seem rather cramped. The solution is exactly the same as it was for the StackPanel: we simply use the Margin property on the TextBlock elements in the Grid to indicate that we want some breathing room around the text. The Grid will honor this, giving us the layout we require.

Figure 2-15. Automatic width and height


If the idea of adding a Margin attribute to every single element sounds tedious, don't worry. We can give all of the TextBlock elements the same margin by defining a style, as Example 2-11 shows.

Example 2-11. Applying a consistent margin with a style
<Grid ShowGridLines="True">
    <Grid.Resources>
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="Margin" Value="5,3" />
        </Style>

    </Grid.Resources>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
... as before

As Figure 2-16 shows, this provides the better-spaced layout we require. (Styles are described in detail in Chapter 5.)

Figure 2-16. Using margins


The final mechanism for specifying width and height in a Grid is the proportional method . This is sometimes called "star" sizing because of the syntax used to represent it in XAML. If you set the width or height of a column or row to be "*", this tells the Grid that it should fill all the leftover space after any fixed and automatic items have taken their share. If you have multiple items set to "*", the space is shared evenly between them.

The default value for column width and row height is "*", so you have already seen the effect of this. As Figure 2-13 shows, when we don't specify column widths or row heights, each cell ends up with exactly the same amount of space.

The star syntax is a little more flexible than this. Rather than dividing up space evenly between all the rows or columns marked with a star, we can choose a proportional distribution. Consider this set of row definitions:

<Grid.RowDefinitions>
    <RowDefinition Height="Auto" />

    <RowDefinition Height="2*" />
    <RowDefinition Height="*" />
</Grid.RowDefinitions>

Here, the first row has been set to size automatically, and the other two rows both use proportional sizing. However, the middle row has been marked as "2*". This indicates that it wants to be given twice as much of the available space as the row marked with "*". For example, if the grid's total height was 350, and the first row's automatic height came out as 50, this would leave 300 for the other rows. The second row's height would be 200, and the third row's height would be 100. The results are shown in Figure 2-17.

Figure 2-17. Proportional Grid sizing


The numbers before the "*" specify relative sizes, not absolute sizes. If the example above were modified to use "6*" and "3*" instead of "2*" and "*", the net result would be exactly the same. It's equivalent to saying that you want the rows to use six ninths and three ninths of the available space, instead of saying that you want them to use two thirds and one thirdit's just two ways of expressing the same ratio.

If you are familiar with HTML, you may have been wondering whether you can use percentage sizes. You can't, but this * mechanism lets you achieve the same effects.


You may have noticed that regardless of which of the three grid sizing strategies we choose, we end up using the same Width and Height properties. Each of these properties contains both a number and the sizing system. Width and Height are both of type GridLength . The GridLength type holds a number and a unit type. The number is stored as a Double, and the unit type is represented by the GridUnitType enumeration.

For a fixed size, the unit type is Pixel.[*] In XAML, this is indicated by providing just a number. For automatic sizing , the unit type is Auto and no number is required. In XAML, this is indicated by the string "Auto". For proportional sizing, the unit type is Star. In XAML this is indicated either by just "*" or a number and a stare.g., "3.5*". If you use a mixture of unit types in a single grid, the grid will allocate space to fixed-size and auto-sized columns or rows first, and then divide the remaining space up amongst any star-sized items.

[*] WPF's resolution independence means that this isn't necessarily a real physical pixel. This issue is discussed in Chapter 7.

2.4.2. Spanning Multiple Rows and Columns

Looking at the Properties dialog shown earlier in Figure 2-11, there is a feature we have left out. The dialog has two horizontal lines dividing the UI into three sections. However, the aligned columns span the whole window, straddling these dividing lines.

It would be inconvenient to try to achieve a layout like this with multiple grids. If you used one for each section of the window, you could keep the columns aligned in all the grids by using fixed column widths. As discussed earlier, use of fixed widths is inconvenient because it tends to require manual adjustment of the widths whenever anything changes. With this layout, it becomes triply inconvenient: you would have to change all three grids every time anything changed.

Fortunately, it is possible to add these dividing lines without splitting the UI up into separate grids. The way to do this is to put the dividing lines into cells that span all of the columns in the grid. An element indicates to its parent Grid that it would like to span multiple columns by using the attached Grid.ColumnSpan property. Example 2-12 illustrates this approach.

Example 2-12. Using Grid.ColumnSpan
<Grid>
    <Grid.Resources>
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="Margin" Value="5,3" />
        </Style>

    </Grid.Resources>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>

        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />

        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>


    <TextBlock Grid.Column="0" Grid.Row="0">Title:</TextBlock>
    <TextBlock Grid.Column="1" Grid.Row="0">Information Overload</TextBlock>

    <Rectangle Grid.Row="1" Grid.ColumnSpan="2" Margin="5"
                 Height="1" Fill="Black" />

    <TextBlock Grid.Column="0" Grid.Row="2">Protocol:</TextBlock>
    <TextBlock Grid.Column="1" Grid.Row="2">Unknown Protocol</TextBlock>
    <TextBlock Grid.Column="0" Grid.Row="3">Type:</TextBlock>

    <TextBlock Grid.Column="1" Grid.Row="3">Not available</TextBlock>
    <TextBlock Grid.Column="0" Grid.Row="4">Connection:</TextBlock>
    <TextBlock Grid.Column="1" Grid.Row="4">Not encrypted</TextBlock>

    <Rectangle Grid.Row="5" Grid.ColumnSpan="2" Margin="5"
                 Height="1" Fill="Black" />

    <TextBlock Grid.Column="0" Grid.Row="6">Created:</TextBlock>
    <TextBlock Grid.Column="1" Grid.Row="6">Not available</TextBlock>
    <TextBlock Grid.Column="0" Grid.Row="7">Modified:</TextBlock>
    <TextBlock Grid.Column="1" Grid.Row="7">Not available</TextBlock>

</Grid>

Example 2-12 uses a single Grid to show three sets of properties. These sets are separated by thin Rectangle elements, using Grid.ColumnSpan to fill the whole width of the Grid. Because a single Grid is used for all three sections, the columns remain aligned across all three sections, as you can see in Figure 2-18. If we had used three separate grids with the left-most column set to use automatic width, each would have chosen its own width, causing the right-hand columns to be misaligned.

Figure 2-18. Dividing lines spanning multiple columns


The Grid class also defines a Grid.RowSpan attached property. This works in exactly the same way as Grid.ColumnSpan, but vertically.

You are free to use both Grid.RowSpan and Grid.ColumnSpan on the same elementany element may occupy as many grid cells as it likes. Also, note that you are free to put multiple overlapping items into each cell.

Example 2-13 illustrates both of these techniques. It adds two Rectangle elements to color in areas of the grid. The first spans multiple rows, and the second spans both multiple rows and multiple columns. Both Rectangle elements occupy cells in the Grid that are also occupied by text.

Example 2-13. Multiple Items in a Grid Cell

<Rectangle Grid.Column="1" Grid.Row="2" Grid.RowSpan="3"
        Margin="5,3" Fill="Cyan" />
<Rectangle Grid.Column="0" Grid.Row="6" Grid.ColumnSpan="2"  Grid.RowSpan="2"
        Margin="5,3" Fill="Khaki" />

<TextBlock Grid.Column="0" Grid.Row="0">Title:</TextBlock>
...as before

Figure 2-19 shows the results. Note that the order in which the elements appear in the markup is crucial, as it determines the Z order for overlapping elements. In Example 2-13, the Rectangle elements were added before the TextBlock items whose cells they share. This means that the colored rectangles appear behind the text, rather than obscuring them. If the rectangles had been added at the end of the Grid after the text, they would have been drawn over the text.

Figure 2-19. Overlapping Grid items (Color Plate 3)


This example illustrates why the Grid requires the row and column of each item to be specified explicitly, rather than being implied by the order of the elements. Cells can be shared by multiple elements. Elements can span multiple cells. This makes it impossible for the Grid to guess which element goes in which cell.

2.4.3. Consistency Across Multiple Grids

Although the row and column spanning features described in the previous section often make it possible to arrange your UI as you need, it will not always be possible to put all of the information you wish to present into a single Grid element. For example, consider a scrollable Grid with headings. You could just put headings and contents into a single Grid, and then place that Grid in a ScrollViewer to make it scrollable, but this suffers from a problem, which Example 2-14 illustrates.

Example 2-14. Grid in ScrollViewer
<ScrollViewer>
    <Grid>
        <Grid.Resources>
            <Style TargetType="{x:Type TextBlock}">
                <Setter Property="Margin" Value="5,3" />

            </Style>
        </Grid.Resources>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />

        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Border Grid.Column="0" Grid.Row="0"
                Background="LightGray" BorderBrush="Gray"
                BorderThickness="1">
            <TextBlock>Title</TextBlock>
        </Border>
        <Border Grid.Column="1" Grid.Row="0"
                Background="LightGray" BorderBrush="Gray"
                BorderThickness="1">
            <TextBlock>Location</TextBlock>

        </Border>
        <Border Grid.Column="2" Grid.Row="0" Background="LightGray"
                BorderBrush="Gray" BorderThickness="1">
            <TextBlock>Rank</TextBlock>
        </Border>

        <TextBlock Grid.Column="0" Grid.Row="1">

            Mastering Visual Studio .NET
        </TextBlock>
        <TextBlock Grid.Column="1" Grid.Row="1">O'Reilly Media, Inc.</TextBlock>
        <TextBlock Grid.Column="2" Grid.Row="1">1</TextBlock>

        <TextBlock Grid.Column="0" Grid.Row="2">IanG on Tap</TextBlock>

        <TextBlock Grid.Column="1" Grid.Row="2">The Internet</TextBlock>
        <TextBlock Grid.Column="2" Grid.Row="2">2</TextBlock>
    </Grid>
</ScrollViewer>

Figure 2-20 shows the results. If you look at the right-hand side you can see that the scrollbar runs the entire height of the Grid, including the header line with the titles. This means that as soon as you scroll down, the headings will disappear. This is not particularly helpful.

Figure 2-20. Grid in ScrollViewer


We could solve this by using two grids, one for the header, and one for the main results area. Only the second grid would be placed inside a ScrollViewer. Figure 2-21 shows the results.

Figure 2-21. Separate grid in ScrollViewer


The scrollbar is now applied just to the part that needs to be scrollable, but the alignment is all wrong. Each Grid has arranged its columns independently, so the headings no longer line up with the main contents.

The Grid supports shared-size groups to solve this problem. A shared-size group is simply a named group of columns that will all have the same width, even though they are in different grids. We can use this approach to keep the headings Grid consistent with the scrollable contents Grid, as shown in Example 2-15.

Example 2-15. Shared-size groups
<DockPanel Grid.IsSharedSizeScope="True">
    <DockPanel.Resources>
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="Margin" Value="5,3" />
        </Style>

    </DockPanel.Resources>
    <Grid DockPanel.Dock="Top">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" SharedSizeGroup="Location" />

            <ColumnDefinition Width="Auto" SharedSizeGroup="Rank" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />

        </Grid.RowDefinitions>

        <Border Grid.Column="0" Grid.Row="0" BorderThickness="1"
                Background="LightGray" BorderBrush="Gray">
            <TextBlock>Title</TextBlock>
        </Border>
        <Border Grid.Column="1" Grid.Row="0" BorderThickness="1"
                Background="LightGray" BorderBrush="Gray">

            <TextBlock>Location</TextBlock>
        </Border>
        <Border Grid.Column="2" Grid.Row="0" BorderThickness="1"
                Grid.ColumnSpan="2"
                Background="LightGray" BorderBrush="Gray">
            <TextBlock>Rank</TextBlock>
        </Border>

        <FrameworkElement Grid.Column="3"
           Width="{DynamicResource {x:Static SystemParameters.ScrollWidthKey}}" />

    </Grid>
    <ScrollViewer>
        <Grid>
            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" SharedSizeGroup="Location" />
                <ColumnDefinition Width="Auto" SharedSizeGroup="Rank" />
            </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>

            <TextBlock Grid.Column="0" Grid.Row="0">
                Mastering Visual Studio .NET
            </TextBlock>

            <TextBlock Grid.Column="1" Grid.Row="0">
                O'Reilly Media, Inc.
            </TextBlock>
            <TextBlock Grid.Column="2" Grid.Row="0">1</TextBlock>

            <TextBlock Grid.Column="0" Grid.Row="1">IanG on Tap</TextBlock>

            <TextBlock Grid.Column="1" Grid.Row="1">The Internet</TextBlock>
            <TextBlock Grid.Column="2" Grid.Row="1">2</TextBlock>
        </Grid>
    </ScrollViewer>
</DockPanel>


Example 2-15 illustrates the use of shared-size groups . The overall layout is defined by a DockPanel, using the attached Dock.Top property to position the header Grid at the top, and allowing the ScrollViewer to fill the remaining space.

Shared-size groups are identified by strings. Strings are prone to name collisionsit's quite possible that two developers independently working on different parts of the user interface might end up choosing the same name for their shared-size groups, inadvertently causing unrelated columns to have the same size. To avoid this problem, WPF requires that we indicate the scope within which we wish to use a particular shared-size group. This is the purpose of the Grid.IsSharedSizeScope attached property on the DockPanelit indicates that the DockPanel is the common ancestor, and prevents the groups defined inside the DockPanel from being associated with any groups defined elsewhere in the UI.

Having defined the scope of the names, using shared-size groups is very straightforward. We just apply the SharedSizeGroup attribute to the "Location" and "Rank" ColumnDefinition, ensuring that the columns are sized consistently across the two grids. Figure 2-22 shows the results.

Figure 2-22. Shared-size groups


The ScrollViewer adds a scrollbar to the display, and this meant that a small hack was required to get this working on the pre-release build of WPF that was current at the time of writing. This scrollbar takes away some space from the main Grid, making it slightly narrower than the header Grid. Remember that the "Title" column's size is set to "*" meaning that it should fill all available space. The ScrollViewer's scrollbar eats into this space, making the "Title" column in the main Grid slightly narrower than the one in the header Grid, destroying the alignment.

You might think that we could fix this by adding a shared-size group for the title column. Unfortunately, specifying a shared-size group seems to disable the "*" behaviorthe column reverts to automatic sizing.

The fix for this is to add an extra column to the header row. This row needs to be exactly the same width as the scrollbar added by the ScrollViewer. So we have added a fourth column, containing a FrameworkElement, with its Width set to the system scroll-width metric in order to make sure that it is exactly the same width as a scrollbar. (We are using a DynamicResource reference to retrieve this system parameter. This technique is described in Chapter 6.) It's unusual to use a FrameworkElement directly, but since we just need something that takes up space but has no appearance, it makes a good lightweight filler. Its presence keeps all of the columns perfectly aligned across the two grids. (A more elegant solution will no doubt be possible in the final release of WPF.)

The Grid is the most powerful of the built-in panels. You can get the Grid to do anything that DockPanel and StackPanel can dothose simpler elements are provided for convenience. For nontrivial user interfaces, the Grid is likely to be the best choice for your top-level GUI layout, as well as being useful for detailed internal layout.



©2008 FAQ - WPF Labs - Discuss - Terms of Use - Privacy Policy - About WPF
- Interview Questions - Sharepoint Articles - Interview Questions Resource Library - All about LINQ - MS Knowledgebase Articles - Electronics and Hardware discussions