Использование сетки в качестве ItemsPanel для ItemsControl в Silverlight 3 - PullRequest
11 голосов
/ 06 марта 2010

Можно ли сделать что-то вроде этого:

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

Источником элементов будет что-то вроде списка объектов со свойствами Text, Column и Row.

Is thisвозможный?Я действительно хочу, чтобы моя сетка данных была привязана к данным.

Ответы [ 6 ]

3 голосов
/ 06 марта 2010

То, что у вас не получится, потому что Silverlight упаковывает каждый элемент - каждый экземпляр DataTemplate - в ListBoxItem, а присоединенные свойства Grid.Column и Grid.Row должны применяться к этому ListBoxItem, а не кTextBox, который становится содержимым этого ListBoxItem.

Хорошая новость заключается в том, что вы можете устанавливать атрибуты неявного ListBoxItem с помощью ListBox.ItemContainerStyle.

Плохая новость заключается в том, что ItemContainerStyle не поддерживаетсвязывание.Поэтому вы не можете использовать его для установки свойств, прикрепленных к Grid.Column и Grid.Row, к атрибутам элемента данных под рукой.

Одно из использованных мной решений - это создать подкласс ListBox и настроить привязку.в PrepareContainerForItemOverride.Вот очень грубый, зашитый пример:

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") });
    }
  }
}

Использование:

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

В этом коде есть (по крайней мере) два основных уродства: во-первых, вам все равно нужно явно указать ItemsPanelв XAML, хотя элемент управления работает только с панелями Grid;и во-вторых, пути привязки встроены в код.Первая может быть решена с помощью обычного механизма управления по умолчанию, а вторая - путем определения свойств, таких как RowBindingPath и ColumnBindingPath, с которыми PrepareItemForContainerOverride может обращаться вместо использования проводных путей.Надеюсь, в любом случае, чтобы вы пошли!

2 голосов
/ 09 июня 2012

Это будет работать, только если вы знаете, сколько строк и столбцов вам нужно, и только в Silverlight 5. (Невозможно связать значение в свойстве setter в 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>
2 голосов
/ 06 октября 2011

Я нашел другое интересное решение для этой проблемы: http://www.scottlogic.co.uk/blog/colin/2010/11/using-a-grid-as-the-panel-for-an-itemscontrol/

Пример сделан с ItemsCountrol - но я почти уверен, что он также работает с ListBox

Результат выглядит следующим образом:

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

и требует реализации local:GridUtils.ItemsPerRow присоединенного свойства.

2 голосов
/ 06 марта 2010

Grid просто не подходит для использования, которое вы пытаетесь использовать здесь.Ожидается, что набор доступных строк и столбцов будет определен заранее, прежде чем вы начнете присваивать элементы ячейкам.

Если вы пытаетесь создать список, который использует как горизонтальное, так и вертикальное пространство, то, возможно, WrapPanel из набора инструментов Silverlight было бы лучшей основой.

С другой стороны, если вы пытаетесь создать «сетку данных», тогда рассмотрите возможность транспонирования или группировки столбцов в каждой строке в модели, тогда вы можете использоватьDataGrid вместо ListBox

0 голосов
/ 14 июля 2011

Вам просто нужно создать два прикрепленных свойства для Grid (что-то вроде ColumnsNumber и RowsNumber, которые будут заполнять коллекции ColumnDefinitions и RowDefenitions).А затем переопределите по умолчанию ItemContainerStyle в ItemsControl (поскольку все элементы в ItemsControl обернуты ContentPresenters).Пример кода:

<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 голосов
/ 21 апреля 2010

Если вы заинтересованы в поддержке такого сценария в будущей версии Silverlight, проголосуйте за портирование макета Adobe Flex Grid , который будет отлично работать в таком сценарии

...