Binding to object not property - wpf

While using a converter, I want to bind to the entire object and send it to the converter to use, rather than binding to each of the individual properties, so I can do this:
Order thisOrder = (Order)values[0];
(note, its a multibinding, other values are being sent in besides the thisOrder object. Now the Order class implements the INotifyPropertyChanged interface, and raised the appropriate events when any of its properties are changed. The converter needs access to multiple properties of the Order, although it actually only needs to rerun on changes to one of the properties. Is there any way to either bind to the entire object so that if any of the properties change, the converter is evaluated again, or bind to only one property, but have the rest of the object also available as a parameter for the converter to work on?
Example Details:
Order has following fields:
ID
MarketPrice
Name
When MarketPrice changes, the converter needs to recalculate based on
ID and MarketPrice.
Presently, the binding is applied on the DataRecordCellArea of a xamDataGrid, as follows:
<MultiBinding Converter="{StaticResource MarketPositionToBrushConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="Record.DataItem"/>
...
</MultiBinding>

As far as i know there currently is no clean solution to this. I would suggest you add two bindings, one to the Order object so you have access to all the values and another to the MarketPrice so that the binding updates if that property changes. You then have access to the object and all properties via the first binding and you get the respective updates.
The is another method which is quite a hack, you can subscribe to PropertyChanged in the class owning the Order instance and then fire a change notification for the Order instance property whenever the MarketPrice in the instance changes. I would avoid doing this but it would allow you to get rid of the second binding.

Related

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

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.

Cannot access property bound to DataGridTextColumn in another element such as Foreground

I have a simple DataGrid with its ItemSource bound to an ObservableCollection<Issue> where Issue is just a class object containing various properties. I have a handful of DataGridTextColumn items bound to the properties of the Issue object and all works as expected.
Binding="{Binding Path=DueDate, StringFormat=dd-MMM-yyyy}" Header="Due Date"
Now, I want to set the color of the Foreground depending upon if that date has passed so you would expect that you can do this:
Foreground="{Binding Path=DueDate, Converter={StaticResource DateHasPastColorConverter}}"
where
DateHasPastColorConverter
returns one of two colors depending upon if the date being past has past.
My problem is that I can access the Issue.DueDate property in the main column binding but it is not available for the Foreground or any other property. The only properties available are those from the actual view model itself.
How do I access the properties of the row to to this and why are they not available? It's like only the binding member recognises the Issue properties and all other bdinginds only recognise the view models properties.

Two way Multiconverter or Converter with property

I need to convert a two part value into a string and back again for example:
{Value = 12.0, Units = DimensionUnits.Inches}
Converts to
"12 in"
This is pretty simple using a multivalue converter to convert from source but becomes impossible to convert back if the user doesn't provide the unit type in the string so a Multivalue converter doesn't look like the solution I need.
A direct converter parameter won't work because the unit type needs to be bound so I researched how to create a bindable parameter. Creating a bindable parameter is actually pretty easy - deriving from DependencyObject - but then you have the problem of your converter not living in the visual tree - and thus not being able to bind to anything - to which there are 2 solutions:
http://tomlev2.wordpress.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/
http://shujaatsiddiqi.blogspot.com/2011/02/wpf-binding-converter-parameter.html
The first method derives your Converter from Freezable instead of DependencyObject to allow DependencyProperties. This works and allows you to bind within the Whatever.Resources section but it has extremely odd behavior like only listening to the binding the first time it is used in your entire application.
The second method doesn't seem to work at all. The dependency property is never updated when the source changes.
<pf:BindingReflector Target="{Binding Source={StaticResource DistanceConverter}, Path=Units, Mode=TwoWay}"
Source="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=g:TestWindow, AncestorLevel=1}, Path=Units, Mode=TwoWay}"/>
Does anyone know of a solution to this problem or is this a big limitation of WPF?
Personally, I would actually suggest rethinking this a bit, and doing either:
Keep the final string one-way using a IMultiValueConverter, and have this be input as two separate items. This seems like a reasonable approach, as the units appear to be an enum with a specific set of options. A combo box for units and textbox for amount seems appropriate, and the total display can be done with a one-way converter.
Handle this conversion explicitly in the ViewModel. This has the advantage of allowing much better validation handling, which is probably going to be required as entering two separate values (amount + units) in one control is likely to not validate correctly. By moving the logic directly into your ViewModel instead of binding to the properties, you can correctly handle errors in a clean way.

WPF nested data binding works randomly

I have a simple form with several textboxes. I am using a ViewModel as DataContext set from code. In the ViewModel I have a property with name Metadata. This propery changes as the user loads new "Metadata" into the form.
The textbox XAML looks like this:
<TextBox Text="{Binding Path=Metadata.ContractMetadata.Utstrackning.VastligasteLongitud, Mode=TwoWay}" />
In the ViewModel when the Metadata property is changed I run OnProperyChanged("Metadata"). Metadata implements the INotifyPropertyChanged interface, the other classes in the hierarchy does not. Sometimes it works, other times it does not. I have tried running:
OnProperyChanged("Metadata")
OnProperyChanged("Metadata.ContractMetadata");
OnProperyChanged("Metadata.ContractMetadata.Utstrackning");
OnProperyChanged("Metadata.ContractMetadata.Utstrackning.VastligasteLongitud");
...with no luck.
ProperyChanged for nested properties is not done the way you are doing it!
Each nested level instance must raise a property changed notification itself.
e.g. Metadata instance should raise "ContractMetadata" property
ContractMetadata instance should raise "Utstrackning" property
and Utstrackning instance should raise "VastligasteLongitud" property.
Did you try NotifyPropertyChanged("...") ?

WPF Binding Converters

I am currently stuck at a dead end with the following situation:
I have a List<Category> collection with each Category containing an Id, Name and a List<string[]> property called Subcategory (where array contains the Name and Id of that subcagtegory - I didn't feel like creating another class).
I have a TreeView with HierarchicalDataTemplate bound properly to categories and subcategories. I also have check boxes next to the tree node and I bind IsChecked to a MultiBinding of the Id of the sub/category and the an overall list of sub/categories that should be checked-off.
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource IsCategoryChecked}">
<Binding Path="Id"/>
<Binding Path="myDataSet.Tables[Categories]/cat_subcat"/>
</MultiBinding>
</CheckBox.IsChecked>
The converter I use simply checks if the name of the sub/category I provide is part of the list (both are values I'm binding to). This works fine and overall I am quite happy with the result.
The problem comes with converting the check/uncheck back. When user changes the IsChecked value I must either add the Id of the sub/category to the list of all checked-off categories or remove that Id from it. Unfortunately when I've attempted to implement the ConvertBack() method for IMultiValueConverter, I only have access to the ACTUAL value that changed (the true or false value of IsChecked) and have NO access to the Id of the sub/category that THAT SPECIFIC CHECKBOX is bound to.
SOLUTIONS I'VE TRIED but failed miserably:
Saving the sub/category name when I do the initial conversion is not an option since I'm using the same Converter for ALL the sub/categories and thus I'd only save the last sub/category I've tried to convert.
Using a MouseUp/KeyUp event is useless because (for whatever reason) they fail to be triggered (a bug?).
I am contemplating to ignore the ConvertBack() and use Checked/Unchecked events instead but feel like that is a rather "dirty" solution because I might just as well ignore the bindings altogether! Is this the only way to go? I've ran out of options but still hope for a "good" solution, if it's out there!
You have a couple of options, and I've used both in production:
Overload Checked/Unchecked, as you've said, which gives you the most control over the situation
Add an IsCategoryChecked property to your strongly typed table in the code behind which handles the changes required behind the scenes, and you bind to the IsCategoryChecked property rather than using the converter
The second one works fairly well thanks to the partial classing with strongly typed datasets. However, it still isn't "seamless".

Resources