【问题标题】:WPF textbox text databinding strange behaviorWPF 文本框文本数据绑定奇怪的行为
【发布时间】:2014-02-28 07:36:42
【问题描述】:

假设我有 texbox 和 textblock:

    <TextBox Name="textBox1"
             Text="{Binding Path=user,
                            RelativeSource={RelativeSource FindAncestor,
                                    AncestorType=my:MainWindow, AncestorLevel=1},
                            Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    <TextBlock Name="textBlock1" 
               Text="{Binding Path=user, 
                              RelativeSource={RelativeSource FindAncestor,
                                    AncestorType=my:MainWindow, AncestorLevel=1},
                            Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

后面的代码:

    private string _user = "a";

    public string user
    {
        get
        {
            return _user;
        }

        set
        {
            if (!String.IsNullOrEmpty(value.Trim()))
                _user = value;
            //else
                //_user = _user + Environment.NewLine;

            NotifyPropertyChanged("user");

        }
    }

我想要实现的 - 不允许用户在文本框和文本块中输入空字符串(或空格)。如果文本为空 - 我只是恢复以前的值(由用户文本清除 - 在我的代码的情况下为“a”)。

上面的代码非常适合文本块,但不适用于文本框(在文本框内按退格键后的屏幕截图):

此外,如果您从

中删除评论
 //else
       //_user = _user + Environment.NewLine;

一切都像魅力一样(当然,除了我需要恢复以前的值,而不是 Environment.NewLine 的值 :))

那么发生了什么?为什么文本框的行为与预期的差异很大(甚至与文本块的行为不同)?

更新:

我的期望:

如果我在文本框中按退格键,我的表达式!String.IsNullOrEmpty(value.Trim()) 是假的。

_user 未更新(仍为“a”)。

在下一行调用 NotifyPropertyChanged("user") 应该强制绑定以获取“a”(如果我在 getter 中切换断点,_user 等于“a”)。

但正如您在屏幕截图中看到的那样 - 文本框由于某种原因是空的。

【问题讨论】:

  • 奇怪的 RelativeSource 绑定有什么问题 - 我认为您可以完全删除不需要指定源的内容 - DataContext 自动成为 Window 本身。
  • 我看不到问题 - 如果您在文本框中按退格键,您的表达式:!String.IsNullOrEmpty(value.Trim()) 将为 false,因此 _user 不会更新,因此当绑定再次获取它时,它仍然是“a”!
  • 你不能输入TextBlock所以你怎么能更新它的值——它永远不会调用set!
  • @markmnl 如果我在文本框中按退格键,我的表达式:!String.IsNullOrEmpty(value.Trim()) 是假的。 _user 没有更新(它仍然是“a”)。在下一行调用 NotifyPropertyChanged("user") 应该强制绑定获取“a”(如果我在 setter 中切换断点,情况就是如此)。但正如您在屏幕截图中看到的那样 - 文本框由于某种原因是空的。我已经更新了我的问题。
  • @markmnl 删除 RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1} 使绑定对我不起作用。不知道为什么,但 DataContext 自动是 Window 本身在我的情况下不是正确的陈述

标签: c# wpf data-binding textbox


【解决方案1】:

我不会称其为任何类型的错误。造成差异的原因是TextBox 可以从两个位置更改,而TextBlock 只能从一个更改。那个来源有一条规则,不允许该值是空的string,因此它不会显示空的string。另一方面,TextBox 没有这样的规则来阻止用户更改值。为此,您需要向 TextBox.PreviewKeyDown 事件添加处理程序:

<TextBox Name="textBox1" Text="{Binding Path=user, RelativeSource={RelativeSource
    AncestorType=my:MainWindow}, UpdateSourceTrigger=PropertyChanged}" 
    PreviewKeyDown="TextBox_PreviewKeyDown"/>

...

private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
    if (((TextBox)sender).Text.Trim().Length == 1 && 
        (e.Key == Key.Delete || e.Key == Key.Back)) e.Handled = true;
}

还请注意,您不需要在RelativeSource Binding 中设置这些属性的一半...以上应该可以正常工作。 TextBox.TextProperty DependencyProperty 在其默认的FrameworkMetadata 中设置了BindsTwoWayByDefault 标志,因此您无需在此处使用Mode=TwoWay,只需设置AncestorType 属性即可。

【讨论】:

  • TextBlock 尊重 NotifyPropertyChanged 和 get 而 TextBox 没有 - 这是一个错误。 .NET 4.5 中不存在该错误。即使在 3.5 中,您也可以将“aa”重置为“bb”并且它可以工作。
  • 不管怎样,给TextBox.PreviewKeyDown事件添加一个处理程序可以满足用户的需求。
  • 将处理程序附加到TextBox.PreviewKeyDown 为我节省了问题。我接受这个,谢谢。但尽管如此,我仍然认为这种 TextBox 行为出乎意料和奇怪。对我来说,这是一个错误(抱歉,我无法按照@Blam 的建议在 .NET 4.5 上进行测试)
  • 并且 +1 指向我那些多余的属性:) BTW RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1} 是由 VS 2010 SP1 自动设置的 - 我用鼠标选择绑定类型。奇怪的 IDE 行为..
  • 我没有得到你的第一段解释。 NotifyPropertyChanged("user") 应该触发绑定并将TextBoxTextProperty 重新设置为最后一个字符,不是吗?此外,您的回答不会阻止用户选择和删除文本,或右键单击 -> 剪切等。无论如何,原始问题是基于一个可疑的要求。
【解决方案2】:

我也遇到过同样的问题
当人们告诉我他们无法复制时,这让我想到了环境
对我来说,它不适用于 .NET 3.5 或 .NET 4.0,但它确实适用于 .NET 4.5
答案是在 .NET 4.5 上尝试

这是一种设置 DataContext 的 XAML 方式
没有理由在 TextBlock 上使用 TwoWay 或 UpdateSourceTrigger

    DataContext="{Binding RelativeSource={RelativeSource self}}"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <StackPanel Orientation="Vertical">
        <TextBox Text="{Binding TextNoEmpty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBlock Text="{Binding TextNoEmpty, Mode=OneWay}"/>
    </StackPanel>
</Grid>

在 .NET 3.5 上测试了上述内容并重现了问题
在 .NET 4.5 上测试了上述内容,它工作正常

如果您尝试在 setter 中截断到最大长度,可能会遇到同样的问题。

【讨论】:

  • 想解释否决票?这是在 3.5 上重现问题并在 4.5 上工作的经过测试的代码。
  • 这不是他问题的答案,数据上下文的“问题”不是他原来的问题..
  • 他的问题是:为什么,既然我的设置器拒绝设置值,那么 TextBox 的文本会改变吗?
  • 而且(不像你)我确实回答了这个问题。答案是它适用于 .NET 4.5。见第三句。
  • 抱歉,您提到有关 DataContext 的问题让您感到困惑,而您仍然可以使用 {RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1} 的绑定重现该问题
猜你喜欢
  • 2015-11-24
  • 1970-01-01
  • 2012-03-15
  • 1970-01-01
  • 2012-11-23
  • 1970-01-01
  • 2012-12-25
  • 1970-01-01
  • 2012-11-24
相关资源
最近更新 更多