如何在Silverlight 3中将Canvas用作ItemsControl的ItemsPanel

11

我正在尝试在Silverlight 3中使用ItemsControl DataTemplate设置Canvas属性。根据此帖子,唯一的方法是使用ContentPresenter类型的ItemsContainerStyle进行设置,因为Canvas属性只对Canvas的直接子元素生效。但是这在SL3中似乎不起作用,因为ItemsControl没有ItemsContainerStyle属性,所以我尝试了ListBox,正如这篇文章所建议的,但仍然不起作用。从下面的XAML中,我希望看到一个绿色的正方形,并且数字10、30、50、70从“NW”到“SE”方向递增。有人能告诉我为什么它们都堆叠在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
谢谢skb,这回答了我的问题,也就是如何在WPF中实现这个 :-)正如你所说,在WPF中它运行得很好。 - MikeKulls
4个回答

7

我不确定它是否适用于你的情况,但我过去使用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 }
        }
    };
}

1
在所呈现的场景中,这个方案运作得足够好。但如果将其放置在大小不足的ScrollViewer或Stackpanel中,问题就会浮出水面。但你唯一能做的就是全力以赴地编写自定义面板,以便在测量和排列阶段提供所需的行为。 - AnthonyWJones

3

Silverlight4无法在样式中绑定附加属性。我建议使用David Anson在这里描述的方法。

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

2

虽然这篇文章有些老,但是我也遇到了同样的问题,只不过现在使用的是 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中表现出色 :)

0

我也无法解释您所看到的内容。您的Xaml文件存在至少几个问题。

首先,Xaml本身失败了,因为这个原因:

<Style TargetType="ContentPresenter">

应该是

<Style TargetType="ContentControl">

ListBox 中的项目容器是 ListBoxItem 类型,它们派生自 ContentControl

即使在样式设置器中放置 {Binding},仍然无法工作。我想你可能想象着样式会依次应用于每个项目,并从当前项目获取其值。但是,即使绑定在样式中起作用,也只有一个样式,并且它将从 ListBox 的 DataContext 获取其数据绑定。这是一个不同的 DataContext,适用于每个 ListBox 项(在此情况下是 Items 集合中的每个项目)。

尽管如此,我认为 Ben 已经有了一个合理的解决方案,可以消除这种方法。


1
嗨,安东尼,我可能误读了你的答案,但从我所看到的来看,你写的几乎所有内容都是不准确的。首先,XAML并不会因为目标类型而失败,事实上,如果按照你建议的更改,它会失败。其次,样式中的绑定确实有效。第三,绑定不是从列表框中获取上下文,而是从列表框项中获取。至于只有一个样式,我怀疑该样式会被克隆。这里重要的是,一开始提出的解决方案确实可行,我正在WPF中使用它,但在Silverlight中无法使用。 - MikeKulls
@MikeKulls:是的,你误解了我的意思;)。让我从你反对的第二点开始,因为这才是误解的真正根源。我说“在样式设置器中放置{Binding}仍然不起作用”,并且“即使绑定起作用”。从那时起,一切都是假设的,因为正如你所说,它不起作用。假设此时绑定已经解析,样式的“Value”属性将绑定到此__公共__值(注意不是绑定本身),并将在所有项目中使用。最后,即使__一切__都起作用,TargetType也是__不正确__的。 - AnthonyWJones
但是这个解决方案确实有效(我哪里说它无效了?),绑定在样式中也能正常工作。我将这个解决方案几乎完全复制粘贴到一个生产应用程序中,可以肯定地说它确实有效。请记住,skb表示该解决方案适用于WPF,但不适用于Silverlight。 - MikeKulls

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接