Two-way binding in WPF - wpf

I cannot get a two-way bind in WPF to work.
I have a string property in my app's main window that is bound to a TextBox (I set the mode to "TwoWay").
The only time that the value of the TextBox will update is when the window initializes.
When I type into the TextBox, the underlying string properties value does not change.
When the string property's value is changed by an external source (an event on Click, for example, that just resets the TextBox's value), the change doesn't propagate up to the TextBox.
What are the steps that I must implement to get two-way binding to work properly in even this almost trivial example?

Most probably you're trying to bind to a .net CLR property instead of a WPF dependencyProperty (which provides Change Notification in addition to some other things).
For normal CLR property, you'd need to implement INotifyPropertyChanged and force update on the textbox in the event handler for PropertyChanged.
So make your object with the property implement this interface, raise the event in the property setter. (So now we have property change notification)
Make sure the object is set as the DataContext property of the UI element/control
This threw me off too when I started learning about WPF data binding.
Update: Well OP, it would have been a waste of time if i was barking up the wrong tree.. anyways now since you had to dig a bit.. you'll remember it for a long time. Here's the code snippet to round off this answer. Also found that updating the textbox happens automatically as soon as I tab-out.. You only need to manually subscribe to the event and update the UI if your datacontext object is not the one implementing INotifyPropertyChanged.
MyWindow.xaml
<Window x:Class="DataBinding.MyWindow" ...
Title="MyWindow" Height="300" Width="300">
<StackPanel x:Name="TopLevelContainer">
<TextBox x:Name="txtValue" Background="AliceBlue" Text="{Binding Path=MyDotNetProperty}" />
<TextBlock TextWrapping="Wrap">We're twin blue boxes bound to the same property.</TextBlock>
<TextBox x:Name="txtValue2" Background="AliceBlue" Text="{Binding Path=MyDotNetProperty}" />
</StackPanel>
</Window>
MyWindow.xaml.cs
public partial class MyWindow : Window, INotifyPropertyChanged
{
public MyWindow()
{
InitializeComponent();
this.MyDotNetProperty = "Go ahead. Change my value.";
TopLevelContainer.DataContext = this;
}
private string m_sValue;
public string MyDotNetProperty
{
get { return m_sValue; }
set
{
m_sValue = value;
if (null != this.PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("MyDotNetProperty"));
}
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}

I feel the need to add some precision:
"Two ways" data binding is more than "One way" data binding.
"One way" data binding is a binding from a source to a dependency property. The source must implement INotifyPropertyChanged, in order to get change propagation from source to target.
To get the " 2 way" , so to get a propagation from Target to Source, it depends on the binding mode which you set on the Binding . If you don't set any BindingMode for your binding, the default Binding mode will be used, and this default mode is a characteristics for your target Dependency Property.
Example:
A Textbox bound to a string property, called "MyTextProperty".
In the code, you bind Textbox.Text DependencyProperty to "MyTextProperty" on object "MyObject"
--> "one way" binding : the setter of "My TextProperty" must raise an event Property Changed,and "MyObject" must implement INotifyPropertyChanged.
--> "2 ways data binding": in addition to what is needed for "One way", bindingMode must be set to "2 ways". In this special case, the Text DependencyProperty for Textbox does have "2 ways" as default mode, so there is nothing else to do !

We might need to see the code. Does your string property raise a PropertyChanged event? Or (even better) is it implemented as a DependencyProperty? If not, the bound TextBox won't know when the value changes.
As for typing into the TextBox and not seeing the property's value change, that may be because your TextBox isn't losing focus. By default, bound TextBoxes don't write their values back to the source property until focus leaves the control. Try tabbing out of it and seeing if the property value changes.

Make sure that the binding specifies two way and when the property has a change, it is immediately transmitted to the holding property.
<TextBox Text="{Binding TextBuffer,
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay}"/>
The above assures that the TextBox input control Text property binds to, then sends the changes back to the string property named TextBuffer in an immediate, PropertyChanged, and TwoWay fashion.

Related

How to apply Metadata from a ViewModel to the View in WPF with MVVM

I have a viewModel with properties like the following and a set of specific attributes used throughout the viewmodels.
public class MyViewModel : BaseModel
{
[StringLength(50), Required]
[SetLockedForExistingEntities]
public string FirstName { get ... set ... }
public bool IsInNewMode { get; }
}
Now I want to apply such metaData in a view in a consistent way. Like... If bound, set TextBox maxlength from the MaxLengthAttribute. If SetLockedForExistingEntitiesAttribute is set, disable the control in case viewModel is not in some 'New' Mode etc..
Is that doable/a good idea to do with a custom MarkupExtension that replaces "Binding" for VM Bindings? Or would it be better to use a Behavior (applied via attached property) which tries to apply anything it can from the bound ViewModel property to the control it is attached to?
Usage would be like
(A) Attached dependencyproperty that reads the binding from TextBox.Text and applies behaviors
<TextBox Text="{Binding Model.FirstName, ValidatesOnDataErrors=True}" "bb:MyBindingHelper.ApplyViewModelBehaviors="True" />
(B) Custom MarkupExtension that does all in one
<TextBox Text="{BindingWithModelBasedBehaviors Model.FirstName}" />
You could write a markup extension that gets the property from the datacontext and reads attributes.
That would be kind of complicated but you can get the property name of properties where the source changed event was raised.
That looks rather like validation to me.
You could implement inotifydataerrorinfo in a base viewmodel and write code there that validates properties using attributes.
That's how the code in this works:
https://gallery.technet.microsoft.com/scriptcenter/WPF-Entity-Framework-MVVM-78cdc204
That works by the view telling the viewmodel which property's value just passed to the viewmodel.
You can extend the method you use for raising property changed to pass the property name to the validation.
Or you could even do the check from a method called in the property setter before you set the value on a property and not set the value if the new one fails validation.
As a specific property fails validation in a particular way you could run an action.
The production code version of that app I linked also has a dictionary of predicates used as well as attributes. They could have code in them references and sets other viewmodel properties.

c# wpf Binding Command - access value of textbox from command

I want to call a simple Command which adds values from my GUI into the database.
My Command:
private ICommand addSpeechCommand;
public ICommand AddSpeechCommand
{
get
{
if (addSpeechCommand == null)
{
addSpeechCommand = new RelayCommand(param => AddSpeech());
}
return addSpeechCommand;
}
}
public void AddSpeech()
{
// TODO add
OrganizerBL.InsertSpeech(new Speech(maxSpeechId, currentSpeech.Title, currentSpeech.Summary, currentSpeech.Start, currentSpeech.End, currentSpeech.TrackId, currentSpeech.SpeakerId, currentSpeech.ConferenceId, currentSpeech.Room));
this.LoadSpeeches();
}
-- this commented out row shows how i dealt with it when a row of my datagrid was selected. but i want it to work without a currentSpeech
My XAML:
<Label x:Name ="lblTitle" Content="Title"/>
<TextBox x:Name="txtTitle" Text="{Binding CurrentSpeech.Title, Mode=TwoWay}" Margin="2,144,0,0" Height="20" VerticalAlignment="Top"/>
and other textboxes...
I really don't know how to access the values of the textboxes from the command to call my insertSpeech method...
Sorry for my english :)
UPDATE:
I'm getting a nullreference exception because my currentSpeech is null.
Is there a way to solve this without the currentSpeech?
The reason you get the NullReferenceException is probably because it's instanced in the property itself. When you cerate a binding, it's created to the property as it is at that stage. And you bind to the property when it's NULL. IT's actually created inside the property, but the Binding will never know that.
First of all, I would remove all logic from properties.
I would also implement the INotifyPropertyChanged to the class, and call the PropertyChanged in the property's "set". This means that the UI will know of any changes to the porperty.
Then I would create a depencency property for the property, if it's used in any Binding ot XAML.
Last, I would instance the command in the class's constructor.
Logic don't (in my book) belong to properties.
How to do
1. Bind TextBox.Text to View model property
2. Use View model property in Command Handler.
In your case, You have binded TextBox.Text to CurrentSpeech.Title, but using this.Title.
In you command, change this.Title to currentSpeech.Title

Set UpdateSourceTrigger to Explicit in ShowDialog (WPF MVVM)

I saw this example - Binding.UpdateSourceTrigger Property
in the example the UpdateSourceTrigger set to Explicit and then in the view code he call to UpdateSource of the TextBox name.
But if i use the MVVM dp i dont want to have names to my controls and source properties are in the VM and not in the view so what is the right way to bind controls to VM properties and set the UpdateSourceTrigger to explicit?
I want to do this because in my case its ShowDialog window and I want that the source will update only if the user click "ok"
Thanks in advance!
If you are using MVVM truely then your OK button click must be handled by some Command. This command must be coming from your ViewModel. The Expliticly bound properties must be coming from your ViewModel again. So whats stopping you.
Do not use Explicit binding but use OneWay binding.
In you button, bind a command and bind a command parameter to the OneWay bound Dependency property.
In your Command's Execute handler (which must be some method from your ViewModel), change the ViewModel's property with the parameter coming.
Raise the NotifyPropertyChanged for that property from your ViewModel.
E.g.
Assume I need to update a TextBox's Text back into my model on OK button click.
So for that I have a EmployeeViewModel class that has EmployeeName property in it. The property is has a getter and a setter. The setter raises property changed notification. The view model also has another property of type ICommand named SaveNameCommand that return a command for me to execute.
EmployeeViewModel is the data context type of my view. Myview has a TextBox (named as x:Name="EmployeeNameTxBx") OneWay bound to the EmployeeName and a Button as OK. I bind Button.Command property to EmployeeViewModel.SaveNameCommand property and Button.CommandParameter is bound to EmployeeNameTxBx.Text property.
<StackPanel>
<TextBox x:Name="EmployeeNameTxBx"
Text="{Binding EmployeeName, Mode=OneWay}" />
<Button Content="OK"
Command="{Binding SaveNameCommand}"
CommandParameter="{Bidning Text, ElementName=EmployeeNameTxBx}" />
</StackPanel>
Inside my EmployeeViewModel I have OnSaveNameCommandExecute(object param) method to execute my SaveNameCommand.
In this perform this code...
var text = (string)param;
this.EmployeeName = text;
This way ONLY OK button click, updates the TextBox's text back into EmployeeName property of the model.
EDIT
Looking at your comments below, I see that you are trying to implement Validation on a UI. Now this changes things a little bit.
IDataErrorInfo and related validation works ONLY IF your input controls (such as TextBoxes) are TwoWay bound. Yes thats how it is intended. So now you may ask "Does this mean the whole concept of NOT ALLOWING invalid data to pass to model is futile in MVVM if we use IDataErrorInfo"?
Not actually!
See MVVM does not enforce a rule that ONLY valid data should come back. It accept invalid data and that is how IDataErrorInfo works and raises error notfications. The point is ViewModel is a mere softcopy of your View so it can be dirty. What it should make sure is that this dirtiness is not committed to your external interfaces such as services or data base.
Such invalid data flow should be restricted by the ViewModel by testing the invalid data. And that data will come if we have TwoWay binding enabled. So considering that you are implementing IDataErrorInfo then you need to have TwoWay bindings which is perfectly allowed in MVVM.
Approach 1:
What if I wan to explicitly validate certain items on the UI on button click?
For this use a delayed validation trick. In your ViewModel have a flag called isValidating. Set it false by default.
In your IDataErrorInfo.this property skip the validation by checking isValidating flag...
string IDataErrorInfo.this[string columnName]
{
get
{
if (!isValidating) return string.Empty;
string result = string.Empty;
bool value = false;
if (columnName == "EmployeeName")
{
if (string.IsNullOrEmpty(AccountType))
{
result = "EmployeeName cannot be empty!";
value = true;
}
}
return result;
}
}
Then in your OK command executed handler, check employee name and then raise property change notification events for the same property ...
private void OnSaveNameCommandExecute(object param)
{
isValidating = true;
this.NotifyPropertyChanged("EmployeeName");
isValidating = false;
}
This triggers the validation ONLY when you click OK. Remember that EmployeeName will HAVE to contain invalid data for the validation to work.
Approach 2:
What if I want to explicitly update bindings without TwoWay mode in MVVM?
Then you will have to use Attached Behavior. The behavior will attach to the OK button and will accept list of all items that need their bindings refreshed.
<Button Content="OK">
<local:SpecialBindingBehavior.DependentControls>
<MultiBinding Converter="{StaticResource ListMaker}">
<Binding ElementName="EmployeeNameTxBx" />
<Binding ElementName="EmployeeSalaryTxBx" />
....
<MultiBinding>
</local:SpecialBindingBehavior.DependentControls>
</Button>
The ListMaker is a IMultiValueConverter that simply converts values into a list...
Convert(object[] values, ...)
{
return values.ToList();
}
In your SpecialBindingBehavior have a DependentControls property changed handler...
private static void OnDependentControlsChanged(
DependencyObject depObj,
DependencyPropertyChangedEventArgs e)
{
var button = sender as Button;
if (button != null && e.NewValue is IList)
{
button.Click
+= new RoutedEventHandler(
(object s, RoutedEventArgs args) =>
{
foreach(var element in (IList)e.NewValue)
{
var bndExp
= ((TextBox)element).GetBindingExpression(
((TextBox)element).Textproperty);
bndExp.UpdateSource();
}
});
}
}
But I will still suggest you use my previous pure MVVM based **Approach 1.
This is an old question but still I want to provide an alternative approach for other users who stumble upon this question...
In my viewmodels, I do not expose the model properties directly in the get/set Property methods. I use internal variables for all the properties. Then I bind all the properties two-way. So I can do all the validation as "usual" because only the internal variables are changed. In the view model constructor, I have the model object as parameter and I set the internal variables to the values of my model. Now when I click on the "Save" Button (-> Save Command fires in my view model fires) and there are no errors, I set all the properties of my model to the values of the correspondng internal variable. If I click on the "Canel/Undo"-Button (-> Cancel-Command in my view model fires), I set the internal variables to the values of my untouched model (using the setters of the view model properties so that NotifyPropertyChanged is called and the view shows the changes=old values).
Yet another approach would be to implement Memento-Support in the model, so before you start editing you call a function in the model to save the current values, and if you cancel editing you call a function to restore those values...that way you would have the undo/cancel support everywhere an not just in one view model...
I've implemented both methods in different projects and both work fine, it depends on the requirements of the project...

DataBinding Text Property of UserControl to ObservableCollection does not update

I have created a UserControl, which is to display a converted string value based on the contents of a bound ObservableCollection. Everything works when the application loads; my IValueConverter is called and produces the correct string result, which is displayed correctly in my UserControl. However if the ObservableCollection contents change, my control is not updated.
Also, before I created this control, I had the same behaviour, but binding the Content property of a regular Button control, and this also worked correctly and updated as expected.
Any ideas what I am missing to get the same thing with my UserControl?
The control property looks like;
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MyUserControl));
public string Text
{
get { return GetValue(TextProperty) as string; }
set { SetValue(TextProperty, value);
}
The relevant section in the UserControl XAML (which displays the converted string value) is;
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type Controls:MyUserControl}}, Path=Text}" />
And the control is created in a separate Window like so;
<CoreControls:MyUserControl
Name="myControl"
Text="{Binding Path=ObservableCollectionInstance, Converter={StaticResource MyValueConverter}, Mode=OneWay}" />
I would use ElementName instead of RelativeSource in your binding, since you have named your user control. Also, you are trying to bind a collection to a <Textbox>. a <Textbox> is designed to display a single item. this is probably why its not working. ObservableCollection fires CollectionChanged events, not PropertyChanged. Even if it did respond, you are still going to have problems because ObservableCollection does not notify when an item contained in it has property changes--only when items are added/removed etc (think, the collection itself changes). If this is the behavior you want, you are going to have to write some code.
EDIT
after your comments, it sounds to me like even though you set it to OneWay binding mode, its acting like OneTime binding mode.
I would try this to help you debug it:
add this xmlns:
xmlns:diagnostics="clr-namespace:System.Diagnostics;assembly=WindowsBase"
and then, in your binding add this:
diagnostics:PresentationTraceSources.TraceLevel=High
here is an article on debugging bindings.
the other thing you could do is set breakpoints in your converter. see if its actually updating when you add/remove things to your collection. I would be willing to bet that its bc the ObservableCollection is NOT firing PropertyChanged events and that the initial update occurs because its not based on an update event.
ObservableCollection notifies only in case if items get added or removed. It is used to observe a collection. They are more suited for content controls. Read about it here. You are talking about observing a property, which needs INotifyPropertyChanged. Posting more code might help, like how are you changing the value of the collection.
Thanks for the tips guys.
I managed to work out a solution; I can handle the CollectionChanged event on the ObservableCollection and then explicitly update the target with something like;
BindingExpression exp = myControl.GetBindingExpression(MyUserControl.TextProperty);
if (null != exp) exp.UpdateTarget();
As noted, most likely, binding on the Text property is only listening to PropertyChanged events, not NotifyCollectionChanged events, but this solution does the trick.

Why won't my WPF bound Visibility property update?

I have a textblock in my XAML where the Visibility is bound to a property in my viewmodel. When the window first loads, the value from the viewmodel determines the visibility correctly (I tried manually overriding the backing store variable value and it works great, hiding the control as I need). However, when I change the property value the visibility doesn't change.
Here's the XAML for the control:
<TextBlock Text="Click the button" Style="{StaticResource Message}" Visibility="{Binding NoResultsMessageVisibility}" />
The "NoResultsMessageVisibility" property that I bind to is this:
public Visibility NoResultsMessageVisibility
{
get { return _noResultsMessageVisibility; }
set
{
_noResultsMessageVisibility = value;
NotifyPropertyChanged("NoResultsMessageVisibility");
}
}
NotifyPropertyChange raises a PropertyChanged event for the provided name using standard INotifyPropertyChanged.
Can anyone spot my mistake?
EDIT
In response to the comments / answer so far.
The program is super simple so there's no parallelism / multithreading used.
The DataContext is set only once when the window loads, using:
new MainWindow { DataContext = new MainWindowViewModel() }.ShowDialog();
The binding does seem to work when first loaded. I've noticed as well that a textbox I have bound to a property isn't updating when I change the property. However, the property is definitely updating when I change the textbox as the value is used as the basis for a command that's bound to a button. As the text changes, the button is enabled and disabled correctly and when I click it the value from the property is correct. Again, if I set a value against the backing store variable, this shows in the textbox when the window first loads.
Don't see anything wrong with this, is it possible that the DataContext gets changed, so the binding breaks? (You only specify the path, so it's relative to the current DataContext)
Solved it. I'm a dozy dork :)
I have copied some code from another class and for some reason I'd added the PropertyChanged event to my viewmodel's interface, rather than implementing INotifyPropertyChanged on the interface. D'Oh!

Resources