Recently, I realized that MVVM pattern is so useful for Silverlight application and studying how to adopt it into my project.
BTW, how to hook up the textbox\'s text
Here's the easiest way. Bind your text box to the property on the view model, as you described above. Then, simply add a code-behind (yes, I'm talking code-behind with MVVM, it's not the end of the world) event to the Text Box. Add a TextChanged event, then simply refresh the binding.
Altogether, you'll have something like this for a view model:
public class MyViewModel
{
private string _myText;
public string MyText
{
get { return _myText; }
set
{
_myText = value;
RaisePropertyChanged("MyText"); // this needs to be implemented
// now do whatever grid refresh/etc
}
}
}
In your XAML, you'll have this:
<TextBox Text="{Binding MyText,Mode=TwoWay}" TextChanged="TextBox_TextChanged"/>
Finally, in the code behind, simply do this:
public void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var binding = ((TextBox)sender).GetBindingExpression(TextBox.TextProperty);
binding.UpdateSource();
}
That will cause your property to update anytime the text changes. }
For the sake of conversation, lets say that you do need to hook up some arbitrary event to a Command rather than bind directly to a property on the ViewModel (due to a lack of support in the control or framework, defect, etc.) This can be done in the codebehind. Contrary to some misconceptions, MVVM does not preclude codebehind. It is just important to remember that the logic in the codebehind should not cross layers - it should be relate directly to the UI and the specific UI technology being used. (Note however, that putting 95% of your work in the markup file may make it a little unintutitive to have some functionality in the codebehind, so a comment or two in the markup about this one-off codebehind implementation may make things easier down the road for yourself or others.)
There are usually 2 parts to binding a command in codebehind. First, you have to respond to the event. Second, you (may) want to tie into the command's CanExecute property.
// Execute the command from the codebehind
private void HandleTheEvent(Object sender, EventArgs e)
{
var viewModel = DataContext as ViewModel;
if (viewModel != null)
{
var command = viewModel.SomeCommand;
command.Execute(null);
}
}
// Listen for the command's CanExecuteChanged event
// Remember to call this (and unhook events as well) whenever the ViewModel instance changes
private void ListenToCommandEvent()
{
var viewModel = DataContext as ViewModel;
if (viewModel != null)
{
var command = viewModel.SomeCommand;
command.CanExecuteChanged += (o, e) => EnableOrDisableControl(command.CanExecute(null));
}
}
Why not just bind the Text
property to a property on your view model? That way you get notified when it has changed, and also get the new value:
public string MyData
{
get { return this.myData; }
set
{
if (this.myData != value)
{
this.myData = value;
this.OnPropertyChanged(() => this.MyData);
}
}
}
XAML:
<TextBox Text="{Binding MyData}"/>
I had the same question. Then I find this article.
http://deanchalk.com/wpf-mvvm-property-changed-command-behavior/
behavior
(behavior
recive value to changend event and command to changend)behavior
in your WPFBehavior
your command to a ValueChanged
Simply use
<TextBox Text="{Binding MyText,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
You should use a Behavior to execute the Command:
public class CommandBehavior : TriggerAction<FrameworkElement>
{
public static readonly DependencyProperty CommandBindingProperty = DependencyProperty.Register(
"CommandBinding",
typeof(string),
typeof(CommandBehavior),
null);
public string CommandBinding
{
get { return (string)GetValue(CommandBindingProperty); }
set { SetValue(CommandBindingProperty, value); }
}
private ICommand _action;
protected override void OnAttached()
{
DataContextChangedHandler.Bind(AssociatedObject, _ProcessCommand);
}
private void _ProcessCommand(FrameworkElement obj)
{
if (AssociatedObject != null)
{
var dataContext = AssociatedObject.DataContext;
if (dataContext != null)
{
var property = dataContext.GetType().GetProperty(CommandBinding);
if (property != null)
{
var value = property.GetValue(dataContext, null);
if (value != null && value is ICommand)
{
_action = value as ICommand;
if (AssociatedObject is Control)
{
var associatedControl = AssociatedObject as Control;
associatedControl.IsEnabled = _action.CanExecute(null);
_action.CanExecuteChanged +=
(o, e) => associatedControl.IsEnabled = _action.CanExecute(null);
}
}
}
}
}
}
protected override void Invoke(object parameter)
{
if (_action != null && _action.CanExecute(parameter))
{
_action.Execute(parameter);
}
}
}
public static class DataContextChangedHandler
{
private const string INTERNAL_CONTEXT = "InternalDataContext";
private const string CONTEXT_CHANGED = "DataContextChanged";
public static readonly DependencyProperty InternalDataContextProperty =
DependencyProperty.Register(INTERNAL_CONTEXT,
typeof(Object),
typeof(FrameworkElement),
new PropertyMetadata(_DataContextChanged));
public static readonly DependencyProperty DataContextChangedProperty =
DependencyProperty.Register(CONTEXT_CHANGED,
typeof(Action<FrameworkElement>),
typeof(FrameworkElement),
null);
private static void _DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var control = (FrameworkElement)sender;
var handler = (Action<FrameworkElement>)control.GetValue(DataContextChangedProperty);
if (handler != null)
{
handler(control);
}
}
public static void Bind(FrameworkElement control, Action<FrameworkElement> dataContextChanged)
{
control.SetBinding(InternalDataContextProperty, new Binding());
control.SetValue(DataContextChangedProperty, dataContextChanged);
}
}
Now you can "Bind" your command in xaml:
<TextBox Text="{Binding SearchText, Mode=TwoWay}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<utils:CommandBehavior CommandBinding="SearchCommand" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
If you need you can extend this Behavior with extra properties, for example if you need the sender or the DataContext of a different element..
Kind regards, Tamás
(I have found this on a blog post, but I can not remember for it's address)