WPF Toolkit DataGrid Checkbox Issues

2020-03-27 09:33发布

问题:

I'm really hoping someone can help me out here. I have a DataGrid in my program that has a checkbox column. The ItemsSource for the DataGrid is a DataSet loaded programmatically. When I select a couple of items in the DataGrid and then scroll it, I get some very odd behavior. For example, when I check two of the CheckBoxes, it tells me that I have "2 selected", but then if I scroll up or down in the DataGrid, the number changes. If I scroll back to the initial position it goes back to the "2 selected". As odd as it sounds, it seems like it's calling the Checked/Unchecked events when I scroll the box...very odd...

I have the following defined at the top of my code:

private DataSet MyDataSet;
int selected_count = 0;

Then I have the following code in my method to load the information into the DataSet:

MyDataSet = new DataSet();
DataTable tempDataTable = new DataTable();
MyDataSet.Tables.Add(tempDataTable);

DataColumn tempCol = new DataColumn("Checked", typeof(bool));
tempDataTable.Columns.Add(tempCol);

for (int i = 0; i < 50; i++)
{
    DataRow tempRow = tempDataTable.NewRow();
    tempDataTable.Rows.Add(tempRow);
    tempRow["Checked"] = false;
}

MyList.ItemsSource = MyDataSet.Tables[0].DefaultView;

I have the IsChecked property binded to the DataColumn named "Checked" using the following XAML:

<dtgrd:DataGrid x:Name="MyList" AutoGenerateColumns="False" CanUserAddRows="False" CanUserResizeRows="False" HeadersVisibility="Column" SelectionUnit="FullRow" HorizontalGridLinesBrush="#FF688CAF" VerticalGridLinesBrush="#FF688CAF">
    <dtgrd:DataGrid.Columns>
        <dtgrd:DataGridTemplateColumn x:Name="CheckCol" CanUserSort="True" CanUserResize="False">
            <dtgrd:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <CheckBox Name="MyCheckBox" IsChecked="{Binding Checked}" HorizontalAlignment="Center" VerticalAlignment="Center" Checked="MyCheckBox_Checked" Unchecked="MyCheckBox_Unchecked" />
                </DataTemplate>
            </dtgrd:DataGridTemplateColumn.CellTemplate>
        </dtgrd:DataGridTemplateColumn>
    </dtgrd:DataGrid.Columns>
</dtgrd:DataGrid>

Then, I have the following events that are called by checking/unchecking one of the checkboxes:

private void MyCheckBox_Checked(object sender, RoutedEventArgs e)
{
    selected_count++;
    TxtSelectedCount.Text = "" + selected_count + " selected";
}

private void MyCheckBox_Unchecked(object sender, RoutedEventArgs e)
{
    selected_count--;
    TxtSelectedCount.Text = "" + selected_count + " selected";
}

I have also tried other things, but get different bugs. For example, I removed the Binding from the XAML code and tried to set it programmatically using the following Checked/Uncheck events:

private void MyCheckBox_Checked(object sender, RoutedEventArgs e)
{
    DataRow tempRow = MyDataSet.Tables[0].Rows[MyList.Items.IndexOf(MyList.SelectedItems[0])];
    tempRow["Checked"] = true;

    selected_count++;
    TxtSelectedCount.Text = "" + selected_count + " selected";
}

private void MyCheckBox_Unchecked(object sender, RoutedEventArgs e)
{
    DataRow tempRow = MyDataSet.Tables[0].Rows[MyList.Items.IndexOf(MyList.SelectedItems[0])];
    tempRow["Checked"] = false;

    selected_count--;
    TxtSelectedCount.Text = "" + selected_count + " selected";
}

When I use that code, the number of checked items stays the same, but the actual checks move around to different items while I'm scrolling.

I honestly have no clue what's going on, but it's very frustrating! Any help would be GREATLY appreciated!

回答1:

You're running into item container recycling. See http://blogs.msdn.com/b/vinsibal/archive/2008/05/14/recycling-that-item-container.aspx. WPF is re-using the row objects as you scroll, and you're seeing the Checked and Unchecked events fire as it binds to a different row.

If you want to stick with your current solution, you can just disable item container recycling by adding VirtualizingStackPanel.VirtualizationMode="Standard" to your dtgrd:DataGrid element. You could also disable virtualization entirely by adding VirtualizingStackPanel.IsVirtualizing="False".

A better design might be to get that data from your underlying data model rather than relying on the UI events. Try handling the DataTable.ColumnChanged event on the DataTable.