传播 DependencyProperty 默认值

Propagate DependencyProperty default value

我有一个带有 ViewModel 的 WPF 用户控件:

<MyUserControl ...>
    <Grid Name="UxRootContainer">
        <Grid.DataContext>
            <MyViewModel/>
        </Grid.DataContext>
    </Grid>
</MyUserControl>

此 UserControl 具有必须传播到 ViewModel 的 DependencyProperty:

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);
}

这工作正常,除了我们不会在 OnDurationChanged 事件中收到默认值。

我知道我可以在构造函数中自己调用此方法,将默认持续时间放入常量,但是:

任何关于如何将默认值传播到 ViewModel 的好建议,前提是默认值是最后使用的值(不是其他值集)。

我决定写一个答案(以防万一我错了:)。

在给定的情况下,您正在创建 自定义控件,这意味着在其他视图中是一个简单的控件。两点:

  • 不创建 ViewModel;
  • 不设置此控件的DataContext

ViewModel 在这种情况下没有任何作用:没有可从中抽象出的基础模型,没有任何东西(不是 View,也不是 ViewModel)将被重用。没有 ViewModel 存在默认值已经是依赖的默认值 属性 = 问题已解决。

至于 DataContext:如果您尝试在列表中使用此控件将项目 属性 绑定到它,您将始终必须通过父容器 DataContext 引用它(因为控件有它的覆盖和绑定 "{Binding Text}" 将不会引用该项目 Text 属性,而是引用控件 ViewModel 文本 属性,您将必须执行类似 "{Binding DataContext.Text, RelativeSource={RelativeSource FindAncestor, AncestorType=Grid}}").这在任何设计中都是不对的。

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

让 class 遵守 INotifyPropertyChanged,然后让 VM(或任何需要信息的东西)订阅控件外的事件。这样,当它发生变化(或设置值)时,消费者会收到通知。

查看此问题的 SO 答案 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).

您描述了需要编码以确定是否使用默认值的业务逻辑。似乎没有上述逻辑,您的请求就无法完成。


正在发生的是竞争条件。控件无法绑定以通知对不存在的内容的更改...首先创建控件,然后设置默认值,然后您的控件绑定到它,当然所有这些都是在运行时进行的。该操作在设计时就位,但这并不影响进程本身。