WPF, create Custom DataGridTextColumn to Prevent unwanted Character

1.4k views Asked by At

i newbie to WPF, i want to prevent user to input character, eg. character "-", so i created custom DataGridTextColumn with following code :

public class DataGridNumericColumn : DataGridTextColumn
{
    protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
    {
        var textBox = (TextBox) editingElement;
        textBox.PreviewTextInput += OnPreviewTextInput;
        return base.PrepareCellForEdit(editingElement, editingEventArgs);
    }


    private void OnPreviewTextInput(object sender, TextCompositionEventArgs e)
    {
        var textBox = (TextBox)sender;
        if (e.Text == "-")
            return;
        if (!this.IsNumeric(e.Text))
            e.Handled = true;
    }
}

and XAML :

<ZF:ZFDataGrid
        Grid.Row="4" Grid.Column="0" 
        HorizontalAlignment="Stretch" VerticalAlignment="Top"
        HorizontalContentAlignment="Stretch"
        VerticalContentAlignment="Stretch"
        CanUserAddRows="True"
        CanUserDeleteRows="False"
        CanUserResizeRows="False"
        CanUserReorderColumns="False"
        CanUserSortColumns="False"
        IsSynchronizedWithCurrentItem="True"
        SelectionUnit="Cell"
        SelectionMode="Single"
        Margin="3,3,3,0" 
        AutoGenerateColumns="False"
        AlternatingRowBackground="WhiteSmoke"
        RowHeaderWidth="30"
        FontSize="18"
        ItemsSource="{Binding POSModel}">
    <ZF:DataGridNumericColumn Header="Qty" Width="80" />
</ZF:ZFDataGrid>

the Custom DataGridNumericColumn work well, except when i press the character for the first time. if i press F2 to edit or double click the column and then press the key, everything works well.

but if i press the key without editing the cell first, the custom DataGridNumericColumn not work.

i put breakpoint on PrepareCellForEdit, and the coding works. but method OnPreviewTextInput works the second time when i press the key. not the first one.

can anyone give me another solution ?

EDITED:

protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
    {
        var textBox = (TextBox) editingElement;
        textBox.PreviewTextInput += OnPreviewTextInput;
        textBox.TextChanged += OnTextChanged; //change here
        return base.PrepareCellForEdit(editingElement, editingEventArgs);
    }

this code only run ONCE, the rest will be handled by OnPreviewTextInput

  private void OnTextChanged(object sender, TextChangedEventArgs e)
    {
        var textBox = (TextBox)sender;

        if (textBox.Text.Contains("-"))
        {
            textBox.TextChanged -= OnTextChanged;
            textBox.Text = "";
        }
    }
2

There are 2 answers

2
almulo On BEST ANSWER

This is way hackier, but I've used it other times and it usually works fine.

Instead of using only PreviewTextInput, couple it with TextChanged too. In the first event you just save the current text in a backing field, and then in the second event you check for the invalid character. If the invalid character has been entered, you just re-set the previous text you stored in your field.

string oldText = string.Empty;
int oldcaret = 0;

protected override FrameworkElement GenerateEditingElement(DataGridCell cell, Object dataItem)
{
    var textBox = (TextBox)base.GenerateEditingElement(cell, dataItem);
    textBox.PreviewTextInput += OnPreviewTextInput;
    textBox.TextChanged += OnTextChanged;
    return textBox;
}

private void OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
    var textBox = (TextBox)sender;

    oldText = textBox.Text;
    oldCaret = textBox.CaretIndex;
}

private void OnTextChanged(object sender, TextChangedEventArgs e)
{
    var textBox = (TextBox)sender;

    if (textBox.Text.Contains("-"))
    {
        textBox.Text = oldText;
        textBox.CaretIndex = oldCaret;
    }
}
1
almulo On

Instead of PrepareCellForEdit, you could try GenerateEditingElement:

protected override FrameworkElement GenerateEditingElement(DataGridCell cell, Object dataItem)
{
    var textBox = (TextBox)base.GenerateEditingElement(cell, dataItem);
    textBox.PreviewTextInput += OnPreviewTextInput;
    return textBox;
}

It should be called before PrepareCellForEdit and also before the key input is processed for the first time, I guess.