views:

189

answers:

2

I am quite new to WPF. I have a page that displays data from a SQL database using L2S. The L2S returns a DataTable that contains all the available options to choose from for a specific area. Every row it returns from the DataBase needs to be a checkbox and I want to put those checkboxes in a stackpanel.

Am I looking at databinding to the StackPanel? That feels wrong... I was guessing I need to loop through the DataTable and create checkbox items for each row and then add them at runtime to the StackPanel. Is this correct? Is returning a DataTable part of my problem?

I see that StackPanel has a DataContext property but I can't just set that cause it wouldn't know to make each item a checkbox, correct?

+4  A: 

You likely want an ItemsControl. This allows you to present a series of items using a specified DataTemplate. You can either do this inline of the ItemsControl:

   <ItemsControl ItemsSource="{Binding MyCollectionOfItems}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <CheckBox IsChecked="{Binding NameOfTheCheckedPropertyOnEachItem}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>  

or reference the data template explicitly from a resource... something more like:

    <!-- In some parent resource section -->
    <DataTemplate x:Key="MyDataTemplateName">
        <CheckBox IsChecked="{Binding NameOfTheCheckedPropertyOnEachItem}"/>
    </DataTemplate>

    <!-- ... -->

    <ItemsControl ItemsSource="{Binding MyCollectionOfItems}" ItemTemplate="{StaticResource MyDataTemplateName}">
    </ItemsControl>

Or you can define a DataTemplate that defines the look and feel for your bound class. (Note that if your Linq-to-SQL is projecting into an Anonymous Type, this isn't an option) Something like:

        <!-- In some parent resource section -->
        <DataTemplate DataType="{x:Type MyBoundClass}">
            <CheckBox IsChecked="{Binding NameOfTheCheckedPropertyOnEachItem}"/>
        </DataTemplate>

    <!-- ... -->

    <ItemsControl ItemsSource="{Binding MyCollectionOfItems}">
    </ItemsControl>

WPF will then look for a DataTemplate that matches the DataType of each of the items in your collection. Note that this can be VERY helpful for binding heterogeneous collections which need different presentations.

You CAN bind the DataContext of the Stackpanel, but there is no inherent logic about repeating a template for each element of data. It simply provides a context for child controls and contained {Binding ...} statements. All of the controls that handle repeating data descend from ItemsControl and take their data in through the ItemsSource property.

Ben Von Handorf
+1 Good, thorough answer.
Benny Jobigan
+2  A: 

In addition to Ben Von Handorf's answer, I thought it would be useful to mention that you can also change the way that an ItemsControl arranges its elements by changing the ItemsPanel. For example:

<ListBox ItemsSource="{Binding MyCollection}">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
</ListBox>

This makes the ListBox arrange items horizontally instead of vertically. You could also use a WrapPanel if you want the items to "flow" like text (or span if you know html). WrapPanel can also be oriented horizontally or vertically. (But try not to use WrapPanel for large collections of items because it's not virtualizing, and will cause some big lag.)

You can even create your own custom panels and substitute them. I once made a panel that laid out it's items randomly, and used that for a ListBox. Each item in my bound collection was displayed in a random position within the ListBox. Each time the layout refreshed, the items got a new position.

Benny Jobigan
Excellent point... I should have covered that. Thanks for filling in my hole. :)
Ben Von Handorf