Propagate DependencyProperty default value

2019-07-31 06:49发布

问题:

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).

回答1:

except we will not receive the default value in the OnDurationChanged event

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?

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).

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.



回答2:

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:

  • do not create ViewModel;
  • do not set 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 container DataContext (because control has its overriden and binding "{Binding Text}" will not refer to that item Text 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.