Is WPF UpdateSourceTrigger always needed to notify model of changes in collection? - wpf

Question: Was UpdateSourceTrigger always necessary to have properties update the source? I seem to recall that Mode=TwoWay was enough a long time ago. But now, I have to UpdateSourceTrigger=PropertyChanged? Maybe I'm losing it...
<DataGridTemplateColumn Header="Hub" Width="SizeToHeader">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="XHub" IsChecked="{Binding Hub, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
When I omit that part of the binding the model is never called when a value in the ObservableCollection changes.

Both Mode and UpdateSourceTrigger properties of the binding depends on the dependency property you are binding to. According to MSDN:
The default is Default, which returns the default UpdateSourceTrigger value of the target dependency property. However, the default value for most dependency properties is PropertyChanged, while the Text property has a default value of LostFocus.
A programmatic way to determine the default UpdateSourceTrigger value of a dependency property is to get the property metadata of the property using GetMetadata and then check the value of the DefaultUpdateSourceTrigger property.
So in your case you are binding to IsChecked that is defined in ToogleButton class as following:
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof (bool?), typeof (ToggleButton),
(PropertyMetadata) new FrameworkPropertyMetadata(
BooleanBoxes.FalseBox,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
new PropertyChangedCallback(ToggleButton.OnIsCheckedChanged)));
So BindsTwoWayByDefault makes Mode=TwoWayredundant, while
querying metadata:
var def = ((FrameworkPropertyMetadata)CheckBox.IsCheckedProperty.GetMetadata(typeof(CheckBox))).DefaultUpdateSourceTrigger;
results in PropertyChanged, that makes the UpdateSourceTrigger=PropertyChanged part also redundant.
Edit: about this part:
When I omit that part of the binding the model is never called when a value in the ObservableCollection changes.
Sincerely, I cannot explain myself the behavior that you've noticed (but it's quite easy to reproduce). I was expecting the binding to update viewmodel according to dependency property default UpdateSourceTrigger. But I've noticed that the ViewModel is updated not even on focus lost, but when you either move focus to next row or press enter. So the explanation that Colin Eberhardt gives in his blog seems the best I can find. Obviously this behavior is strongly related to DataGrid and if you would have the same checkbox outside of the grid, then the ViewModel would update as expected without explicit UpdateSourceTrigger set to PropertyChanged.
When you bind to a DataTable, you are actually binding to your DataTable's DefaultView, which is of type DataView. As a result, each row of your table will be bound to a DataRowView. If you look at the documentation for DataRowView you will find that it implements the IEditableObject interface which is the significant factor here. This interface allows you to perform transactional changes to your object, i.e. you can change the object's properties within a 'transaction', then commit then all in a single atomic action. By default, when you bind to a DataGrid this occurs when the user finishes editing a row, either by moving focus or hitting Enter.

Was UpdateSourceTrigger always necessary to have properties update the source?
No. The UpdateSourceTrigger property of a binding specifies what triggers the update of a source property whereas the Mode property lets you control the direction of the data flow. These are two different things.
Even if you don't explicitly set the UpdateSourceTrigger property your Hub source property will indeed still be set, but not until the you step out of the cell of the DataGrid.

Related

MVVM not binding after initial value has been entered

I'm facing an odd issue with my WPF (MVVM) project.
I have a few controls which bind to the properties in the ViewModel. INotifyPropertyChanged is configured, everything (initially works). I type in some values into my controls and I click a button. I can see, by stepping through the code, all the property values are what they should be. So far, it is text book.
Now I notice the issue. After I click the button, some logic is performed, such as saving these values to a database. I can then edit the control values and then save to the database again. The properties at this point to do not update.
Binding clearly works, because the output shows no binding errors and when I click the Save button, the properties are correct. However, after I click the save button, and then change the property values, the properties are not updatdd. I cannot fathom why this is the case.
As a trial, I added the PropertyChanged to the update source trigger and this seems to fix the issue, however, I've never had to do this before. Any ideas what could be wrong?
I don't believe the answer is 2 way binding (I am happy to be wrong) because it binds!
<TextBox Text="{Binding DataSource, UpdateSourceTrigger=PropertyChanged}" Grid.Row ="1" Grid.Column="2" />
Where as normally I would use
<TextBox Text="{Binding DataSource}" Grid.Row ="1" Grid.Column="2" />
UpdateSourceTrigger property determines the time, when the binding has to be updated. The default value for this property is LostFocus. So by default, after you type something and move the focus out, the binding will update. If you set the property value to PropertyChanged, binding will update immediately once you entered the value in text box.
http://msdn.microsoft.com/en-us/library/system.windows.data.binding.updatesourcetrigger(v=vs.110).aspx
In your case, the binding is updated on button click, since focus transferred to Button from textbox. Once the UpdateSourceTrigger set to PropertyChanged, the binding will update on every text change.

IDataErrorInfo validation in DataGrid with CellTemplate and CellEditingTemplate

I'm having trouble concerning validation in DataGrid. I'm using IDataErrorInfo validation in model classes.
The problem is in editable DataGrid with separate CellTemplate and CellEditingTemplate (Note is a not-null property - validation returns Error if null or empty):
<!-- some other validated columns -->
<DataGridTemplateColumn Header="Note">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Name="textBoxNote" Text="{Binding Note, ValidatesOnDataErrors=True}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Note}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
On Save button, I check MyObject.Error validation property and if not null I show a MessageBox. The problem is that when changing the first column (not the Note one) to a valid value and then clicking Save button, the .Error property is null - this is expected (although unwanted) behavior because the binding with ValidatesOnDataError on Note property never happened (the TextBox control never even existed!). But if I set the ValidatesOnDataErrors to true on TextBlock then I get the undesired validation on every object shown in a DataGrid (say from a database) that I'm not concerned about; validation can also take a lot of time in this case...
What would be the proper way to handle this issue? I would like to keep validation in model classes (the object should know about whether it's valid or not). Is there any way to force validation of a row-bound object in codebehind (Save button event)? Or should I somehow initialize .Error on object construction? Any other ideas?
EDIT:
how could I put the whole row (all cells) into edit mode (CellEditingTemplate)? Then, all of the controls would be loaded and databound, this also means validated...
Thanks to all,
DB
Ok, I managed to revalidate the IDataErrorInfo object - kind of forced IDataErrorInfo validation. Otherwise I could add new object to the DataGrid, but properties (except of the edited ones) never got validated.
In the superclass of all of my model objects (that extends IDataErrorInfo) I added this method:
public virtual void Revalidate() // never needed to override though
{
Type type = this.GetType();
// "touch" all of the properties of the object - this calls the indexer that checks
// if property is valid and sets the object's Error property
foreach (PropertyInfo propertyInfo in type.GetProperties())
{
var indexerProperty = this[propertyInfo.Name];
}
}
Now when the user adds new object to DataGrid I manually call myNewObject.Revalidate() method to set the Error property which I check before saving the object to the database. Maybe this is not the best possible solution, but it works for me very painlessly.
Thanks and regards,
DB

UpdateSourceTrigger's Default vs PropertyChanged?

<TextBlock Name="txtName" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
"Name" is the property of an object which is bound with the TextBlock at runtime. I have implemented INotifyPropertyChanged so the code is working fine. My question is: if I replace PropertyChanged to Default will it still work? What's the difference between them?
According to MSDN, the UpdateSourceTrigger's default value is PropertyChanged for most properties and LostFocus for the TextBox.Text property.
In your case, you're probably binding to a property for which the default is already set to PropertyChanged, so you won't see any difference.
PropertyChanged is telling Binding that whenever you receive PropertyChanged notification for that property, update it's value on destination.
For some controls, like TextBox, using Default, it only updates binding destination when for example, it loses focus. When you set UpdateTrigger='PropertyChanged' on it, it will update binding destination while you are typing.
For TextBox Control
When we use UpdateSourceTrigger=Default means when source object loses focus, value in target will get updated.
When we use UpdateSourcetrigger=PropertyChanged means when source object text changes(even when we type a single character) immediately than change will be reflected in target.
Above case is for TextBox control as default for most control is PropertyChanged only

Setting a property on the ViewModel from the View in WPF

I have a dependency property on my ViewModel which is the DataContext for my View. The ViewModel has no reference to the View. The property on the ViewModel is going to reference a control on the view, but I need to be able to set this property in XAML.
How is this possible? One thought I had was to develop a custom control which has a Property property and a Value property, so you could do something like this in the View to set the property on the ViewModel:
<PropertySetter Property="{Binding MyViewModelDependencyProperty}" Value="{Binding ElementName=aControlOnMyView" />
Before I went down this route, I wanted to check if there was any other approach I could take?
Thanks for the detailed reply Ray, but if I give you a bit more detail about the problem I'm trying to solve, you might get a better idea of why I mentioned the approach I did.
Basically, what I'm trying to do is set the focus to a textbox when the user hits a button. I've written an attached property which you can attach to the Button control, specify what the trigger event is (in this case the 'Click' event), and then what control to focus on. This works really nicely, and keeps everything in XAML.
However, I now have a use case where the focus should be set to an arbitrary text box from the click event on a button which is part of a toolbar. This toolbar is itself a user control which is sitting inside another user control, which is inside another user control! This toolbar needs to be reusable across various different forms, and each time, the control to set focus on after you click the button will be different per form.
That's why I had the idea of making the focus control (i.e. a textbox) a property on the view model itself (on my ViewModel base to be precise), and have the ViewModel base code (which the toolbar is bound to), set the focus to the control when the button is clicked (and the e.g. Add/Edit method is called on the ViewModel base).
In unit test land, the control to focus on property will be null, so it's .Focus() method just won't be called. So I can't see an issue there. My problem is then how you set the focus control property from XAML, which is why I had the PropertySetter idea.
I don't like the fact that the ViewModel has any reference to controls sitting on the view, but I can't see another way to achieve what I need. What if the logic that dictates whether to set focus to the control is quite complex? This would sit in the ViewModel surely? Therefore, is there any harm in the ViewModel having this UIElement property? It still knows nothing about the specific View it is bound to, it just knows that there is a control which it needs to set focus to when some action happens on the ViewModel.
My first reaction (and it's a strong one) is so say "Don't do that!" By giving your view model a reference to a part of your UI you are breaking the encapsulation that makes view models so powerful and useful.
For example, what if you want to unit test your view model or serialize it to disk? In each case the piece of your UI will not be present, because there will be no view at all. Your tests will miss coverage and your reconstitution will be incomplete.
If your view model actually needs references to UI objects and there is no better way to architect it, the best solution is to have the view model itself construct those controls it requires a reference to. Then your view can incorporate that control as the Content of a ContentPresenter via binding and provide a Style to configure the control, including a ControlTemplate to provide its content. Thusly:
public class MyViewModel
{
public ListBox SpecialControl { get; set; }
public MyViewModel()
{
SpecialControl = new ListBox();
}
}
and
<DataTemplate TargetType="{x:Type local:MyViewModel}">
<DataTemplate.Resources>
<Style TargetType="ListBox" ... />
</DataTemplate.Resources>
...
<ContentPresenter Content="{Binding SpecialControl}" />
</DataTemplate>
Other possibilities are:
Have the view model actually derive from the Control class, then override OnApplyTemplate() and use GetTemplateChild to find a template item whose name starts with "PART_"
Implement an attached property that takes a property name, finds that property in the DataContext, and sets it to the DependencyObject to which the property is attached.
Implement your PropertySetter idea
My option #2 would look like this:
<DataTemplate TargetType="{x:Type MyViewModel}">
...
<TextBox local:PropertyHelper.SetViewModelToThis="SpecialControl" />
...
</DataTemplate>
The code in the SetViewModelToThis PropertyChangedCallback would get the view model from the DataContext, reflect on it to find the "SpecialControl" property, then set it to the TextBox. Note that the implementation of SetViewModelToThis must take into account the possiblity that DataContext is not set right away, and that it maybe changed requiring the old setting to be removed and a new one made.
First of all, the DataContext of the control should be the ViewModel object and not a property of it. Second, when you TwoWay bind a property of ViewModel to your control, changes in the control's value will update (in your case, 'set') the value of ViewModel's property.

Why do I get Inconsistent Binding results

I have a control template with a toggle button. This ToggleButton has it's IsChecked property one way bound to a dependancy property. If i set the dependancy property explicitly the binding works.
The problem is that after I interact with the toggle button in the UI, the bindings don't update the IsChecked property if I set the dependency property explicitly.
I do have a work arround using TwoWay binding which works fine. My question is, why does it behave this way? Am I missing something? Is there a bug in the binding mechanism of Silverlight?
EDIT TO INCLUDE SNIPPET:
The binding in the ControlTemplate looks something like (could be replaced with TemplateBinding)
<ToggleButton x:Name="PlayPause" Grid.Column="0"
IsChecked="{Binding Paused, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="Center"
Width="50" Height="50"/>
The explicit setting of the dependency property is the fairly bog standard:
myComponent.Paused = true;
WPF deletes one way bindings when the target property (IsChecked in this case) is modified. Silverlight used to keep the binding when IsChecked was modified. If Paused was later set, this value would overwrite IsChecked as well.
According to you, it seems Silverlight reverted to WPF behavior. Oh well. Personally, I consider modifying a binded property a bug. If the properties are not meant to be in sync commanding may be a better solution.
You should use TwoWay binding
Make sure that the object that contains your Paused property supports INotifyPropertyChanged.
Make sure that the setter for Paused triggers the PropertyChanged event

Resources