.NET 3.5 | WPF 文本框在绑定到视图模型属性时拒绝更新自身
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14628424/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
.NET 3.5 | WPF Textbox refuses to update itself while binded to a view model property
提问by Mike
Before I start explaining my issue, please note that my target framework is .NET 3.5.
在开始解释我的问题之前,请注意我的目标框架是 .NET 3.5。
I have a textbox whose text is bound to a viewmodel property. My requirement is that when user enters something(via keyboard as well as Mouse Paste) into the textbox, any junk characters inside it should be cleaned and the textbox should be updated with the replaced string[In the below example 's' to be replaced with 'h'].
我有一个文本框,其文本绑定到一个视图模型属性。我的要求是,当用户在文本框中输入某些内容(通过键盘和鼠标粘贴)时,应清除其中的任何垃圾字符,并应使用替换的字符串更新文本框[在下面的示例中要替换的 's'用'h']。
XAMLCode:
XAML 代码:
<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>
ViewModel property:
视图模型属性:
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);
}
}
The above is simply not working for me. The view model property setter is firing...the value is getting replaced..however the textbox is not getting updated. What is confusing is that this works perfectly on .Net4.0.
以上对我来说根本不起作用。视图模型属性设置器正在触发......该值正在被替换......但是文本框没有得到更新。令人困惑的是,这在 .Net4.0 上可以完美运行。
Do you know why this wont work and what is a potential solution to this problem, of course other than upgrading to .NET 4.0?
您知道为什么这行不通吗?除了升级到 .NET 4.0 之外,还有什么潜在的解决方案?
My requirement:
我的要求:
User can type as well as paste anything into a multilined textbox.
The text can contain junk which should be changed before it comes on to the textbox.
用户可以在多行文本框中键入和粘贴任何内容。
文本可能包含垃圾,在进入文本框之前应该对其进行更改。
Thanks in advance, -Mike
提前致谢,-迈克
回答by Jason Frank
I encountered a very similar problem where I wanted the two way binding and I was modifying the value in the ViewModel and expecting to see the update in the TextBox. I was able to get it resolved. Although I was using .NET 4.0, I basically had the same issue, so this may be worth trying out for your situation with 3.5 as well.
我遇到了一个非常相似的问题,我想要双向绑定,我正在修改 ViewModel 中的值并希望看到 TextBox 中的更新。我能够解决它。尽管我使用的是 .NET 4.0,但我基本上遇到了同样的问题,因此对于 3.5 的情况,这可能也值得一试。
Short Answer:
简答:
What I encountered was a bug where the TextBox'sdisplayed text was getting out of sync with the value of that TextBox'sown Textproperty. Meleak's answer to a similar questionclued me in to this and I was able to verify this with the debugger in Visual Studio 2010, as well as by employing Meleak's TextBlocktechnique.
我遇到的是其中的一个错误TextBox's显示的文本是与价值越来越同步了TextBox's自己的Text财产。 Meleak 对类似问题的回答让我明白了这一点,我能够使用 Visual Studio 2010 中的调试器以及使用 Meleak 的TextBlock技术来验证这一点。
I was able to resolve it by using explicit binding. This required handling the UpdateSource()and UpdateTarget()issues myself in code behind (or in a custom control code as I eventually did to make it easier to reuse).
我能够通过使用显式绑定来解决它。这需要在后面的代码中(或在自定义控件代码中,因为我最终这样做以使其更易于重用)中自己处理UpdateSource()和UpdateTarget()问题。
Further Explanation:
进一步说明:
Here's how I handled the explicit binding tasks. First, I had an event handler for the TextChanged event which updated the source of the binding:
这是我处理显式绑定任务的方式。首先,我有一个用于更新绑定源的 TextChanged 事件的事件处理程序:
// Push the text in the textbox to the bound property in the ViewModel
textBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
Second, in I had an event handler for the TextBox's Loaded event. In that handler, I registered a handler for the PropertyChanged event of my ViewModel (the ViewModel was the "DataContext" here):
其次,我有一个用于 TextBox 的 Loaded 事件的事件处理程序。在该处理程序中,我为 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);
}
Finally, in the PropertyChanged handler, I cause the TextBox to grab the value from the ViewModel (by initiating the UpdateTarget()). This makes the TextBox get the modified string from the ViewModel(in your case the one with replaced characters). In my case I also had to handle restoring the user's caret position after refreshing the text (from the UpdateTarget()). That part may or may not apply to your situation though.
最后,在 PropertyChanged 处理程序中,我使 TextBox 从 ViewModel 中获取值(通过启动 UpdateTarget())。 这使得 TextBox 从 ViewModel 获取修改后的字符串(在您的情况下是替换字符的字符串)。在我的情况下,我还必须在刷新文本(来自 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;
}
}

