Использование сетки в качестве элемента ItemsPanel для элемента ItemsControl в Silverlight 3
Можно ли сделать что-то вроде этого:
<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.
Возможно ли это? Я действительно хочу, чтобы моя сеть данных была привязана к данным.
Ответы
Ответ 1
То, что у вас есть, не будет работать, потому что 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
Grid
просто не подходит для использования, которое вы пытаетесь установить здесь. Ожидается, что набор доступных строк и столбцов будет определен заранее, прежде чем вы начнете назначать элементы ячейкам.
Если вы пытаетесь создать окно списка, которое использует как горизонтальное, так и вертикальное пространство, то, возможно, WrapPanel
из Silverlight Toolkit будет лучше.
С другой стороны, если вы пытаетесь создать "сетку данных", рассмотрите возможность переноса или группировки столбцов в каждой строке модели, тогда вы можете использовать DataGrid
вместо ListBox
Ответ 3
Я нашел другое интересное решение для этой проблемы:
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
.
Ответ 4
Это будет работать, только если вы знаете, сколько строк и столбцов вам нужно, и только в 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>
Ответ 5
Если вы заинтересованы в поддержке такого сценария в будущей версии Silverlight, проголосуйте за портирование макета Adobe Flex Grid, который будет работать отлично в таком сценарии
Ответ 6
Вам просто нужно создать два прикрепленных свойства для 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>