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

6

11

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.

Maseru answered 6/3, 2010 at 3:35 Comment(1)
Its not clear what you full intentions are. Are you really after ListBox semantics which is a list (possibly requiring scrolling) of N items where one or more may be selected?Couscous
T
3

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!

Trilinear answered 6/3, 2010 at 3:54 Comment(4)
There is still the problem that a Grid requires the set of Rows and Columns to be defined in its RowDefinitions and ColumnDefinitions collection.Couscous
I don't know if this works. Part of the problem is that there is no BindingOperations (at least that I can find) in SL3. I tried to just do a SetBinding on the element object, but that didn't work either. itowlson: have you gotten this to work with SL3?Maseru
BindingOperations is a static class in the System.Windows.Data namespace. Yes, I have had this working with SL3 (albeit using a Canvas rather than a Grid, but the concept is the same, with due acknowledgement to the issue raised by AnthonyWJones).Trilinear
Sorry for the thread necromancy, but I found this very useful, and have a recommendation for making it at least a bit more flexible. I used your method, but added additional dependency properties to my custom ItemsControl for each of the "paths" in question, e.g. RowPath. That way there's no hard-coded strings in the PrepareContainerForItemOverride, I just set a default for the DP and let the consuming xaml set a new value if needed.Romulus
C
2

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

Couscous answered 6/3, 2010 at 15:53 Comment(0)
S
2

I found an other interesting solution for this problem: http://www.scottlogic.co.uk/blog/colin/2010/11/using-a-grid-as-the-panel-for-an-itemscontrol/

The Example is done with a ItemsCountrol - but i am pretty sure that it also works with a ListBox

The Result looks like:

<ItemsControl ItemsSource="{Binding}">  
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <!-- use the ItemsPerRow attached property to dynamically add rows -->
      <Grid local:GridUtils.ItemsPerRow="1"
          ShowGridLines="True"/>
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <TextBlock Text="{Binding}"/>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

and does need to implement a local:GridUtils.ItemsPerRow attached property.

Sayles answered 6/10, 2011 at 13:27 Comment(0)
C
2

This will work only if you know how many rows and columns you need and only in Silverlight 5. (You can't bind value in setter property in silverlight 4.)

<Grid x:Name="LayoutRoot" Background="White">
        <ItemsControl x:Name="ic" Background="#FFE25454">
            <ItemsControl.Resources>
                <Style TargetType="ContentPresenter">
                    <Setter Property="Grid.Row" Value="{Binding X}"/>
                    <Setter Property="Grid.Column" Value="{Binding Y}"/>
                </Style>
            </ItemsControl.Resources>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid ShowGridLines="True">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions></Grid>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>

                    <TextBlock Text="{Binding text}"/>

                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
Courtmartial answered 9/6, 2012 at 13:21 Comment(0)
A
0

If you are interested in support of such scenario in future version of Silverlight please vote for porting of Adobe Flex Grid layout, which will work perfectly in such scenario

Aglimmer answered 21/4, 2010 at 13:44 Comment(0)
L
-1

You just need create two attached properties for Grid (something like ColumnsNumber and RowsNumber which would be fill ColumnDefinitions and RowDefenitions collections). And then override default ItemContainerStyle in ItemsControl (because all items in ItemsControl wrapped by ContentPresenters). Code example:

<ItemsControl>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid
                behavior:GridBehavior.ColumnsNumber="{Binding}"
                behavior:GridBehavior.RowsNumber="{Binding}">
            </Grid>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <ItemsControl.ItemsSource>
        <Binding />
    </ItemsControl.ItemsSource>

    <ItemsControl.ItemTemplate>
        <DataTemplate />
    </ItemsControl.ItemTemplate>

    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Grid.Column" Value="{Binding}" />
            <Setter Property="Grid.Row" Value="{Binding}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>
Legpull answered 14/7, 2011 at 12:59 Comment(1)
There is no ItemContainerStyle in Silverlight.Romulus

© 2022 - 2024 — McMap. All rights reserved.