Wobbles
Wobbles

Reputation: 3135

Trying to create an IsFirstItem and IsLastItem IValueConverter for object collection bindings

The end goal to be able to set a specific ItemContainerStyle on the first and last element in my list box;

The converters thus far are:

public class IsFirstItemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool result = false;
        result = ((IList<object>)parameter).First() == value;

        return result;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
public class IsLastItemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool result = false;
        result = ((IList<object>)parameter).Last() == value;

        return result;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

And implementation:

<DataTrigger Value="True" Binding="{Binding Converter={StaticResource IsFirstItemConverter},ConverterParameter=Items,  ElementName=SubItems}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="First"/>
                    <ContentPresenter/>
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</DataTrigger>

And the error is:

InvalidCastException: Unable to cast object of type 'System.String' to type 'System.Collections.Generic.IList`1[System.Object]'.

Im sure I screwed up in multiple spots, just not experienced enough with XAML and bindings to narrow down where.

Upvotes: 1

Views: 196

Answers (1)

Wobbles
Wobbles

Reputation: 3135

The following adapted from the questions comments work perfectly:

public class IsLastItemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        DependencyObject item = (DependencyObject)value;
        ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(item);

        return ic.ItemContainerGenerator.IndexFromContainer(item) == ic.Items.Count - 1;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

public class IsFirstItemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        DependencyObject item = (DependencyObject)value;
        ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(item);

        return ic.ItemContainerGenerator.IndexFromContainer(item) == 0;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

public class IsOnlyItemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        DependencyObject item = (DependencyObject)value;
        ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(item);

        return (ic.ItemContainerGenerator.IndexFromContainer(item) == 0 && ic.ItemContainerGenerator.Items.Count == 1);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Implementation:

<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource IsLastItemConverter}}" Value="True">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="└"/>
                    <ContentPresenter/>
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</DataTrigger>

The magic seems to be that ItemsControlFromItemContainer makes it so I do not have to pass both the item and its collection instance to the converter but just the item and the converter can infer the parent collection.

Upvotes: 1

Related Questions