你真的需要处理TextChanged
事件吗?只需绑定到Text
属性,您就可以收到更改通知:
<TextBox Text="{Binding TextValue, Mode=TwoWay}" />
然后在视图模型中:
private string _textValue;
public string TextValue
{
get
{
return _textValue;
}
set
{
if (_textValue == value)
return;
_textValue = value;
OnTextChanged(value); // react to the changed value
}
}
编辑:
Text
如果你想从你的内部获取价值,你需要注意两件事Command
:
首先,您需要修复CommandParameter
绑定。通过使用{Binding Text}
,您实际上是在尝试绑定到视图模型中的属性,即您首先需要将该TextBox.Text
属性绑定到相同的视图模型属性。正如您在评论中所说,这对您没有好处,因为您需要有关每次更改的信息,而不仅仅是失去焦点的信息,因此您获得的价值还不够最新。
因此,正确的方法是您的第二次尝试,即直接绑定到TextBox
usingElementName
语法。不幸的是,触发器不是可视化树的一部分,因此它们无法访问 XAML 名称范围(您可以在此博客文章中了解更多信息)。您可以通过使用 MVVMHelpers.Metro 包来解决NameScopeBinding
这个问题:
<Behaviors:NameScopeBinding x:Key="MyTextBox"
Source="{Binding ElementName=MyTextBox}" />
现在您可以使ElementName
绑定工作:
<i:InvokeCommandAction Command="{Binding UpdateText}"
CommandParameter="{Binding Source.Text, Source={StaticResource MyTextBox}}"/>
你还有第二个问题。您绑定的Text
值仅在失去焦点时更新,因此您在处理TextChanged
事件时无法获得正确的值。解决方案是绑定到TextBox
自身:
<i:InvokeCommandAction Command="{Binding UpdateText}"
CommandParameter="{Binding Source, Source={StaticResource MyTextBox}}"/>
然后在命令内部Text
直接从以下获取属性TextBox
:
private void OnUpdateText(object parameter)
{
var value = ((TextBox) parameter).Text;
// process the Text value as you need to.
}
编辑2:
要使上述代码与 PCL 中的视图模型一起工作,您可以采取几种方法。
最简单的技巧是使用反射。由于它在 PCL 中可用,因此您可以访问该Text
属性并读取其值:
private void OnUpdateText(object parameter)
{
var textProperty = textSearch.GetType().GetProperty("Text");
var value = textProperty.GetValue(parameter, null) as string;
// process the Text value as you need to.
}
更简洁的解决方案是将 WinRT 特定代码放入通过接口抽象的单独程序集中。您将首先在 PCL 库中创建一个接口:
public interface IUiService
{
string GetTextBoxText(object textBox);
}
并在构造函数中修改视图模型以接受此接口:
public ViewModel(IUiService uiService)
{
_uiService = uiService;
}
在命令处理程序中,您将使用界面中的方法:
private void OnUpdateText(object parameter)
{
var value = _uiService.GetTextBoxText(parameter);
// process the Text value as you need to.
}
您将在 WinRT 库中实现该接口:
public class UiService : IUiService
{
public string GetTextBoxText(object textBox)
{
var typedTextBox = textBox as TextBox;
return typedTextBox.text;
}
}
在应用程序中,您引用此库并将实现传递给视图模型:
var viewModel = new ViewModel(new UiService);
我最喜欢的方法是不同的:我会创建一个Behavior
公开的Text
属性,每次TextChanged
触发事件时都会自动更新:
public class TextChangedBehavior : Behavior<TextBox>
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(TextChangedBehavior),
new PropertyMetadata(null));
public string Text
{
get { return (string) GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.TextChanged += OnTextChanged;
Text = AssociatedObject.Text;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.TextChanged -= OnTextChanged;
}
private void OnTextChanged(object sender, TextChangedEventArgs textChangedEventArgs)
{
Text = AssociatedObject.Text;
}
}
现在,我可以将一个TextValue
属性绑定到此行为并对其在属性设置器中的更改做出反应,正如在这个长答案的开头所建议的那样:
<TextBox>
<i:Interaction.Behaviors>
<b:TextChangedBehavior Text="{Binding TextValue, Mode=TwoWay}" />
</i:Interaction.Behaviors>
</TextBox>