in Silverlight DataGrid, CheckBox's DataContex

2019-09-20 04:00发布

问题:

I have a DataGrid. One of the columns is a template with a CheckBox in it. When the Checked or Unchecked events trigger, (it happens with both) the CheckBox's DataContext is sometimes null, which causes my code to error. It seems to be null most often if the mouse is moving while you press and release the button quickly (it's intermittent).

I listened for changes to the DataContext of the CheckBox by making views:ListenCheckBox (extends CheckBox) and attaching a binding, and it's never set to null, but it is set from null to a Task at times I wouldn't expect, i.e. after the DataGrid has been totally generated and you're checking/unchecking boxes. Immediately after the [un]checked event runs with a null DataContext, I get the notification that shows the DataContext changed from null to a Task, so it appears that when I get a null DataContext, it's because it hadn't actually set the DataContext by the time it ran the Checked/Unchecked event.

Also, I added Tag="{Binding}" to the CheckBox for debugging. The Tag is not null (i.e. it has the proper object) more often than the DataContext, but still not all the time.

Here are the relevant bits of the XAML code:

<navigation:Page.Resources>
    <sdk:DataGridTemplateColumn x:Key="DeleteOrPrintSelect" Header="Delete Or Print Notes Selection">
        <sdk:DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <views:ListenCheckBox IsChecked="{Binding DeleteOrPrintNotesSelection, Mode=TwoWay}" Checked="DeletePrintNotesCheckBox_Changed" Unchecked="DeletePrintNotesCheckBox_Changed" HorizontalAlignment="Center" VerticalAlignment="Center" Tag="{Binding}" />
            </DataTemplate>
        </sdk:DataGridTemplateColumn.CellTemplate>
    </sdk:DataGridTemplateColumn>
</navigation:Page.Resources>


    <sdk:DataGrid x:Name="dataGrid1" Grid.Column="1" Grid.Row="2" AutoGeneratingColumn="dataGrid1_AutoGeneratingColumn">
        <sdk:DataGrid.RowGroupHeaderStyles>
            [removed]
        </sdk:DataGrid.RowGroupHeaderStyles>
    </sdk:DataGrid>

And the relevant code behind:

        // Create a collection to store task data.
        ObservableCollection<Task> taskList = new ObservableCollection<Task>();
        [code adding Tasks to taskList removed]

        PagedCollectionView panelListView = new PagedCollectionView(taskList);
        this.dataGrid1.ItemsSource = panelListView;
    }

    private void dataGrid1_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
    {
        if (e.PropertyName == "DeleteOrPrintNotesSelection")
        {
            e.Column = Resources["DeleteOrPrintSelect"] as DataGridTemplateColumn;
        }
        else
        {
            e.Column.IsReadOnly = true;
        }
    }

    private void DeletePrintNotesCheckBox_Changed(object sender, RoutedEventArgs e)
    {
        try
        {
            var cb = sender as CheckBox;
            var t = cb.DataContext as Task;
            t.DeleteOrPrintNotesSelection = cb.IsChecked == true;
            PagedCollectionView pcv = this.dataGrid1.ItemsSource as PagedCollectionView;
            ObservableCollection<Task> taskList = pcv.SourceCollection as ObservableCollection<Task>;
            bool anySelected = taskList.Any(x => x.DeleteOrPrintNotesSelection);
            this.btnPrint.IsEnabled = anySelected;
            this.btnDelete.IsEnabled = anySelected;
        }
        catch (Exception ex)
        {
            ErrorMessageBox.Show("recheck", ex, this);
        }
    }

Any ideas? Thanks in advance.

回答1:

I found that the problem happened when you double click on the cell and it moved it to the cell editing template. In my case, I didn't have a cell editing template defined, so it used the same cell template, but instead of not changing anything, it apparently decided to make a new check box. I set the column's IsReadOnly property to true, and it fixed it. An alternate solution:

DataContext="{Binding}" (in XAML, or the code equivalent:)
cb.SetBinding(FrameworkElement.DataContextProperty, new Binding());

I'm not sure why this one fixes it, since I thought the default DataContext is {Binding}. Perhaps it's a Silverlight bug, and it gets set in a different order if you define it explicitly instead of leaving it the default.