【问题标题】:Unable to use Undo in TextChanged无法在 TextChanged 中使用撤消
【发布时间】:2012-06-22 08:47:51
【问题描述】:

当使用 textbox.Undo();我收到以下错误:

撤消单元打开时无法撤消或重做。

现在我明白为什么会出现这种情况(因为它是一个活动的可撤消事件),但是如果用户输入了无效字符,我可以通过什么事件对文本框执行验证并撤消更改?

【问题讨论】:

  • 你应该改用行为。
  • SepehrM - 你能举个例子吗?如果您还不想验证但只想阻止这个该死的异常消息破坏您的会话怎么办?当用户输入速度太快或者可能将一大块数据粘贴到字段中时,我会得到这个。甚至没有验证。

标签: c# wpf undo


【解决方案1】:

回答 simbay 的方法,我认为它正在被驳回。

您不能在 TextChanged 中调用 Undo,因为 TextBox 仍在准备撤消操作。它似乎有时有效,有时无效,因此这表明在发出事件信号和完成撤消准备之间存在竞争条件。

但是,在 Dispatcher 上调用 Undo 将允许文本框完成其撤消准备。您可以验证文本更改的结果,然后决定是否要保留或撤消更改。这可能不是最好的方法,但我尝试了,将一堆文本更改和粘贴到文本框中,无法重现异常。

仅当您想防止输入或粘贴无效字符时,“接受的答案”才很棒,但通常我经常对 TextBox 输入进行更多涉及验证并希望验证最终文本值。从预览事件中辨别最终文本并不容易,因为就控件而言,还没有发生任何事情。

回答Terribad的问题,simbay的回答在更多情况下更好更简洁。

tb.TextChanged += ( sender, args ) =>
{
    if(! MeetsMyExpectations( tb.Text ) )
        Dispatcher.BeginInvoke(new Action(() => tb.Undo()));
};

我已经阅读了很多关于文本框验证的狂野冒险,这和我发现的一样简单。

【讨论】:

    【解决方案2】:

    您应该使用 PreviewTextInputDataObject.Pasting 事件,而不是使用 Undo 和 TextChanged。在PreviewTextInput 事件处理程序中,如果键入的文本是无效字符,则将e.Handled 设置为true。在Pasting 事件处理程序中,如果粘贴的文本无效,则调用e.CancelCommand()

    这是一个只接受数字 0 和 1 的文本框示例:

    XAML:

    <Window x:Class="BinaryTextBox.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="133" Width="329">
        <StackPanel>
            <TextBox x:Name="txtBinary" Width="100" Height="24"
                     PreviewTextInput="txtBinary_PreviewTextInput"
                     DataObject.Pasting="txtBinary_Pasting"/>
        </StackPanel>
    </Window>
    

    后面的代码:

    using System.Text.RegularExpressions;
    using System.Windows;
    using System.Windows.Input;
    
    namespace BinaryTextBox
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void txtBinary_PreviewTextInput(object sender,
                     TextCompositionEventArgs e)
            {
                e.Handled = e.Text != "0" && e.Text != "1";
            }
    
            private void txtBinary_Pasting(object sender, DataObjectPastingEventArgs e)
            {
                if (!Regex.IsMatch(e.DataObject.GetData(typeof(string)).ToString(), "^[01]+$"))
                {
                    e.CancelCommand();
                }
            }
        }
    }
    

    【讨论】:

    • 太好了,“粘贴”处理程序还会验证您是否使用拖放插入字符。
    • 这种方法的问题是 PreviewTextInput 只预览正在输入的内容,而不是最终结果。这只有在编写一个非常复杂且不可维护的算法来解释所有可能的输入时才有效。例如,在小数字段上,尝试验证减号或句点何时适用。显然,您只能在每个位置键入 1,但不能在每个位置都键入。最佳解决方案是验证最终结果,无论如何键入。不幸的是,WPF 不支持此类。
    • Neolisk,您可以将发件人转换为 TextBox 并检查 CaretPosition 以确定是否在开头插入了“+”或“-”,因此您得到...... ......... if ((e.Text == "+" || e.Text=="-") && (发件人为 TextBox).CaretPosition != 0) e.Handled=true ;
    • Neolisk,同样,您可以检查是否为“。”字符串中已经存在,则不允许另一个........ if(e.Text=="." && (sender as TextBox). Text.Contains(".")) e.Handled=true;
    【解决方案3】:

    从来自 TextChanged 事件处理程序中异步调用撤消:

    Dispatcher.BeginInvoke(new Action(() => tb.Undo())) 
    

    【讨论】:

    • 这个解决方案与接受的答案相比如何?
    猜你喜欢
    • 1970-01-01
    • 2017-04-26
    • 2011-12-27
    • 2015-01-23
    • 2018-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-01
    相关资源
    最近更新 更多