vwegert
vwegert

Reputation: 18483

How to apply a Converter to each item of an ObservableCollection?

I am using C# and .NET 4.5 to create a MVVM desktop application. I have a set of view model entries that are already contained in an ObservableCollection<MyEntryClass>.

I need to use a third-party control to display the data. This control requires that the entries be converted to their own entry classes. They provide an example on how to do that, using XAML and an IValueConverter. It boils down to ...Items="{Binding Path=The.Source, Converter={StaticResource CustomDataConverter}}... and a Converter that is implemented roughly like this:

public class CustomDataConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        IEnumerable<CustomItem> dataContext = value as IEnumerable<CustomItem>;
        ObservableCollection<OutputItem> items = new ObservableCollection<OutputItem>();
        foreach (CustomItem customItem in dataContext)
        {
            OutputItemitem =
                new OutputItem
                {
                    // ... some value transfers ...
                };
            items.Add(item);
        }
        return items;
    }

While this works for the initialization of the control, it breaks the "observation chain" between the control and the ObservableCollection specified in the binding -- simply because the custom converter creates its own list of items. Is there a way to change the binding instructions so that the converter is not called once for the entire source collection, but once for every single collection element? If not, what alternative strategies exist for dealing with this kind of situation?

(In the example above I have left out some code that registers an event handler to the source object's PropertyChanged event and updates the target object. Updates of individual item properties are working correctly, it's just the updates of the list that are not handled.)


EDIT: The third party control in question is the GanttChartDataGrid from the Gantt Chart Light Library.

Upvotes: 1

Views: 1396

Answers (2)

trix
trix

Reputation: 898

Another approach could be to forget to use converters in XAML code and do your conversions in the ViewModel.

The converter you posted havent a ConvertBack method so I'm guessing the list of ObservableCollection<OutputItem> needed by your thirdy parts control is readonly, meaning no modifications will occur from XAML to C# - as everybody supposes using a Grafic Chart library.

So you can have 2 properties, one private containing your custom object you will update in the ViewModel basing upon your business logic, then one public property binded to your thirdy parts UserControl:

private ObservableCollection<OutputItem> myPrivateData 
{ 
   get; 
   set; 
}

private ObservableCollection<CustomItem> DataToPlotOnGant 
{ 
   get; 
   set; 
}

Then you can do your conversion stuff inside CollectionChanged event of your private collection:

this.myPrivateData.CollectionChanged += (s, e) =>
            {
                // do convertion stuff here
            };

and choose to update DataToPlotOnGant list by modifying just the item changed in the private collection - more efficiently - or to create every time a new collection like the converter did.

Upvotes: 1

qqbenq
qqbenq

Reputation: 10460

You could try to use the converter in the ItemTemplate, if this third party control offers such possibility. With standard WPF controls it should go something like this:

<ListBox ItemsSource="{Binding Path=The.Source}">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <ContentPresenter Content="{Binding Converter={StaticResource CustomDataConverter}}" 
                    ContentTemplate="{StaticResource MyOptionalDataTemplate}"/>
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

But maybe the easiest way to achieve this would be to bind a collection that contains OutputItems...

Upvotes: 3

Related Questions