I've an WPF UserControl with a ViewModel:
<MyUserControl ...>
<Grid Name="UxRootContainer">
<Grid.DataContext>
<MyViewModel/>
</Grid.DataContext>
</Grid>
</MyUserControl>
This UserControl has a DependencyProperty, that has to be propagated to the ViewModel:
public static readonly DependencyProperty DurationProperty =
DependencyProperty.Register( "Duration", typeof(TimeSpan),
typeof(MyUserControl), new FrameworkPropertyMetadata(TimeSpan.FromHour(1), OnDurationChanged ));
public TimeSpan Duration
{
get { return (TimeSpan)GetValue(DurationProperty); }
set { SetValue(DurationProperty, value); }
}
private static void OnDurationChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
MyUserControl control = source as MyUserControl;
TimeSpan duration = (TimeSpan)e.NewValue;
control.UxRootContainer.SetDuration(duration);
}
This is working fine, except we will not receive the default value in the OnDurationChanged
event.
I know I could call to this method myself in the constructor, put the default duration into a constant, but:
- I've to create a constant for every DependencyProperty
- I will have to call this even if at the end I don't use the default value
Any nice proposal on how to propagate the default value to the ViewModel, only if the default value is the one used at the end(not other value set).
I decide to write an answer (to see down-votes in case I am wrong :).
In given case you are creating custom control, which is meant to be a simple control inside other Views. Two points:
DataContext
of this control.ViewModel doesn't serve any purpose in this case: there is no underlying Model to abstract from, nothing (not View, nor ViewModel) will be reused. Without ViewModel existence default value is already default value of dependence property = problem solved.
As for
DataContext
: if you try to use this control inside list to bind item property to it you will always have to refer to it via parent containerDataContext
(because control has its overriden and binding"{Binding Text}"
will not refer to that itemText
property, but to a control ViewModel Text property, and you will have to do something like"{Binding DataContext.Text, RelativeSource={RelativeSource FindAncestor, AncestorType=Grid}}"
). That's not right in any design.Have the class adhere to
INotifyPropertyChanged
and then have the VM (or whatever needs the info) subscribe to the event off of the control. That way when it changes (or it the value is set) the consumer is notified.See this SO answer to this question How To Raise Property Changed events on a Dependency Property?
You describe business logic which would need to be coded to determine whether the default value is being used. It doesn't appear that without said logic, your request could be accomplished.
What is happening is a race condition. A control cannot be bound for notify changes to something that does not exist...the creation of the control happens first, the default value is set second, then your control binds to it, all at runtime of course. The operation is set in place at design time, but that does not affect the process per-se.