Recently I was working on an MVVM demo for an article where I wanted live filtering based on the contents of a text box. The problem with this is that the TextBox does not have a Command property, and thus I’d need to use code-behind to proxy its text changed event to the ViewModel. And I did not want to do that. This was purely whimsical behavior on my part since MVVM does not mandate this at all, though a lot of people do recommend it. Apparently, you can get around this by referencing an Expression Blend DLL, which has support for interaction triggers that you can forward to a command object, thereby avoiding any code-behind. I didn’t want to reference an Expression Blend DLL, not for a simple demo app anyway and so I was forced to work around it by adding an attached behavior to the TextBox that could take a command object. This was a bit of an over-kill of course, and in a real world app I’d simply do it in code-behind. Most likely something like :
ViewModel viewModel = this.DataContext as ViewModel;
. . .
private void TextChanged(. . .)
{
viewModel.SomeCommand(. . .) ;
}
That’d be in my view’s code-behind, and while some purists may not be too happy, I think it’s definitely simpler than referencing Expression Blend! Here’s what a typical Xaml code snippet using this behavior would be like:
<TextBox x:Name="searchTextBox" Width="165"
HorizontalAlignment="Left" Margin="3,0,0,0"
Text="{Binding SearchText}"
local:TextChangedBehavior.TextChanged="{Binding FilterCommand}" />
Instead of handling the TextChanged event, I handle it via the attached behavior and route it to the FilterCommand command object in the ViewModel. Here’s the code for the attached behavior:
internal class TextChangedBehavior
{
public static DependencyProperty TextChangedCommandProperty
= DependencyProperty.RegisterAttached(
"TextChanged",
typeof(ICommand),
typeof(TextChangedBehavior),
new FrameworkPropertyMetadata(
null,
new PropertyChangedCallback(
TextChangedBehavior.TextChangedChanged)));
public static void SetTextChanged(TextBox target, ICommand value)
{
target.SetValue(TextChangedBehavior.TextChangedCommandProperty,
value);
}
public static ICommand GetTextChanged(TextBox target)
{
return (ICommand)target.GetValue(TextChangedCommandProperty);
}
private static void TextChangedChanged(
DependencyObject target, DependencyPropertyChangedEventArgs e)
{
TextBox element = target as TextBox;
if (element != null)
{
if (e.NewValue != null)
{
element.TextChanged += Element_TextChanged;
}
else
{
element.TextChanged -= Element_TextChanged;
}
}
}
static void Element_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
BindingExpression bindingExpression = textBox.GetBindingExpression(
TextBox.TextProperty);
if (bindingExpression != null)
{
bindingExpression.UpdateSource();
}
ICommand command = GetTextChanged(textBox);
if (command.CanExecute(null))
{
command.Execute(null);
}
}
}
Nothing complicated there, just a basic attached behavior implementation.
where is “FilterCommand”??
can you please complete the example