如何使用ItemsControl和WrapPanel以逗号分隔的方式列出项目?

4
我有一个ItemsControl,它通过逗号分隔列出项目。代码如下:
<ItemsControl ItemsSource="{Binding MyItems}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text=", " 
                           Name="commaTextBlock"/>
                <TextBlock Text="{Binding}"/>
            </StackPanel>
            <!-- Hide the first comma -->
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding 
                        RelativeSource={RelativeSource PreviousData}}" 
                             Value="{x:Null}">
                    <Setter Property="Visibility" 
                            TargetName="commaTextBlock" 
                            Value="Collapsed"/>
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

结果类似于这样: 项目1,项目2,项目3。
现在,我想使用WrapPanel而不是StackPanel作为ItemsPanelTemplate来完成相同的操作。我测试过了,它运行得很好,除了一个小细节,它会做出下面的效果:
项目1, 项目2,
项目3
当然,这是因为逗号在每个元素之前,并且我隐藏了第一个元素。我想将逗号放在每个元素之后,并隐藏最后一个元素,使结果如下所示:
项目1,项目2,
项目3
如果存在类似于NextData的东西(那么我可以绑定到它而不是PreviousData),那么这将非常简单,但不幸的是并没有这样的东西(或者我还没有找到)。有人有解决这个问题的想法吗?
谢谢。

2
+1 这是一个非常方便的 WPF 示例,即使没有答案也很有用。 - Qwertie
2个回答

4

我为类似的问题编写了一个可见性转换器:

<TextBlock Text=", " Name="commaTextBlock"> 
   <TextBlock.Visibility>
       <MultiBinding Converter="{StaticResource commaVisibilityConverter}">
       <Binding ElementName="myItemsControl" Path="ItemsSource"/>
       <Binding/>
    </MultiBinding>                            
   </TextBlock.Visibility>
</TextBlock>

同时,这里还有转换器的逻辑:

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var collection = values[0] as IEnumerable<MyItem>;
        var item = values[1] as MyItem;
        if ((collection != null) && (item != null) && (collection.Count() > 0))
        {
            return collection.Last() == item ? Visibility.Hidden : Visibility.Visible;
        }          

        return Visibility.Hidden;
    }

很抱歉我接受了其他的解决方案,但无论如何还是感谢你的帮助! - Carl

3
也许你可以尝试使用多绑定和转换器。类似这样:
<ItemsControl.ItemTemplate>
  <DataTemplate>
    <TextBlock>
      <TextBlock.Text>
        <MultiBinding Converter="{StaticResource myConverter}">
          <Binding Mode="OneWay"/>
          <Binding ElementName="root" Path="ItemsSource" Mode="OneWay"/>
        </MultiBinding>
      </TextBlock.Text>
    </TextBlock>
  </DataTemplate>
</ItemsControl.ItemTemplate>

其中root是您的ItemsControl的名称。

并编写一个转换器来检查位置:

public class MyConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var text = values[0] as string;
        var list = values[1] as ObservableCollection<string>;

        //check for null etc...

        if (list.IndexOf(text) == list.Count - 1)
            return text;

        return string.Format("{0}, ", text);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
       //ignore this, not gonna happen with OneWay Binding 
       return null;
    }
}

对我来说没问题!希望它能帮助你解决问题!

编辑:
与其他答案几乎相同,这里的区别在于您只需要在模板中使用1个TextBlock,转换器会决定是否有逗号。但基本上是相同的原则。MultiBinding非常棒! :-)


我接受了这个解决方案,因为这是我决定使用的方案(我更喜欢只有一个文本框),但两个解决方案都可以很好地工作,并且几乎相同。非常感谢 :) - Carl

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