1

I have DataGrid with DataGridComboBoxColumn column which can be edited. When I edit it exception is thrown. After debugging I found out that Converter receives DependencyProperty.UnsetValue and therefore I had to filter it out by returning "" and this cause to show empty cell.

I tried to google and understand why I get DependencyProperty.UnsetValue but without luck.

Any ideas?

public UserControl()
{
    InitializeComponent();

    DataContext = new MyViewModel();
}

public class MyViewModel : ViewModelBase
{
    public ObservableCollection<MyItem> MyItems { get; private set; }

    public MyViewModel()
    {           
        LoadStates();
    }

    public void LoadStates()
    {
        MyItems = new ObservableCollection<MyItem>(DataProvider.GetList());

        //...
    }
}

public class MyItem
{
    public bool Start {get; set;}
}


public class BoolToStatusConverter : IMultiValueConverter
{
    private MyViewModel _model;            

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        _model = values[0] as MyViewModel ;
        //TODO: Why I get UnsetValue??
        if (values[1] == DependencyProperty.UnsetValue) return "";

        //Using _model
        //...

        return (bool)values[1] ? Statuses.Start : Statuses.Stop;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        return new object[] { _model, (Statuses)value == Statuses.Start };
    }
}

<UserControl.Resources>
    <local:BoolToStatusConverter x:Key="BoolToStatusConverter" />

    <ObjectDataProvider x:Key="myEnum" MethodName="GetValues" ObjectType="{x:Type core:Enum}">
        <ObjectDataProvider.MethodParameters>
            <x:Type Type="local:Statuses"/>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>

</UserControl.Resources>

<Grid>
    <DataGrid ItemsSource="{Binding MyItems}" AutoGenerateColumns="False" >
        <DataGrid.Columns>              
            <DataGridComboBoxColumn Header="Status" ItemsSource="{Binding Source={StaticResource myEnum}}">                    
                <DataGridComboBoxColumn.SelectedItemBinding>
                    <MultiBinding Converter="{StaticResource BoolToStatusConverter}">
                        <Binding Path="DataContext"
                                RelativeSource="{RelativeSource AncestorType={x:Type UserControl}}" />
                        <Binding Path="Start" />
                    </MultiBinding>
                </DataGridComboBoxColumn.SelectedItemBinding>
            </DataGridComboBoxColumn>
        </DataGrid.Columns>
    </DataGrid>
</Grid>
theateist
  • 13,879
  • 17
  • 69
  • 109

1 Answers1

0

Why not just use a regular IValueConverter? I am not sure if you really need an IMultiValueConverter in this circumstance. This works for me:

public class BoolToStatusConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if ((bool) value)
        {
            return Statuses.Start;
        }

        return Statuses.Stop;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var theValue = (Statuses)value;

        if (theValue == Statuses.Start)
        {
            return true;
        }

        return false;
    }
}

And the XAML:

    <DataGrid ItemsSource="{Binding MyItems}" AutoGenerateColumns="False" >
        <DataGrid.Columns>
            <DataGridComboBoxColumn Header="Status" ItemsSource="{Binding Source={StaticResource myEnum}}">
                <DataGridComboBoxColumn.SelectedItemBinding>
                    <Binding Converter="{StaticResource BoolToStatusConverter}" Path="Start">
                    </Binding>
                </DataGridComboBoxColumn.SelectedItemBinding>
            </DataGridComboBoxColumn>
        </DataGrid.Columns>
    </DataGrid>

Also, to simplify it even further just make the field Start in MyItem a Statuses, and loose the converter altogether.

Eric Scherrer
  • 3,328
  • 1
  • 19
  • 34
  • I need to pass my ViewModel to the converter and based on its other properties to decide whether to convert it to `Start` or `Stop`. Though, while writing this I'm thinking that this should be responsibility of ViewModel itself, no? Anyway, if I do want to pass ViewModel to Converter, so how I do it? – theateist Apr 04 '14 at 20:23
  • It looks like you almost had it. I did see that UnsetValue behavior before starting to mess around but I am not sure why it was doing that. I agree though, the ViewModel or an object it is composed of should encapsulate the logic of what the value of Start is. – Eric Scherrer Apr 04 '14 at 20:31
  • FWIW - if you want to get at that view model with the multibinding the problem is you need to add FindAncestor to your RelativeSource like so: RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}} – Eric Scherrer Apr 04 '14 at 20:39
  • ...and looking at it further adding FindAncestor will make the UnsetValue issue go away for values[0], but your still going to have to deal with it for values[1]. – Eric Scherrer Apr 04 '14 at 20:43
  • actually, I didn't have problems with values[0], but only with values[1] – theateist Apr 04 '14 at 20:56
  • I think that has to do with the grid adding a new record, of whose value is indeed unset. – Eric Scherrer Apr 04 '14 at 20:59
  • though I probably will change field Start to Statuses, it's still very annoying to not understand why it happens. It could happen to me in future in some other place and if I don't understand it now I'll face it again in future – theateist Apr 04 '14 at 21:03