wpf数据网格:在wpf中创建数据网格数值列

e7arh2l6  于 2023-05-30  发布在  其他
关注(0)|答案(8)|浏览(208)

我有一个要求,我想使一个datagridcolumn只接受数字值(整数),当用户输入的东西以外的数字处理文本框。我尝试了很多网页,我厌倦了这些,我非常感谢任何人有帮助的头脑。

kcrjzv8t

kcrjzv8t1#

基于@nit的建议,你可以创建自己的DataGridTextColumn派生类,如下所示:

public class DataGridNumericColumn : DataGridTextColumn
{
    protected override object PrepareCellForEdit(System.Windows.FrameworkElement editingElement, System.Windows.RoutedEventArgs editingEventArgs)
    {
        TextBox edit = editingElement as TextBox;
        edit.PreviewTextInput += OnPreviewTextInput;

        return base.PrepareCellForEdit(editingElement, editingEventArgs);
    }

    void OnPreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
    {
        try
        {
            Convert.ToInt32(e.Text);
        }
        catch
        {
            // Show some kind of error message if you want

            // Set handled to true
            e.Handled = true;
        }
    }
}

PrepareCellForEdit方法中,将OnPreviewTextInput方法注册到编辑TextBoxPreviewTextInput事件,在该事件中验证数值。
在xaml中,您只需使用它:

<DataGrid ItemsSource="{Binding SomeCollection}">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding NonNumericProperty}"/>
            <local:DataGridNumericColumn Binding="{Binding NumericProperty}"/>
        </DataGrid.Columns>
    </DataGrid>

希望这个能帮上忙

a0zr77ik

a0zr77ik2#

使用TryParse代替,这有助于将输入值限制为整数。

/// <summary>
    /// This class help to create data grid cell which only support interger numbers.
    /// </summary>
    public class DataGridNumericColumn : DataGridTextColumn
    {
        protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
        {
            TextBox edit = editingElement as TextBox;

            if (edit != null) edit.PreviewTextInput += OnPreviewTextInput;

            return base.PrepareCellForEdit(editingElement, editingEventArgs);
        }

        private void OnPreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
        {
            int value;

            if (!int.TryParse(e.Text, out value))
                e.Handled = true;
        }
    }
slsn1g29

slsn1g293#

如果你不想显示任何验证错误,只想阻止任何非数字值,那么你可以创建DataGridTemplateColumn,并在CellEditingTemplate中使用TextBox

<DataGridTemplateColumn Width="100*">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Path=NumericProperty}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                    <DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <TextBox PreviewTextInput="TextBox_PreviewTextInput" Text="{Binding Path=NumericProperty}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellEditingTemplate>
                </DataGridTemplateColumn>

在TextBox的PreviewTextInput中,如果值不是整数,则设置e.Handled = true

private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            try
            {
                Convert.ToInt32(e.Text);
            }
            catch
            {
                e.Handled = true;
            }
        }
jq6vz3qz

jq6vz3qz4#

我来这里是为了解决同样的问题:将DataGrid上单元格的输入约束为数字。但这个公认的答案对我不起作用。以下机构做到了这一点:
1.为DataGrid添加PreparingForCellEdit的事件处理程序。
1.在该事件处理程序中,将EditingElement强制转换为TextBox,并将PreviewTextInput的事件处理程序添加到TextBox
1.在PreviewTextInput事件处理程序中,如果不允许输入,则将e.Handled设置为true。
如果用户单击要编辑的单元格,则上述步骤有效。但是,如果单元格未处于编辑模式,则不会调用PreparingForCellEdit事件。要在这种情况下执行验证,请执行以下操作:
1.为PreviewTextInputDataGrid添加事件处理程序。
1.在该事件处理程序中,安全地将e.OriginalSource转换为DataGridCell(如果不是DataGridCell,则退出),检查DataGridCell'sIsEditing属性,如果单元格未编辑,则将e.Handled设置为true。
上面的效果是,用户必须单击单元格才能编辑其内容,因此,对单元格内容的所有更改都将调用上面的PreparingForCellEdit/PreviewTextInput组合。

flvlnr44

flvlnr445#

只是为了扩展@Omribitan的答案,以下是添加了数据Paste防护的解决方案:

public class NumericTextColumn : DataGridTextColumn
{
    protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
    {
        var edit = editingElement as TextBox;
        edit.PreviewTextInput += Edit_PreviewTextInput;
        DataObject.AddPastingHandler(edit, OnPaste);
        return base.PrepareCellForEdit(editingElement, editingEventArgs);
    }

    private void OnPaste(object sender, DataObjectPastingEventArgs e)
    {
        var data = e.SourceDataObject.GetData(DataFormats.Text);
        if (!IsDataValid(data)) e.CancelCommand();
    }

    private void Edit_PreviewTextInput(object sender, TextCompositionEventArgs e)
    {
        e.Handled = !IsDataValid(e.Text);
    }

    bool IsDataValid(object data)
    {
        try
        {
            Convert.ToInt32(data);
            return true;
        }
        catch
        {
            return false;
        }
    }
}
7ivaypg9

7ivaypg96#

不管怎样,我是这么解决的。此解决方案允许您在验证输入时指定各种选项,允许使用字符串格式(例如数据网格中的“$15.00”)和更多。
Binding类本身提供的空值和字符串格式不足以满足需要,因为当单元格可编辑时,这两种格式都不能正确工作,因此这个类涵盖了它。它使用了另一个我已经用了很久的类:TextBoxInputBehavior,它对我来说是一笔无价的财富,它最初来自WPF – TextBox Input Behavior博客文章,尽管这里的版本似乎要老得多(但经过了很好的测试)。因此,我所做的只是将我在TextBox上已经拥有的现有功能转移到my custom列,因此我在两者中具有相同的行为。是不是很棒?
下面是自定义列的代码:

public class DataGridNumberColumn : DataGridTextColumn
{
    private TextBoxInputBehavior _behavior;

    protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
    {
        var element = base.GenerateElement(cell, dataItem);

        // A clever workaround the StringFormat issue with the Binding set to the 'Binding' property. If you use StringFormat it
        // will only work in edit mode if you changed the value, otherwise it will retain formatting when you enter editing.
        if (!string.IsNullOrEmpty(StringFormat))
        {
            BindingOperations.ClearBinding(element, TextBlock.TextProperty);
            BindingOperations.SetBinding(element, FrameworkElement.TagProperty, Binding);
            BindingOperations.SetBinding(element,
                TextBlock.TextProperty,
                new Binding
                {
                    Source = element,
                    Path = new PropertyPath("Tag"),
                    StringFormat = StringFormat
                });
        }

        return element;
    }

    protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
    {
        if (!(editingElement is TextBox textBox))
            return null;

        var originalText = textBox.Text;

        _behavior = new TextBoxInputBehavior
        {
            IsNumeric = true,
            EmptyValue = EmptyValue,
            IsInteger = IsInteger
        };

        _behavior.Attach(textBox);

        textBox.Focus();

        if (editingEventArgs is TextCompositionEventArgs compositionArgs) // User has activated editing by already typing something
        {
            if (compositionArgs.Text == "\b") // Backspace, it should 'clear' the cell
            {
                textBox.Text = EmptyValue;
                textBox.SelectAll();
                return originalText;
            }

            if (_behavior.ValidateText(compositionArgs.Text))
            {
                textBox.Text = compositionArgs.Text;
                textBox.Select(textBox.Text.Length, 0);
                return originalText;
            }
        }

        if (!(editingEventArgs is MouseButtonEventArgs) || !PlaceCaretOnTextBox(textBox, Mouse.GetPosition(textBox)))
            textBox.SelectAll();

        return originalText;
    }

    private static bool PlaceCaretOnTextBox(TextBox textBox, Point position)
    {
        int characterIndexFromPoint = textBox.GetCharacterIndexFromPoint(position, false);
        if (characterIndexFromPoint < 0)
            return false;
        textBox.Select(characterIndexFromPoint, 0);
        return true;
    }

    protected override void CancelCellEdit(FrameworkElement editingElement, object uneditedValue)
    {
        UnwireTextBox();
        base.CancelCellEdit(editingElement, uneditedValue);
    }

    protected override bool CommitCellEdit(FrameworkElement editingElement)
    {
        UnwireTextBox();
        return base.CommitCellEdit(editingElement);
    }

    private void UnwireTextBox() => _behavior.Detach();

    public static readonly DependencyProperty EmptyValueProperty = DependencyProperty.Register(
        nameof(EmptyValue),
        typeof(string),
        typeof(DataGridNumberColumn));

    public string EmptyValue
    {
        get => (string)GetValue(EmptyValueProperty);
        set => SetValue(EmptyValueProperty, value);
    }

    public static readonly DependencyProperty IsIntegerProperty = DependencyProperty.Register(
        nameof(IsInteger),
        typeof(bool),
        typeof(DataGridNumberColumn));

    public bool IsInteger
    {
        get => (bool)GetValue(IsIntegerProperty);
        set => SetValue(IsIntegerProperty, value);
    }

    public static readonly DependencyProperty StringFormatProperty = DependencyProperty.Register(
        nameof(StringFormat),
        typeof(string),
        typeof(DataGridNumberColumn));

    public string StringFormat
    {
        get => (string) GetValue(StringFormatProperty);
        set => SetValue(StringFormatProperty, value);
    }
}

我所做的是偷看DataGridTextColumn的源代码,并以几乎相同的方式处理TextBox创建,并将自定义行为附加到TextBox。
下面是我附加的行为的代码(这是一个可以在任何TextBox上使用的行为):

public class TextBoxInputBehavior : Behavior<TextBox>
{
    #region DependencyProperties

    public static readonly DependencyProperty RegularExpressionProperty = DependencyProperty.Register(
        nameof(RegularExpression), 
        typeof(string), 
        typeof(TextBoxInputBehavior), 
        new FrameworkPropertyMetadata(".*"));

    public string RegularExpression
    {
        get
        {
            if (IsInteger)
                return @"^[0-9\-]+$";
            if (IsNumeric)
                return @"^[0-9.\-]+$";
            return (string)GetValue(RegularExpressionProperty);
        }
        set { SetValue(RegularExpressionProperty, value); }
    }

    public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register(
        nameof(MaxLength), 
        typeof(int), 
        typeof(TextBoxInputBehavior),
        new FrameworkPropertyMetadata(int.MinValue));

    public int MaxLength
    {
        get { return (int)GetValue(MaxLengthProperty); }
        set { SetValue(MaxLengthProperty, value); }
    }

    public static readonly DependencyProperty EmptyValueProperty = DependencyProperty.Register(
        nameof(EmptyValue), 
        typeof(string), 
        typeof(TextBoxInputBehavior));

    public string EmptyValue
    {
        get { return (string)GetValue(EmptyValueProperty); }
        set { SetValue(EmptyValueProperty, value); }
    }

    public static readonly DependencyProperty IsNumericProperty = DependencyProperty.Register(
        nameof(IsNumeric), 
        typeof(bool), 
        typeof(TextBoxInputBehavior));

    public bool IsNumeric
    {
        get { return (bool)GetValue(IsNumericProperty); }
        set { SetValue(IsNumericProperty, value); }
    }

    public static readonly DependencyProperty IsIntegerProperty = DependencyProperty.Register(
        nameof(IsInteger),
        typeof(bool),
        typeof(TextBoxInputBehavior));

    public bool IsInteger
    {
        get { return (bool)GetValue(IsIntegerProperty); }
        set
        {
            if (value)
                SetValue(IsNumericProperty, true);
            SetValue(IsIntegerProperty, value);
        }
    }

    public static readonly DependencyProperty AllowSpaceProperty = DependencyProperty.Register(
        nameof(AllowSpace),
        typeof (bool),
        typeof (TextBoxInputBehavior));

    public bool AllowSpace
    {
        get { return (bool) GetValue(AllowSpaceProperty); }
        set { SetValue(AllowSpaceProperty, value); }
    }

    #endregion

    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.PreviewTextInput += PreviewTextInputHandler;
        AssociatedObject.PreviewKeyDown += PreviewKeyDownHandler;
        DataObject.AddPastingHandler(AssociatedObject, PastingHandler);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        if (AssociatedObject == null)
            return;

        AssociatedObject.PreviewTextInput -= PreviewTextInputHandler;
        AssociatedObject.PreviewKeyDown -= PreviewKeyDownHandler;
        DataObject.RemovePastingHandler(AssociatedObject, PastingHandler);
    }

    private void PreviewTextInputHandler(object sender, TextCompositionEventArgs e)
    {
        string text;
        if (AssociatedObject.Text.Length < AssociatedObject.CaretIndex)
            text = AssociatedObject.Text;
        else
            text = TreatSelectedText(out var remainingTextAfterRemoveSelection)
                ? remainingTextAfterRemoveSelection.Insert(AssociatedObject.SelectionStart, e.Text)
                : AssociatedObject.Text.Insert(AssociatedObject.CaretIndex, e.Text);
        e.Handled = !ValidateText(text);
    }

    private void PreviewKeyDownHandler(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Space)
            e.Handled = !AllowSpace;

        if (string.IsNullOrEmpty(EmptyValue))
            return;

        string text = null;

        // Handle the Backspace key
        if (e.Key == Key.Back)
        {
            if (!TreatSelectedText(out text))
            {
                if (AssociatedObject.SelectionStart > 0)
                    text = AssociatedObject.Text.Remove(AssociatedObject.SelectionStart - 1, 1);
            }
        }
        // Handle the Delete key
        else if (e.Key == Key.Delete)
        {
            // If text was selected, delete it
            if (!TreatSelectedText(out text) && AssociatedObject.Text.Length > AssociatedObject.SelectionStart)
            {
                // Otherwise delete next symbol
                text = AssociatedObject.Text.Remove(AssociatedObject.SelectionStart, 1);
            }
        }

        if (text == string.Empty)
        {
            AssociatedObject.Text = EmptyValue;
            if (e.Key == Key.Back)
                AssociatedObject.SelectionStart++;
            e.Handled = true;
        }
    }

    private void PastingHandler(object sender, DataObjectPastingEventArgs e)
    {
        if (e.DataObject.GetDataPresent(DataFormats.Text))
        {
            var text = Convert.ToString(e.DataObject.GetData(DataFormats.Text));

            if (!ValidateText(text))
                e.CancelCommand();
        }
        else
            e.CancelCommand();
    }

    public bool ValidateText(string text)
    {
        return new Regex(RegularExpression, RegexOptions.IgnoreCase).IsMatch(text) && (MaxLength == int.MinValue || text.Length <= MaxLength);
    }

    /// <summary>
    /// Handle text selection.
    /// </summary>
    /// <returns>true if the character was successfully removed; otherwise, false.</returns>
    private bool TreatSelectedText(out string text)
    {
        text = null;
        if (AssociatedObject.SelectionLength <= 0)
            return false;

        var length = AssociatedObject.Text.Length;
        if (AssociatedObject.SelectionStart >= length)
            return true;

        if (AssociatedObject.SelectionStart + AssociatedObject.SelectionLength >= length)
            AssociatedObject.SelectionLength = length - AssociatedObject.SelectionStart;

        text = AssociatedObject.Text.Remove(AssociatedObject.SelectionStart, AssociatedObject.SelectionLength);
        return true;
    }
}

以上行为课的所有好成绩都归功于blindmeis,我只是随着时间的推移调整了一下。在检查他的博客后,我看到他有一个更新的版本,所以你可以看看。我很高兴地发现我也可以在DataGrid上使用他的行为!
这个解决方案工作得很好,你可以通过鼠标/键盘正确地编辑单元格,正确地粘贴内容,使用任何绑定源更新触发器,使用任何字符串格式等。- 它只是工作。
下面是一个如何使用它的示例:

<local:DataGridNumberColumn Header="Nullable Int Currency" IsInteger="True" Binding="{Binding IntegerNullable, TargetNullValue=''}" StringFormat="{}{0:C}" />

希望这对某人有帮助。

2w2cym1i

2w2cym1i7#

我继续从Omris方法,但我希望能够删除单元格值后,它已输入的情况下,他们想清除它
我这样做的方法是覆盖CommitCellEdit方法并使字符串为null而不是空白。我也用decimal?在我的情况下

public class DataGridNumericColumn : DataGridTextColumn
{
    protected override object PrepareCellForEdit(System.Windows.FrameworkElement editingElement, System.Windows.RoutedEventArgs editingEventArgs)
    
    {
        TextBox edit = editingElement as TextBox;
        edit.PreviewTextInput += OnPreviewTextInput;

        return base.PrepareCellForEdit(editingElement, editingEventArgs);
    }

    protected override bool CommitCellEdit(System.Windows.FrameworkElement editingElement)
    {
        TextBox tb = editingElement as TextBox;
        if (string.IsNullOrEmpty(tb.Text))
            tb.Text = null;
        
        return base.CommitCellEdit(editingElement);
    }
    void OnPreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
    
    {
        try
        {
            Convert.ToDecimal(e.Text);
        }
        catch
        {
            // Show some kind of error message if you want

            // Set handled to true
            e.Handled = true;
        }
    }
}
kxe2p93d

kxe2p93d8#

这个类扩展了前面的答案的功能,而不会像后面的一些答案那样变得过于复杂。
主要增加的内容是:

*TextBox_Loaded用于处理用户直接在单元格上输入而不是首先进入编辑模式的情况
*TextBox_LostFocus清理用户输入。处理小数点后的前导0和尾随0等。

其余的与前面的答案非常相似,除了这个检查数字输入是否与Regex有效(允许负数和小数)。

internal class CustomDataGridNumericColumn : DataGridTextColumn
{
    protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
    {
        var textBox = editingElement as TextBox;
        textBox.PreviewTextInput += OnPreviewTextInput;
        DataObject.AddPastingHandler(textBox, OnPaste);
        textBox.LostFocus += TextBox_LostFocus;
        textBox.Loaded += TextBox_Loaded; // Add Loaded event handler to handle initial text
        return base.PrepareCellForEdit(editingElement, editingEventArgs);
    }

    private void TextBox_Loaded(object sender, RoutedEventArgs e)
    {
        var textBox = (TextBox)sender;
        textBox.Loaded -= TextBox_Loaded; // Remove the event handler after it's executed once
        if (!IsValidNumericInput(textBox.Text))
        {
            textBox.Text = string.Empty; // Clear the text if it's not valid initially
        }
    }

    private void OnPreviewTextInput(object sender, TextCompositionEventArgs e)
    {
        var textBox = (TextBox)sender;
        var proposedText = textBox.Text.Insert(textBox.CaretIndex, e.Text);
        if (!IsValidNumericInput(proposedText))
        {
            e.Handled = true;
        }
    }

    private void OnPaste(object sender, DataObjectPastingEventArgs e)
    {
        var data = e.SourceDataObject.GetData(DataFormats.Text);
        if (!IsValidNumericInput(data.ToString()))
        {
            e.CancelCommand();
        }
    }

    private void TextBox_LostFocus(object sender, RoutedEventArgs e)
    {
        var textBox = (TextBox)sender;
        var text = textBox.Text;
        if (string.IsNullOrEmpty(text))
        {
            textBox.Text = null;
            return;
        }

        if (decimal.TryParse(text, out decimal value))
        {
            textBox.Text = value.ToString("G29"); // Removes trailing zeros for a decimal
        }
    }

    private static bool IsValidNumericInput(string input)
    {
        const string numericPattern = @"^-?[0-9]*(?:\.[0-9]*)?$";
        return Regex.IsMatch(input, numericPattern);
    }
  }
}

相关问题