簡體   English   中英

C# - WPF - 防止更新綁定的焦點TextBox

[英]C# - WPF - Prevent an update of a bound focused TextBox

我在Windows桌面WPF應用程序中有一個TextBox綁定到ViewModel的屬性。 現在,用戶關注TextBox並開始輸入新值。 在此期間,后台進程獲取相同屬性的新值(例如,因為多用戶環境中的另一個用戶輸入新值並且觀察者正在檢測並傳播此更改)並為此屬性調用PropertyChanged事件。 現在值發生變化,當前用戶輸入的內容將丟失。

在TextBox聚焦時是否有內置的方法來阻止更改? 或者我是否必須構建自己的解決方案?

我認為需要自定義控件來實現您描述的行為。 通過覆蓋默認WPF TextBox上的幾個方法,即使視圖模型發生更改,我們也可以保留用戶輸入。

無論我們的文本框如何更新(鍵盤事件和視圖模型更改)都將調用OnTextChanged方法,但重寫OnPreviewKeyDown方法將分離出直接用戶輸入。 但是, OnPreviewKeyDown不提供對文本框值的輕松訪問,因為它還會調用不可打印的控件字符(箭頭鍵,退格鍵等)

下面,我創建了一個繼承自TextBox的WPF控件,並覆蓋了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);
    }
}

這是我用於測試的示例視圖模型。 它每隔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和綁定到同一屬性的常規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>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM