How to use ItemsControl with WrapPanel to list items separated by a comma?
I have an ItemsControl which lists items by separating them with a comma. The code is the following:
<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="co开发者_JAVA百科mmaTextBlock"
Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The result is something like this: Item1, Item2, Item3
Now, I'd like to do the same using a WrapPanel instead of a StackPanel as the ItemsPanelTemplate. I tested it and it works fine, except for a small detail, it does something like this:
Item1, Item2
, Item3
Of course this is because the comma is before each element and I hide the first one. I would like to put the comma after each element and hide the last one, so the result would be this:
Item1, Item2,
Item3
It would be really simple if there existed something like NextData (so I would bind to this instead of to PreviousData), but unfortunately no such thing exists (or I haven't found one). Does anyone have an idea how to solve this problem?
Thanks
I have done a visibility converter for a similar problem:
<TextBlock Text=", " Name="commaTextBlock">
<TextBlock.Visibility>
<MultiBinding Converter="{StaticResource commaVisibilityConverter}">
<Binding ElementName="myItemsControl" Path="ItemsSource"/>
<Binding/>
</MultiBinding>
</TextBlock.Visibility>
</TextBlock>
And the converter logic:
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;
}
Maybe you can try to use multibinding and a converter. Something like this:
<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>
Where root
is the name of your ItemsControl.
And write a converter which checks for position:
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;
}
}
Works for me! Hope it helps you with your problem!
EDIT:
Almost the same as the other answer, the difference here is that you only need 1 TextBlock
in your template, and the converter decides if there is a comma or not. But basically the same principle. MultiBinding
rocks! :-)
精彩评论