I want to write a ViewModel that always knows the current state of some read-only dependency properties from the View.
Specifically, my GUI contains a FlowDocumentPageViewer, which displays one page at a time from a FlowDocument. FlowDocumentPageViewer exposes two read-only dependency properties called CanGoToPreviousPage and CanGoToNextPage. I want my ViewModel to always know the values of these two View properties.
I figured I could do this with a OneWayToSource databinding:
<FlowDocumentPageViewer
CanGoToNextPage="{Binding NextPageAvailable, Mode=OneWayToSource}" ...>
If this was allowed, it would be perfect: whenever the FlowDocumentPageViewer's CanGoToNextPage property changed, the new value would get pushed down into the ViewModel's NextPageAvailable property, which is exactly what I want.
Unfortunately, this doesn't compile: I get an error saying 'CanGoToPreviousPage' property is read-only and cannot be set from markup. Apparently read-only properties don't support any kind of databinding, not even databinding that's read-only with respect to that property.
I could make my ViewModel's properties be DependencyProperties, and make a OneWay binding going the other way, but I'm not crazy about the separation-of-concerns violation (ViewModel would need a reference to the View, which MVVM databinding is supposed to avoid).
FlowDocumentPageViewer doesn't expose a CanGoToNextPageChanged event, and I don't know of any good way to get change notifications from a DependencyProperty, short of creating another DependencyProperty to bind it to, which seems like overkill here.
How can I keep my ViewModel informed of changes to the view's read-only properties?
Best Answer
Yes, I've done this in the past with the
ActualWidth
andActualHeight
properties, both of which are read-only. I created an attached behavior that hasObservedWidth
andObservedHeight
attached properties. It also has anObserve
property that is used to do the initial hook-up. Usage looks like this:So the view model has
Width
andHeight
properties that are always in sync with theObservedWidth
andObservedHeight
attached properties. TheObserve
property simply attaches to theSizeChanged
event of theFrameworkElement
. In the handle, it updates itsObservedWidth
andObservedHeight
properties. Ergo, theWidth
andHeight
of the view model is always in sync with theActualWidth
andActualHeight
of theUserControl
.Perhaps not the perfect solution (I agree - read-only DPs should support
OneWayToSource
bindings), but it works and it upholds the MVVM pattern. Obviously, theObservedWidth
andObservedHeight
DPs are not read-only.UPDATE: here's code that implements the functionality described above: