开发者

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.Ro开发者_开发知识库w="{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.


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!


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


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.


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>


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


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>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜