Search code examples
c#wpfdatagridmultibindingimultivalueconverter

Cannot bind DataGrid ItemsSource with multibinding - simple binding works fine


I'm facing a weird issue. I have a DataGrid that I want to bind to ItemsSource with MultiBinding for a reason. While using simple binding to a DataTable works well, I cannot get this to work with multibinding.

Simply put: the markup below works and renders the data table

<DataGrid Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" AutoGenerateColumns="True" IsReadOnly="True">
    <DataGrid.ItemsSource>
        <Binding Path="Mock.Value" Converter="{StaticResource CollectionToDataTableConverter}"></Binding>
    </DataGrid.ItemsSource>
</DataGrid>

... while this does not really work - renders nothing

<DataGrid Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" AutoGenerateColumns="True" IsReadOnly="True">
    <DataGrid.ItemsSource>
        <MultiBinding Converter="{StaticResource CollectionToDataTableConverter}">
            <Binding Path="Mock.Value" />
        </MultiBinding>
    </DataGrid.ItemsSource>
</DataGrid>

Notice that CollectionToDataTableConverter implements both IValueConverter and IMultiValueConverter and simply passes the value

public class CollectionToDataTableConverter : IMultiValueConverter, IValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        return values?.FirstOrDefault();
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value;
    }
    // ...
}

Certainly the Mock.Value property exists on the view model and is a simple DataTable. Also, debugging proves that in both cases the converter returns a proper value.

Do you have any idea what is going on with this?

Thanks!


Solution

  • First of all, DataTable does not implement IEnumerable, so it's not a valid value for DataGrid.ItemsSource property. You can observe a System.Windows.Data Error: 5 in the output window saying just that.

    Second, you can easily check that the final value of DataGrid.ItemsSource when binding to a DataTable is actually a DataView object (precisely it's the value of DataTable.DefaultView property).

    Now there are several ways to make a MultiBinding work - here's a couple:

    • Return the DataTable.DefaultView rather than the DataTable itself in your converter
    • Bind directly to the DataTable.DefaultView (use Mock.Value.DefaultView as binding path)
    • Create a class deriving from DataTable and implementing IEnumerable (return DefaultView.GetEnumerator() in the GetEnumerator implementation), and use that class instead of DataTable

    As for an explanation for how does it automagically work when not using MultiBinding or why it's not the case when using one - I don't know. I thought it was WPF's type conversion mechanism, but default converters neither for DataTable nor for IEnumerable are capable of performing the conversion, so I guess that's not it. Possibly the Binding class has some special capabilities regarding that matter that MultiBinding does not.