wpfbindingdatagridicollectionview

WPF ICollectionView Two Way Binding?


I'm displaying some data in a DataGrid, binding to a CollectionView. Bindings work fine, but seem to be readonly: checking a checkbox in the grid does not update the model.

Reduction ad absurdum:

public class Item
{
    public bool IsChecked
    {
        get
        {
            return _isChecked;
        }
        set
        {
            // Not hit when clicking on checkbox
            _isChecked = value;
        }
    }
    private bool _isChecked;
}

Code behind, my collection view is a dependency property:

        private static DependencyProperty __CollectionViewProperty = DependencyProperty.Register
        (
           "_CollectionView",
           typeof( ICollectionView ),
           typeof( MyClass ),
           new PropertyMetadata( null )
        );

        private ICollectionView _CollectionView
        {
            get { return ( ICollectionView )GetValue( __CollectionViewProperty ); }
            set { SetValue( __CollectionViewProperty, value ); }
        }

And is instantiated like this:

List<Item> items = new List<Item>();
items.Add( new Item() );
this._CollectionView = CollectionViewSource.GetDefaultView( items );

In the xaml, I simply bind a DataGrid to _CollectionView, and the checkbox to IsChecked. No exceptions, and IsChecked's state is correctly reflected in the UI.

But clicking the checkbox only updates the UI, not the model. I'm new to WPF, coming from Cocoa, where the equivalent is NSArrayController and the binding would work two-way straight out of the box. What am I missing?

Edit: xml code:

<DataGrid Grid.Row="1" ItemsSource="{ Binding _CollectionView}" AutoGenerateColumns="False" GridLinesVisibility="None"
                  RowHeight="60" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollBarVisibility="Hidden"
                  CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserSortColumns="True" IsSynchronizedWithCurrentItem="True"
                  x:Name="_DataGrid">
            <DataGrid.Columns>
                <DataGridTemplateColumn Header="" Width="90" CanUserSort="False">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <CheckBox IsChecked="{Binding IsChecked}" Click="CheckBox_Click" HorizontalAlignment="Left"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            <DataGrid.Columns>
</DataGrid>

Solution

  • OK, got it.

    For some idiotic reason, the default Binding Mode is OneWay when binding to a collection, and UpdateSourceTrigger is NOT PropertyChanged. The docs mention the binding being one way, but not the update trigger being different too...

    So, xaml binding becomes:

    <CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    

    To also have changes to the source correctly propagating to the target UI components, the easiest way is to inherit from DependencyObject, and to declare the relevant properties as dependency properties:

    public class Item : DependencyObject
    {
            private static DependencyProperty __IsCheckedProperty = DependencyProperty.Register
            (
               "IsChecked",
               typeof( bool ),
               typeof( iOSAppItem ),
               new PropertyMetadata( true )
            );
    
            public bool IsChecked
            {
                get { return ( bool )GetValue( __IsCheckedProperty ); }
                set { SetValue( __IsCheckedProperty, value ); }
            }
    }
    

    According to the docs, using DependencyProperty is also the most efficient.

    Finally, choosing ObservableCollection over List is a good idea: insertion/deletion of items will be synchronised for free.