Как использовать Canvas как ItemsPanel для ItemsControl в Silverlight 3

Я пытаюсь установить свойства Canvas в ItemsControl DataTemplate с Silverlight 3. Согласно этот пост, единственный способ сделать это - установить его с помощью ItemsContainerStyle для типа ContentPresenter, так как свойства Canvas действуют только на прямых дочерних элементов Canvas. Это, похоже, не работает в SL3, поскольку ItemControl не имеет свойства ItemsContainerStyle, поэтому я попробовал ListBox по рекомендации этой статьи, но он все равно не работает. Из XAML ниже я ожидал бы увидеть зеленый квадрат с цифрами 10, 30, 50, 70, каскадом от "NW" до "SE". Может ли кто-нибудь сказать мне, почему все они сложены на вершине eachother в углу NW?

<UserControl x:Class="TestControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:System="clr-namespace:System;assembly=mscorlib" >
    <StackPanel>
        <ListBox>
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas Background="Green" Width="100" Height="100" />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding}" />
                </DataTemplate>                
            </ListBox.ItemTemplate>
            <ListBox.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Setter Property="Canvas.Left" Value="{Binding}" />
                    <Setter Property="Canvas.Top" Value="{Binding}" />
                </Style>
            </ListBox.ItemContainerStyle>
            <ListBox.Items>
                <System:Int32>10</System:Int32>
                <System:Int32>30</System:Int32>
                <System:Int32>50</System:Int32>
                <System:Int32>70</System:Int32>
            </ListBox.Items>
        </ListBox>
    </StackPanel>
</UserControl>

Ответ 1

Я не уверен, что он будет работать в вашем сценарии, но в прошлом я делал это с использованием RenderTransform.

<ItemsControl>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas Background="Green" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBox Text="{Binding}">
                <TextBox.RenderTransform>
                    <TranslateTransform X="100" Y="100" />
                </TextBox.RenderTransform>
            </TextBox>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.Items>
        <System:Int32>10</System:Int32>
        <System:Int32>30</System:Int32>
        <System:Int32>50</System:Int32>
        <System:Int32>70</System:Int32>
    </ItemsControl.Items>
</ItemsControl>

Или в случае привязки вам нужно будет использовать конвертер

<ItemsControl>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas Background="Green" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBox Text="{Binding}" RenderTransform="{Binding Converter={StaticResource NumberToTransformGroupConverter}}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.Items>
        <System:Int32>10</System:Int32>
        <System:Int32>30</System:Int32>
        <System:Int32>50</System:Int32>
        <System:Int32>70</System:Int32>
    </ItemsControl.Items>
</ItemsControl>

Преобразователь

public void ConvertTo(object value, ...)
{
    int intValue = int.Parse(value.ToString());

    return new TransformGroup()
    {
        Children = new TransformCollection()
        {
            new TranslateTransform { X = intValue, Y = intValue }
        }
    };
}

Ответ 2

Silverlight4 не привязывается к прикрепленным свойствам в стиле. Я предлагаю использовать подход Дэвида Ансона, описанный здесь.

    <UserControl.Resources>
    <Style  x:Key="ScreenBindStyle" TargetType="ListBoxItem">
        <Setter Property="Helpers:SetterValueBindingHelper.PropertyBinding">
            <Setter.Value>
                <Helpers:SetterValueBindingHelper>
                    <Helpers:SetterValueBindingHelper Type="Canvas" Property="Left" Binding="{Binding LocationField.Value.X}" />
                    <Helpers:SetterValueBindingHelper Type="Canvas" Property="Top" Binding="{Binding LocationField.Value.Y}" />
                    <Helpers:SetterValueBindingHelper Type="Canvas" Property="ZIndex" Binding="{Binding ZIndex.Value}" />
                </Helpers:SetterValueBindingHelper>
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ListBoxItem">
                    <ContentPresenter/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</UserControl.Resources>

И в списке:

ItemContainerStyle="{StaticResource ScreenBindStyle}"

Ответ 3

Старый пост, но я пошел в ту же проблему, но теперь с SL5, которая теперь позволяет Binding в настройках стиля. Я пытался избежать использования ListBox (потому что он обрабатывает выбор и т.д.), А ItemsControl по-прежнему не имеет ItemContainerStyle. Поэтому я попробовал несколько вещей.

Я не нашел много вопросов, обсуждающих эту проблему, поэтому позвольте мне поделиться своим решением (извините, если он дублирует)

На самом деле, я нашел очень удобный способ решить проблему, добавив в ресурс ItemsControl неназванный Style:

<ItemsControl ItemsSource="{Binding Path=MyData}">
    <ItemsControl.Resources>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Top" Value="{Binding Path=Bounds.Top}"/>
            <Setter Property="Canvas.Left" Value="{Binding Path=Bounds.Left}"/>
            <Setter Property="Width" Value="{Binding Path=Bounds.Width}"/>
            <Setter Property="Height" Value="{Binding Path=Bounds.Height}"/>
        </Style>
    </ItemsControl.Resources>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate DataType="my:DataType">
            ...
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Работает как шарм в SL5:)

Ответ 4

Я не могу объяснить, что вы видите. Ваш Xaml разбит, по крайней мере, несколькими способами.

Сначала сам Xaml терпит неудачу, потому что это: -

<Style TargetType="ContentPresenter">

должен быть

<Style TargetType="ContentControl">

Контейнеры Item в случае ListBox имеют тип ListBoxItem, которые выводятся из ContentControl.

Тем не менее, размещение {Binding} в настройках стиля по-прежнему не работает. Думаю, вы предполагали, что стиль будет применяться к каждому элементу поочередно и получить его значение из текущего элемента. Однако, даже если привязка работала в стиле, существовал бы только стиль один, и он получил бы привязку данных из ListBox DataContext. Это другой DataContext, который применяется к каждому элементу ListBox (который в этом случае является каждым элементом в коллекции Items).

Тем не менее, я думаю, что у Бен есть разумное решение, которое устраняет этот подход.