.NET 3.5 |WPF 文本框在绑定到视图模型属性时拒绝更新自身
本文关键字:属性 模型 视图 拒绝 更新 绑定 WPF 文本 NET | 更新日期: 2023-09-27 17:56:46
在我开始解释我的问题之前,请注意我的目标框架是.NET 3.5。
我有一个文本框,其文本绑定到视图模型属性。我的要求是,当用户在文本框中输入某些内容(通过键盘和鼠标粘贴)时,应清除其中的任何垃圾字符,并使用替换的字符串更新文本框[在下面的示例中,"s"应替换为"h"]。
XAMLCode:
<Style x:Key="longTextField" TargetType="{x:Type TextBoxBase}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AcceptsReturn" Value="True"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border
Name="Border"
Padding="2"
Background="Transparent"
BorderBrush="LightGray"
BorderThickness="1">
<ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="Black"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}" MinLines="3" TextWrapping="Wrap"
SpellCheck.IsEnabled="True" Style="{StaticResource longTextField}"></TextBox>
视图模型属性:
private string _value;
public string Value
{
get
{
return _value;
}
set
{
if (_value == value)
return;
_value = value;
//replaces 's' with 'h' and should update the textbox.
_value = _value.Replace('s','h');
RaisePropertyChanged(() => Value);
}
}
以上对我根本不起作用。视图模型属性资源库正在触发...值正在被替换。.但是文本框没有更新。令人困惑的是,这在.网络4.0。
您知道为什么这不起作用以及除了升级到 .NET 4.0 之外,这个问题的潜在解决方案是什么?
我的要求:
用户可以在多行文本框中键入和粘贴任何内容。
文本可以包含垃圾,在进入文本框之前应进行更改。
提前感谢,-话筒
我遇到了一个非常类似的问题,我想要双向绑定,我正在修改 ViewModel 中的值,并希望在文本框中看到更新。 我能够解决它。 虽然我使用的是 .NET 4.0,但我基本上遇到了同样的问题,所以这可能也值得尝试一下 3.5 的情况。
简答:
我遇到的是一个错误,即TextBox's
显示的文本与该TextBox's
自己的Text
属性的值不同步。 Meleak对类似问题的回答使我明白了这一点,我能够使用Visual Studio 2010中的调试器以及使用Meleak的TextBlock
技术来验证这一点。
我能够通过使用显式绑定来解决它。 这需要在代码隐藏中处理UpdateSource()
和UpdateTarget()
问题(或者在自定义控件代码中,就像我最终所做的那样,以便于重用)。
进一步解释:
以下是我处理显式绑定任务的方式。 首先,我有一个用于 TextChanged 事件的事件处理程序,它更新了绑定的源代码:
// Push the text in the textbox to the bound property in the ViewModel
textBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
其次,我有一个用于文本框的已加载事件的事件处理程序。 在该处理程序中,我为我的 ViewModel 的 PropertyChanged 事件注册了一个处理程序(ViewModel 在这里是"DataContext"):
private void ExplicitBindingTextBox_Loaded(object sender, RoutedEventArgs e)
{
TextBox textBox = sender as TextBox;
if (textBox.DataContext as INotifyPropertyChanged == null)
throw new InvalidOperationException("...");
(textBox.DataContext as INotifyPropertyChanged).PropertyChanged +=
new PropertyChangedEventHandler(ViewModel_PropertyChanged);
}
最后,在 PropertyChanged 处理程序中,我使文本框从 ViewModel 中获取值(通过启动 UpdateTarget())。 这使文本框从视图模型中获取修改后的字符串(在您的情况下是带有替换字符的字符串)。 就我而言,我还必须在刷新文本(从 UpdateTarget())后恢复用户的插入符号位置。 不过,这部分可能适用于也可能不适用于您的情况。
/// <summary>
/// Update the textbox text with the value that is in the VM.
/// </summary>
void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
// This textbox only cares about the property it is bound to
if (e.PropertyName != MyViewModel.ValueStrPropertyName)
return;
// "this" here refers to the actual textbox since I'm in a custom control
// that derives from TextBox
BindingExpression bindingExp = this.GetBindingExpression(TextBox.TextProperty);
// the version that the ViewModel has (a potentially modified version of the user's string)
String viewModelValueStr;
viewModelValueStr = (bindingExp.DataItem as MyViewModel).ValueStr;
if (viewModelValueStr != this.Text)
{
// Store the user's old caret position (relative to the end of the str) so we can restore it
// after updating the text from the ViewModel's corresponding property.
int oldCaretFromEnd = this.Text.Length - this.CaretIndex;
// Make the TextBox's Text get the updated value from the ViewModel
this.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
// Restore the user's caret index (relative to the end of the str)
this.CaretIndex = this.Text.Length - oldCaretFromEnd;
}
}