【问题标题】:WPF TextBox displayed value is not equal to TextBox.TextWPF TextBox 显示的值不等于 TextBox.Text
【发布时间】:2011-07-27 22:02:43
【问题描述】:

我检查了大约 60 个国家/地区的邮政编码。用户输入一个邮政编码,该邮政编码会根据该国家/地区的规则进行检查。美国是一个例外,我可以根据已输入的其他街道地址信息自动填充值。在某些方面,它基本上是一种自动更正功能。问题是如果自动填充的值与之前的 .Text 值匹配,则永远不会重新显示 - 用户刚刚输入的“错误”邮政编码仍然显示!

例如,字段包含“12345”,用户删除“5”,我的属性设置器将值放回“12345”,绑定getter返回“12345”(此时我的_TextChanged事件触发)但WPF仍然显示用户编辑的文本“1234”!

如何强制 WPF 重新显示该值?

为了简化示例,我对 ZipCode 值进行了硬编码(在应用程序中它以单独的方法计算):

public String ZipCode
{
    get { return _address.ZipCode; }
    set
    {
        _address.ZipCode = "12345";
        RaisePropertyChanged("ZipCode");
    }
}

这是 XAML:

<TextBox Name="ZipBox" 
         Text="{Binding Path=ZipCode, UpdateSourceTrigger=PropertyChanged}"
         TextChanged="ZipBox_TextChanged" />

WPF 知道该字段已更改,因为它总是触发此事件。但是这些方法都没有强制 WPF 正确显示“12345”。

private void ZipBox_TextChanged(object sender, TextChangedEventArgs e)
{
    ZipBox.InvalidateVisual();
    ZipBox.InvalidateMeasure();
    ZipBox.UpdateLayout();
}

编辑:我还创建了一个自定义静态扩展方法 .Refresh(this UIElement uiElement) 调用 UIElement.Dispatcher.Invoke() 并且也不会强制重绘。

【问题讨论】:

  • 什么是 ZipBox? ZipCode 属性在哪里?您不需要实际更改 ZipCode 属性吗?你不应该在 TextChanged 事件中这样做吗?什么更新了 ZipCode 属性?

标签: c# wpf binding textbox


【解决方案1】:

这是一个老问题,但从未得到正确回答,所以这是我的建议。 你需要实现INotifyPropertyChanged

取决于它是 ViewModel 还是 Window 的代码隐藏(这是一个 Window 示例):

public class MyWindow: Window, INotifyPropertyChanged
{
   ...
}

然后在该类中实现一个版本的 INotifyPropertyChanged(那里有很多,但这是我使用的):

public event PropertyChangedEventHandler PropertyChanged;


private void OnPropertyChanged(string propertyName = "")
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

private bool SetField<T>(ref T field, T value, string propertyName)
{
    if (EqualityComparer<T>.Default.Equals(field, value))
    {
        return false;
    }

    field = value;
    OnPropertyChanged(propertyName);
    return true;
}

然后简单地将其与属性一起使用:

private string _text;
public string Text
{
    get { return _text; }
    set { SetField(ref _text, value, "Text"); }
}

【讨论】:

    【解决方案2】:

    微软论坛上的朱敏想出了一个解决方案。代码如下:

    bool flagTextChanged = true;
    private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
      TextBox txt = sender as TextBox;
      if (flagTextChanged && txt != null)
      {
        flagTextChanged = false;
        Binding b = BindingOperations.GetBinding(txt, TextBox.TextProperty);
        if (b != null)
        {
          txt.ClearValue(TextBox.TextProperty);
          BindingOperations.SetBinding(txt, TextBox.TextProperty, b);
        }
        flagTextChanged = true;
      }
    }
    

    【讨论】:

      【解决方案3】:

      我相信如果您放置一个只返回传递的值什么都不做的自定义 IValueConverter,那么 TextBox 将正确更新。

      否则,你应该可以这样做:

      ZipBox.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
      

      【讨论】:

      • 我之前试过GetBindingExpression().UpdateTarget()。这并不能解决问题。 WPF 已经具有正确的绑定,因此再次获取它只需将“12345”的 ZipBox.Text 值替换为“12345”,但它继续显示“1234”。我会尝试 IValueConverter .....
      • 使用 IValueConverter 无效 - 字段仍显示用户价值,而不是程序价值。
      【解决方案4】:

      您的意思是在用户手动编辑文本然后离开框后发生这种情况吗?如果是这样,问题可能是绑定系统不想覆盖用户输入的内容,并且隐含假设如果您在属性上调用 get ,文本框 Text 设置的值将匹配该值,因为在设置期间没有抛出异常。这是“魔法属性”的缺点之一,其中设置的值是变异而不是验证的,因为如果设置器没有抛出异常,许多属性的使用者会假设以下情况为真:

      Instance.X = value;
      Debug.Assert(Instance.X == value);
      

      避免此问题的一个想法是设置一个标志,如果 ZipCode 字段已被自动确定(可能通过绑定 IsReadOnly),则禁止编辑该字段。

      【讨论】:

      • 实际上,用户永远不会离开该字段,因为 XAML 设置 UpdateSourceTrigger=PropertyChanged 会强制将每个键入的字符发送到我的验证例程(在 setter 中)。这看起来确实像是 WPF 中的一个缺陷:我希望 .Text 属性的值与显示的值相匹配。
      • @user610342 - 默认的 WPF 控件倾向于更被动的验证,在绑定上使用 ValidationRules 或从绑定的属性中抛出异常。听起来您正在通过阻止/操纵数据对象的用户输入来进行更积极的验证。
      • @user610342,如果文本可以在用户键入时发生变化(由于 setter 更改值),这会为 TextBox 控件创建复杂的边界条件,因为它不知道正确的位置如果值可以任意更改,则用于数据输入插入符号。您实际上违反了 TextBox 期望其 Text 属性如何表现的预期使用合同,这会产生您正在观察的奇怪症状。
      • WPF 处理设置器非常雄辩地更改以编程方式输入的值的情况。注意这里的关键词是“改变”。因此,如果我将设置器更改为 _address.ZipCode += "#",那么 WPF 总是会丢弃用户输入并重新显示以编程方式设置的 Zipcode(带有尾随的“#”)。并且它将插入符号放在这个新字符串中,紧跟在用户输入字符的位置之后(当然不会显示)。因此,它不是突变违规,而是 WPF 只是缓存了它“认为”不需要更新的显示值。
      【解决方案5】:

      我对 WPF 不是特别熟悉,但我做了很多 Web 编程。您可以尝试触发帖子以重新加载整个页面(或仅使用 ajax 的文本框),这应该允许您设置新值。

      【讨论】:

      • WPF 不是一种网络技术,所以这并不适用。
      • 将您的建议转换为 WPF,我上了可视化树并在几个邮政编码文本框父级上执行了 .InValidateVisual。这些都没有强迫 WPF 重绘页面。我从来没有让 .InValidateVisual 工作,我真的想知道它是否有任何作用!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-26
      • 2016-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-05
      • 1970-01-01
      相关资源
      最近更新 更多