Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to convert the members of a collection used as an ItemsSource?

Tags:

wpf

In WPF you can use an IValueConverter or IMultiValueConverter to convert a data-bound value from say an int to a Color.

I have a collection of Model objects which I would like to convert to their ViewModel representations but in this scenario,

<ListBox ItemsSource="{Binding ModelItems, 
     Converter={StaticResource ModelToViewModelConverter}" />

the converter would be written to convert the whole collection ModelItems at once.

I wish to convert the items of the collection individually, is there a way to do that? I might want to use a CollectionViewSource and have some filtering logic so I don't want to have to iterate over the whole collection every time something changes.

like image 805
Grokodile Avatar asked Sep 21 '11 19:09

Grokodile


2 Answers

You cannot set the converter on the collection itself, because it would get the collection as input. You have two choices:

  1. Make sure your converter can also deal with collections (IEnumerable).
  2. Use the converter within the item template.

If you want to use the second approach, then use something like this:

<ListBox ItemsSource="{Binding ModelItems}">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <ContentPresenter Content="{Binding Converter={StaticResource ModelToViewModelConverter}}" 
                        ContentTemplate="{StaticResource MyOptionalDataTemplate}"/>
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

If you don't need a custom datatemplate, then you can skip the ContentTemplate attribute.

like image 111
Ramon de Klein Avatar answered Sep 19 '22 10:09

Ramon de Klein


Yes you can. It is acting the same as with the IValueConverter. You simply treat the value parameter for the Convert method as a collection.

Here is an example of Converter for a collection:

public class DataConvert : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        ObservableCollection<int> convertible = null;
        var result = value as ObservableCollection<string>;

        if (result != null)
        {
            convertible = new ObservableCollection<int>();
            foreach (var item in result)
            {
                if (item == "first")
                {
                    convertible.Add(1);
                }
                else if (item == "second")
                {
                    convertible.Add(2);
                }
                else if (item == "third")
                {
                    convertible.Add(3);
                }
            }
        }

        return convertible;
    }

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

In this case is just a proof of concept, but I think it should show the idea very well. The Converter converts from a simple collection of strings like this:

ModelItems = new ObservableCollection<string>();
        ModelItems.Add("third");
        ModelItems.Add("first");
        ModelItems.Add("second");

into a collection of integers corresponding to the string meaning.

And here is the corresponding XAML (loc is the reference of the current assembly where is the converter):

<Window.Resources>
    <loc:DataConvert x:Key="DataConverter"/>
</Window.Resources>
<Grid x:Name="MainGrid">
    <ListBox ItemsSource="{Binding ModelItems, Converter={StaticResource DataConverter}}"/>
</Grid>

If you want to make a two way binding, you have to implement also the convert back. From the experience of working with MVVM, i suggest to use something like the Factory Pattern to transform from Model in ViewModel and backwards.

like image 31
louie Avatar answered Sep 20 '22 10:09

louie