C# - WPF - 防止更新绑定(bind)的焦点文本框

标签 c# wpf data-binding

我在 Windows 桌面 WPF 应用程序中有一个文本框绑定(bind)到 ViewModel 的属性。现在用户聚焦 TextBox 并开始输入新值。在此期间,后台进程为同一属性获取新值(例如,因为多用户环境中的另一个用户输入了一个新值,并且观察者正在检测并传播此更改)并为此属性调用 PropertyChanged 事件。现在值发生变化,当前用户刚刚输入的内容丢失了。

是否有一种内置方法可以防止在 TextBox 获得焦点时进行更改?还是我必须构建自己的解决方案?

最佳答案

我认为需要一个自定义控件来实现您描述的行为。通过覆盖默认 WPF 文本框上的几个方法,即使 View 模型发生变化,我们也可以保留用户输入。

OnTextChanged 方法将被调用,无论我们的文本框如何更新(键盘事件和 View 模型更改),但是重写 OnPreviewKeyDown 方法将直接分离出来用户输入。但是,OnPreviewKeyDown 不提供对文本框值的轻松访问,因为它也会为不可打印的控制字符(箭头键、退格键等)调用

下面,我制作了一个 WPF 控件,它继承自 TextBox 并重写了 OnPreviewKeyDown 方法以捕获最后一次用户按键的准确时间。 OnTextChanged 检查时间并仅在两个事件快速连续发生时更新文本。

如果最后一次键盘事件发生在几毫秒之前,那么我们的用户可能没有发生更新。

public class StickyTextBox : TextBox
{
    private string _lastText = string.Empty;
    private long _ticksAtLastKeyDown;

    protected override void OnPreviewKeyDown(KeyEventArgs e)
    {
        _ticksAtLastKeyDown = DateTime.Now.Ticks;
        base.OnPreviewKeyDown(e);
    }

    protected override void OnTextChanged(TextChangedEventArgs e)
    {
        if (!IsInitialized)
            _lastText = Text;

        if (IsFocused)
        {
            var elapsed = new TimeSpan(DateTime.Now.Ticks - _ticksAtLastKeyDown);

            // If the time between the last keydown event and our text change is
            // very short, we can be fairly certain than text change was caused
            // by the user.  We update the _lastText to store their new user input
            if (elapsed.TotalMilliseconds <= 5) {
                _lastText = Text;
            }
            else {
                // if our last keydown event was more than a few seconds ago,
                // it was probably an external change
                Text = _lastText;
                e.Handled = true;
            }
        }
        base.OnTextChanged(e);
    }
}

这是我用于测试的示例 View 模型。它每 10 秒从一个单独的线程更新自己的属性 5 次,以模拟来自另一个用户的后台更新。

class ViewModelMain : ViewModelBase, INotifyPropertyChanged
{
    private delegate void UpdateText(ViewModelMain vm);
    private string _textProperty;

    public string TextProperty
    {
        get { return _textProperty; }
        set
        {
            if (_textProperty != value)
            {
                _textProperty = value;
                RaisePropertyChanged("TextProperty");
            }
        }
    }

    public ViewModelMain()
    {
        TextProperty = "Type here";

        for (int i = 1; i <= 5; i++)
        {
            var sleep = 10000 * i;

            var copy = i;
            var updateTextDelegate = new UpdateText(vm =>
                vm.TextProperty = string.Format("New Value #{0}", copy));
            new System.Threading.Thread(() =>
            {
                System.Threading.Thread.Sleep(sleep);
                updateTextDelegate.Invoke(this);
            }).Start();
        }
    }
}

此 XAML 创建了我们的自定义 StickyTextBox 和绑定(bind)到相同属性的常规 TextBox 以演示行为差异:

<StackPanel>
  <TextBox Text="{Binding TextProperty, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
  <TextBlock FontWeight="Bold" Margin="5" Text="The 'sticky' text box">
    <local:StickyTextBox Text="{Binding TextProperty}" MinWidth="200" />
  </TextBlock>
</StackPanel>

关于C# - WPF - 防止更新绑定(bind)的焦点文本框,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41514411/

相关文章:

c# - 在 .net core 2 解决方案中引用 .net 4.7 框架程序集

c# - WPF 位图到图像的转换仅显示黑色图像

c# - WPF Datagrid在添加新项目后进入编辑

c# - 将字符串集合绑定(bind)到 ListView、Windows 窗体

wpf - 如何以 MVVM 样式进行图像绑定(bind)?

c# 在字符串中间插入字符串

c# - 使用从主窗体启动的子窗体上的变量更新主窗体上的文本框

c# - .Net 4.5 和 4.0 中 Async 方法的 Out 关键字替换是什么?

c# - 在 WPF 中使用全局互斥锁的正确模式是什么?

c# - WPF-对话框相对源绑定(bind)(MVVM)