1

I'm running onto a problem that I'm having some trouble diagnosing.

I have created a custom DataGrid object (as detailed in Sandesh's answer here) in order to be able to bind to the various selected items of the data grid. However, even though it looks like the DependecyProperty itself is getting the right value when the selection changes, the view model never seems to receive the updated value (it gets null instead).

Custom DataGrid class:

class CustomDataGrid : DataGrid
{
    public CustomDataGrid()
    {
        this.SelectionChanged += CustomDataGrid_SelectionChanged;
    }

    void CustomDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        this.SelectedItemsList = this.SelectedItems;
    }
    #region SelectedItemsList

    // In the setter, the value is a valid List with the correct elements.
    // For some reason it doesn't seem to be passed on to the view model. 
    public IList SelectedItemsList
    {
        get { return (IList)GetValue(SelectedItemsListProperty); }
        set { SetValue(SelectedItemsListProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemsListProperty =
            DependencyProperty.Register("SelectedItemsList", typeof(IList), typeof(CustomDataGrid), new PropertyMetadata(OnSelectedItemsListChanged));

    private static void OnSelectedItemsListChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("Test");
    }

    #endregion
}

XAML file:

<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Test"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:TestViewModel />
    </Window.DataContext>
    <Grid>
        <local:CustomDataGrid AutoGenerateColumns="True"
                              SelectionMode="Extended"
                              SelectionUnit="FullRow"
                              CanUserAddRows="False"
                              CanUserDeleteRows="False"
                              CanUserResizeRows="False"
                              ItemsSource="{Binding TestModels}"
                              SelectedItemsList="{Binding Path=SelectedTestModels, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />    
    </Grid>
</Window>

View Model:

class TestViewModel : INotifyPropertyChanged
{
    public TestViewModel()
    {
        TestModels = new List<TestModel>
        {
            new TestModel { Name = "Test 1", ID = 1 },
            new TestModel { Name = "Test 2", ID = 2 },
            new TestModel { Name = "Test 3", ID = 3 },
        };
    }

    private IEnumerable<TestModel> _testModels;

    public IEnumerable<TestModel> TestModels
    {
        get { return _testModels; }
        set
        {
            _testModels = value;
            OnPropertyChanged();
        }
    }

    private IEnumerable<TestModel> _selectedTestModels;

    public IEnumerable<TestModel> SelectedTestModels
    {
        get { return _selectedTestModels; }
        set
        {
            // Right here I would expect value to have a non-null value... but it's always null
            _selectedTestModels = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var propChanged = PropertyChanged;
        if (propChanged != null)
            propChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

The TestModel class (very simple POCO):

class TestModel
{
    public string Name { get; set; }
    public int ID { get; set; }
}
Community
  • 1
  • 1
Mage Xy
  • 1,803
  • 30
  • 36
  • Are there any binding error messages in the Output Window in Visual Studio? `IList` might not be assignable from/to `IEnumerable`. – Clemens Aug 15 '16 at 15:38
  • There are no error messages that appear in the output window, binding or otherwise. Additionally, if I change the type of `TestModels` and `SelectedTestModels` in the view model to `IList`, I still get a null value sent. – Mage Xy Aug 15 '16 at 15:39

1 Answers1

2

You're using the non-generic IList and IList interchangeably. You have a couple of options.

In your viewmodel you can change your property and field to IList.

private IList _selectedTestModels;

public IList SelectedTestModels
{
    get { return _selectedTestModels; }
    set
    {
        _selectedTestModels = value;
        OnPropertyChanged();
    }
}

Alternatively in your CustomControl.

public static readonly DependencyProperty SelectedItemsListProperty = DependencyProperty.Register("SelectedItemsList", typeof(IList<TestModel>), typeof(CustomDataGrid));

public IList<TestModel> SelectedItemsList
{
    get { return (IList<TestModel>)GetValue(SelectedItemsListProperty); }
    set { SetValue(SelectedItemsListProperty, value); }
}

void CustomDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    SetCurrentValue(SelectedItemsListProperty, SelectedItems.OfType<TestModel>().ToList());
}

As a side note, I also believe this bit of code may break your binding.

void CustomDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    this.SelectedItemsList = this.SelectedItems;
}

Try using SetCurrentValue to maintain your binding.

void CustomDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    SetCurrentValue(SelectedItemsListProperty, SelectedItems);
}
Derrick Moeller
  • 4,808
  • 2
  • 22
  • 48
  • Sorry, it doesn't look like that did anything. The view model is still receiving null. – Mage Xy Aug 15 '16 at 17:05
  • I don't think it's your binding then, I think your IList is null in the CustomControl. Try putting a breakpoint and the following code at the end of your SelectionChanged event, I bet SelectedItemsList is null also. var test = SelectedItemsList; – Derrick Moeller Aug 15 '16 at 17:26
  • You are correct. If I add that line after I do the `this.SelectedItemsList = this.SelectedItems` call, `SelectedItemsList` is still null, even though `SelectedItems` is valid and has data in it. How would I go about solving that? – Mage Xy Aug 15 '16 at 17:34
  • @MageXy, I had to make a new project to see it but I believe it's working now. See the edit. – Derrick Moeller Aug 15 '16 at 18:38
  • THANK YOU! I can't believe the fix was so simple. I wonder why I wasn't getting any errors... surely a type conversion error should have been thrown? Well, it works now, in any case. Thanks again! – Mage Xy Aug 15 '16 at 18:45