Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a Grid as the ItemsPanel for an ItemsControl in Silverlight 3

Is it possible to do something like this:

    <ListBox>
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid />
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBox Text="{Binding Text}" Grid.Column="{Binding Column}" Grid.Row="{Binding Row}"  />
            </DataTemplate>                
        </ListBox.ItemTemplate>
    </ListBox>

The items source would be something like a List of objects that had the Text, Column and Row properties.

Is this possible? I really want to have my data grid be data bound.

like image 376
skb Avatar asked Mar 06 '10 03:03

skb


2 Answers

What you have won't work because Silverlight wraps each item -- each instance of the DataTemplate -- in a ListBoxItem, and the Grid.Column and Grid.Row attached properties need to be applied to that ListBoxItem, not to the TextBox that becomes the content of that ListBoxItem.

The good news is that you can set attributes on the implicit ListBoxItem using ListBox.ItemContainerStyle.

The bad news is that ItemContainerStyle doesn't readily support binding. So you can't use it to set the Grid.Column and Grid.Row attached properties to attributes of the data item at hand.

One solution that I've used is to subclass ListBox and set up the binding in PrepareContainerForItemOverride. Here's a very crude, hardwired example:

public class GriderrificBox : ListBox
{
  protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
  {
    base.PrepareContainerForItemOverride(element, item);

    FrameworkElement fe = element as FrameworkElement;
    if (fe != null)
    {
      BindingOperations.SetBinding(fe, Grid.RowProperty,
        new Binding { Source = item, Path = new PropertyPath("Row") });
      BindingOperations.SetBinding(fe, Grid.ColumnProperty,
        new Binding { Source = item, Path = new PropertyPath("Column") });
    }
  }
}

Usage:

<local:GriderrificBox>
  <ListBox.ItemTemplate>
    <DataTemplate>
      <TextBox Text="{Binding Text}" />
    </DataTemplate>
  </ListBox.ItemTemplate>
  <ListBox.ItemsPanel>
    <ItemsPanelTemplate>
      <Grid />
    </ItemsPanelTemplate>
  </ListBox.ItemsPanel>
</local:GriderrificBox>

There are (at least) two major uglinesses with this code: first, you still need to explicitly specify ItemsPanel in XAML, even though the control works only with Grid panels; and second, the binding paths are hardwired into the code. The first can be addressed by using the normal control default style mechanism, and the second by defining properties such as RowBindingPath and ColumnBindingPath which PrepareItemForContainerOverride can consult instead of using hardwired paths. Hopefully enough to get you going anyway!

like image 109
itowlson Avatar answered Oct 26 '22 14:10

itowlson


The Grid just isn't suitable for the usage you are trying to put it to here. Its expecting the set of available rows and columns to be defined upfront before you start assigning elements to the cells.

If you are trying to create a list box that makes use of both horizontal and vertical space then perhaps a WrapPanel from the Silverlight Toolkit would be better basis.

On the other hand if you are trying to create a "Data grid" then consider transposing or grouping the columns in each row in the model, then you can use the DataGrid instead of a ListBox

like image 38
AnthonyWJones Avatar answered Oct 26 '22 14:10

AnthonyWJones