Reputation: 4718
I have a windows 8 XAML/C# application using the MVVM pattern.
All my textboxes on the form have their text properties bound to properties on my MVVM class.
So, one of my textboxes looks like this:
<TextBox x:Name="textAddressLine1" Text="{Binding AddressLine1, Mode=TwoWay}"/>
And that property on the MVVM class looks like this:
private string addressLine1;
public string AddressLine1
{
get { return addressLine1; }
set
{
if (addressLine1 == value)
{
return;
}
addressLine1 = value;
RaisePropertyChanged("AddressLine1");
}
}
As I type into my textbox the MVVM class isn't updated. It only gets updated once the focus moves to a different control.
How can I update the MVVM class property when the text changes on my textbox?
Thanks in advance
Upvotes: 1
Views: 3489
Reputation: 24
I had the same issue, I found here: https://stackoverflow.com/a/11676076/4551080
<TextBox Text="{Binding Path=EmailAddress, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
So make sure to set
UpdateSourceTrigger=PropertyChangedDefault Value is LostFocus
Upvotes: 1
Reputation: 5716
Use explicit binding for textAddressLine1
<TextBox x:Name="textAddressLine1" Text="{Binding AddressLine1,UpdateSourceTrigger=Explicit, Mode=TwoWay}" TextChanged="textAddressLine1_Changed"/>
private void textAddressLine1_Changed(object sender, RoutedEventArgs e)
{
BindingExpression be = textAddressLine1.GetBindingExpression(TextBox.TextProperty);
be.UpdateTarget();
}
I didn't test the code but should work.
EDIT: I see it UpdateSourceTrigger is not exist for environtment
You can create a your viewmodel as instance and give it as datacontext by the way you can easily perform your viewmodel from your code-behind. For this type cases it saves the day!
public MyClassViewModel ViewModel {get;set}
ctor()
{
this.ViewModel=new MyClassViewModel();
this.DataContext=this.ViewModel;
InitializeComponets();
}
private void textAddressLine1_Changed(object sender, RoutedEventArgs e)
{
this.ViewModel.AddressLine1=textAddressLine1.Text;
}
I think you will not need two way by this way. OneWay is ok. Because you explicitly change VM.
Note:I coded on SO, didn't test again.Hope helps!
Upvotes: 1
Reputation: 26268
Use this workaround:
public class ExtendedTextBox : TextBox
{
public static readonly DependencyProperty CustomActionProperty =
DependencyProperty.Register(
"CustomAction",
typeof(Action<string>),
typeof(ExtendedTextBox),
new PropertyMetadata(null, OnPropertyChanged));
public Action<string> CustomAction
{
get
{
return (Action<string>)GetValue(CustomActionProperty);
}
set
{
SetValue(CustomActionProperty, value);
}
}
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if(e.NewValue != null)
(d as ExtendedTextBox).TextChanged += ExtendedTextBox_TextChanged;
else
(d as ExtendedTextBox).TextChanged -= ExtendedTextBox_TextChanged;
}
async static void ExtendedTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
await CoreWindow.GetForCurrentThread().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => (sender as ExtendedTextBox).CustomAction((sender as ExtendedTextBox).Text));
}
}
IN your model:
public Action<string> UpdateBindedViewModelProperty
{
get { return new Action<string>((value) => NewLabelName = value); }
}
and view:
<plmrfc:extendedtextbox customaction="{Binding UpdateBindedViewModelProperty, Mode=OneTime}" text="{Binding Path=NewLabelName, Mode=TwoWay}" width="200" x:name="Label_TextBox"></plmrfc:extendedtextbox>
There is also another way, which does not involve subclassing TextBox. Maybe you like this one more:
using System.Reflection;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace Flexman
{
public class TextBoxUpdateSourceBehaviour
{
private static PropertyInfo _boundProperty;
public static readonly DependencyProperty BindingSourceProperty =
DependencyProperty.RegisterAttached(
"BindingSource",
typeof(string),
typeof(TextBoxUpdateSourceBehaviour),
new PropertyMetadata(default(string), OnBindingChanged));
public static void SetBindingSource(TextBox element, string value)
{
element.SetValue(BindingSourceProperty, value);
}
public static string GetBindingSource(TextBox element)
{
return (string)element.GetValue(BindingSourceProperty);
}
private static void OnBindingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var txt = d as TextBox;
if (txt == null)
return;
txt.Loaded += OnLoaded;
txt.TextChanged += OnTextChanged;
}
static void OnLoaded(object sender, RoutedEventArgs e)
{
var txt = sender as TextBox;
if (txt == null)
return;
// Reflect the datacontext of the textbox to find the field to bind to.
var dataContextType = txt.DataContext.GetType();
_boundProperty = dataContextType.GetRuntimeProperty(GetBindingSource(txt));
// If you want the behaviour to handle your binding as well, uncomment the following.
//var binding = new Binding();
//binding.Mode = BindingMode.TwoWay;
//binding.Path = new PropertyPath(GetBindingSource(txt));
//binding.Source = txt.DataContext;
//BindingOperations.SetBinding(txt, TextBox.TextProperty, binding);
}
static void OnTextChanged(object sender, TextChangedEventArgs e)
{
var txt = sender as TextBox;
if (txt == null)
return;
if (_boundProperty.GetValue(txt.DataContext).Equals(txt.Text)) return;
_boundProperty.SetValue(txt.DataContext, txt.Text);
}
}
}
and view
<TextBox Text="{Binding Username}" Flexman:TextBoxUpdateSourceBehaviour.BindingSource="Username" />
This is the prettiest solution I know of. Others will be "non-generic" hacks. Good luck. I do agree that it's major downgrade from silverlight/WPF but hey, there are a lot of more horrible things in WinRT that are missing in WPF :)
Upvotes: 0