Search code examples
c#wpflistview

ListView Item - Generate column value based on criteria not part of Item's class


Suppose I've a simple model class:

public class Car
{
    public string Make { get; init; }
    public string Model { get; init; }
    public string Year { get; init; }
}

In my ViewModel, I've two lists:

public class ViewModel
{
    public ObservableCollection<Car> Cars { get; }
    public List<Car> CanBeSold { get; }

    public ViewModel()
    {
        Car car1 = new() { Make = "Toyota", Model = "Corolla", Year = "2020" };
        Car car2 = new() { Make = "Honda", Model = "Civic", Year = "2021" };
        Car car3 = new() { Make = "Mitsubishi", Model = "Lancer", Year = "2017" };

        Cars = new();
        CanBeSold = new();

        Cars.Add(car1);
        Cars.Add(car2);
        Cars.Add(car3);

        CanBeSold.Add(car2);
    }
}

In my view, I'm bidining a ListView to the Cars collection:

<ListView ItemsSource="{Binding Cars}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Make" DisplayMemberBinding="{Binding Path=Make}"/>
            <GridViewColumn Header="Model" DisplayMemberBinding="{Binding Path=Model}"/>
            <GridViewColumn Header="Year" DisplayMemberBinding="{Binding Path=Year}"/>
            <GridViewColumn Header="Can Be Sold"/>
        </GridView>
    </ListView.View>
</ListView>

How can I also show a Yes/No based on if the Car is in the list CanBeSold?

Screenshot

Thanks for any help.


Solution

  • You may use a MultiBinding that contains a Binding to the CanBeSold property of the parent view model and a Binding to the current Car element.

    <GridViewColumn Header="Can Be Sold">
        <GridViewColumn.DisplayMemberBinding>
            <MultiBinding>
                <MultiBinding.Converter>
                    <local:ListElementConverter/>
                </MultiBinding.Converter>
                <Binding Path="DataContext.CanBeSold"
                         RelativeSource="{RelativeSource AncestorType=ListView}"/>
                <Binding />
            </MultiBinding>
        </GridViewColumn.DisplayMemberBinding>
    </GridViewColumn>
    

    The Binding Converter checks if the element is contained in the list:

    public class ListElementConverter : IMultiValueConverter
    {
        public object Convert(
            object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return values.Length == 2 &&
                values[0] is IList list &&
                list.Contains(values[1])
                ? "Yes"
                : "No";
        }
    
        public object[] ConvertBack(
            object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }