Как проверить значение ячейки данных на основе предыдущего значения

Я создал сетку данных из нескольких строк и столбцов. Один из столбцов представляет собой список размеров полей, которые пользователь может изменить.

Я проверяю новое значение по сравнению со старым значением, и если новое значение меньше, чем старое значение, я говорю пользователю, что оно недопустимо, и затем я хочу вернуть старое значение обратно и вернуть фокус в эту ячейку.

У меня есть эта строка в моем LostFocus событие:

System.Windows.Controls.TextBox tbNewSize = 
    (System.Windows.Controls.TextBox)dtgCell.Content;

Когда я нажимаю на ячейку, LostFocus событие называется и работает нормально. Однако, когда я пытаюсь перефокусироваться на ячейку, я получаю сообщение об ошибке

"Невозможно привести объект типа 'System.Windows.Controls.TextBlock' к типу 'System.Windows.Controls.TextBox'."

Как я могу исправить эту проблему?

Вот мой код XAML:

<DataGrid HeadersVisibility="Column" Name="dtGrid" Loaded="GridLoaded" AutoGenerateColumns="False" IsReadOnly="False" VirtualizingPanel.IsVirtualizing="False" Height="365" Width="530" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="54,74,0,0" BorderThickness="1,1,0,1" BorderBrush="Black">
<DataGrid.Columns>
    <DataGridTextColumn Header="Field" Binding="{Binding Field, Mode=TwoWay}" Width="209" IsReadOnly="True" />
    <DataGridTextColumn Header="Size" Binding="{Binding Size, Mode=TwoWay}" Width="89"/>
    <DataGridCheckBoxColumn Header="Right Justify" Binding="{Binding RightJustify, Mode=TwoWay}" Width="55" />
    <DataGridCheckBoxColumn Header="Left Justify" Binding="{Binding LeftJustify, Mode=TwoWay}"  Width="55"  />
    <DataGridCheckBoxColumn Header="Left Zero Fill" Binding="{Binding LeftZeroFill, Mode=TwoWay}" Width="55" />
    <DataGridCheckBoxColumn Header="Right Zero Fill" Binding="{Binding RightZeroFill, Mode=TwoWay}" Width="65" />
</DataGrid.Columns>
<DataGrid.ColumnHeaderStyle>
    <Style TargetType="DataGridColumnHeader">
        <Setter Property="ContentTemplate">
            <Setter.Value>
                <DataTemplate>
                    <TextBlock TextWrapping="Wrap" Text="{Binding}"></TextBlock>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Resources>
    <Style TargetType="{x:Type DataGridCell}">
        <Style.Triggers>
            <Trigger Property="DataGridCell.IsSelected" Value="True">
                <Setter Property="Background" Value="#FF9DF3D6" />
                <Setter Property="Foreground" Value="#000000" />
            </Trigger>
        </Style.Triggers>
        <EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown" />
        <EventSetter Event="LostFocus" Handler="DataGridCell_OnCellLostFocus" />
    </Style>
</DataGrid.Resources>

Вот мой код C#:

private void DataGridCell_OnCellLostFocus(object sender, RoutedEventArgs e)
{
    System.Windows.Controls.DataGridCell dtgCell = (System.Windows.Controls.DataGridCell)sender;

    if (dtgCell.Column.Header.ToString() == "Size")
    {
        System.Windows.Controls.TextBox tbNewSize = (System.Windows.Controls.TextBox)dtgCell.Content;
        Int32 intNewSize = Convert.ToInt32(tbNewSize.Text);
        Int32 intCurrSize = Convert.ToInt32(strFieldInfoOrig[dtGrid.Items.IndexOf(dtGrid.CurrentItem), 1]);

        if (intNewSize < intCurrSize)
        {
            string strMsg;

            strMsg = "New size, " + intNewSize.ToString() + " is smaller then the original size, " + intCurrSize.ToString();
            strMsg += Environment.NewLine;
            strMsg += "Due to potential data loss, this is not allowed.";
            System.Windows.MessageBox.Show(strMsg);
            //dtgCell.Content = intCurrSize.ToString();
            dtgCell.Focus();
        }
    }
}

3 ответа

Вы можете обработать событие CellEditEnding.

<DataGrid AutoGenerateColumns="False"
          CellEditEnding="DataGrid_CellEditEnding"
          ...
          >
    <DataGrid.Columns>
        <DataGridTextColumn Header="Size" Binding="{Binding Size, Mode=TwoWay}" .../>

        ...

    </DataGrid.Columns>

    ...

</DataGrid>

Код позади

private void DataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e) 
{
    DataGrid datagrid = sender as DataGrid;
    if (e.EditAction == DataGridEditAction.Commit)
    {
        if (e.Column is DataGridBoundColumn)
        {
            DataGridBoundColumn column = (DataGridBoundColumn)e.Column;
            if (column.Header.ToString() == "Size")
            {
                string oldValue = e.Row.DataContext.GetType().GetProperty("Size")
                                   .GetValue(e.Row.DataContext).ToString();
                TextBox element = e.EditingElement as TextBox;
                string newValue = element.Text;
                int oldSize = int.Parse(oldValue);
                int newSize = int.Parse(newValue);
                if (newSize < oldSize)
                {
                    string strMsg = "New size, " + newValue + ", is smaller then the original size, "
                                  + oldValue + ".\nDue to potential data loss, this is not allowed.";
                    MessageBox.Show(strMsg);
                    element.Text = oldValue;
                    e.Cancel = true;
                }
            }
        }
    }
}

настройка e.Cancel = true держит ячейку в режиме редактирования.

Вы пытаетесь разыграть TextBlock к TextBox и это, очевидно, не сработает. Но если вы просто пытаетесь всегда бросать TextBlock как это:

System.Windows.Controls.TextBlock tbNewSize = (System.Windows.Controls.TextBlock)dtgCell.Content;

... это тоже не сработает. Это потому что Content клетки могут быть TextBox или TextBlock в зависимости от того, находится ли ячейка в данный момент в режиме редактирования или нет.

То, что вы могли бы, это использовать as оператор, чтобы попытаться привести к TextBox и если произойдет сбой, то разыграйте Content собственность на TextBlock:

private void DataGridCell_OnCellLostFocus(object sender, RoutedEventArgs e)
{
    System.Windows.Controls.DataGridCell dtgCell = (System.Windows.Controls.DataGridCell)sender;
    if (dtgCell.Column.Header.ToString() == "Size")
    {
        string text = null;
        System.Windows.Controls.TextBox tbNewSize = dtgCell.Content as System.Windows.Controls.TextBox;
        if (tbNewSize != null)
        {
            text = tbNewSize.Text;
        }
        else
        {
            System.Windows.Controls.TextBlock tb = dtgCell.Content as System.Windows.Controls.TextBlock;
            if (tb != null)
                text = tb.Text;
        }
        Int32 intNewSize = Convert.ToInt32(text);
        Int32 intCurrSize = Convert.ToInt32(strFieldInfoOrig[dtGrid.Items.IndexOf(dtGrid.CurrentItem), 1]);

        if (intNewSize < intCurrSize)
        {
            string strMsg;

            strMsg = "New size, " + intNewSize.ToString() + " is smaller then the original size, " + intCurrSize.ToString();
            strMsg += Environment.NewLine;
            strMsg += "Due to potential data loss, this is not allowed.";
            System.Windows.MessageBox.Show(strMsg);
            //dtgCell.Content = intCurrSize.ToString();
            dtgCell.Focus();
        }
    }
}

Это происходит потому, что DataGridTextColumn показывает TextBlock в обычном режиме и TextBox во время редактирования. Итак, когда эта клетка теряет фокус, DataGridTextColumn возвращается в нормальном режиме, поэтому его содержимое будет TextBlock и не TextBox и, следовательно, это показывает исключение.

Итак, попробуйте бросить в TextBlock и не TextBox,

Другие вопросы по тегам