C# 传播依赖属性默认值

C# 传播依赖属性默认值,c#,wpf,mvvm,user-controls,C#,Wpf,Mvvm,User Controls,我有一个带有ViewModel的WPF用户控件: <MyUserControl ...> <Grid Name="UxRootContainer"> <Grid.DataContext> <MyViewModel/> </Grid.DataContext> </Grid> </MyUserControl> public static rea

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

<MyUserControl ...>
    <Grid Name="UxRootContainer">
        <Grid.DataContext>
            <MyViewModel/>
        </Grid.DataContext>
    </Grid>
</MyUserControl>
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事件中收到默认值

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

我必须为每个DependencyProperty创建一个常量 我将不得不调用它,即使最后我没有使用默认值
关于如何将默认值传播到ViewModel的任何好的建议,仅当默认值是endnot other value set中使用的值时。我决定写一个答案,以便在我出错的情况下查看向下投票:

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

不要创建ViewModel; 不要设置此控件的DataContext。 在这种情况下,ViewModel没有任何用途:没有可从中提取的基础模型,没有任何东西不是视图,也不会重用ViewModel。如果不存在ViewModel,则默认值已经是dependence property=问题已解决的默认值


至于DataContext:如果您试图在列表中使用此控件将item属性绑定到它,您将始终必须通过父容器DataContext引用它,因为控件已被重写,绑定{binding Text}将不会引用该item文本属性,而是引用控件ViewModel文本属性,您必须执行以下操作{Binding DataContext.Text,RelativeSource={RelativeSource FindAncestor,AncestorType=Grid}。这在任何设计中都是不对的。

我决定写一个答案,以便在我出错的情况下查看投票结果:

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

不要创建ViewModel; 不要设置此控件的DataContext。 在这种情况下,ViewModel不起任何作用:没有可从中提取的基础模型,没有任何对象不是视图,也不会重用ViewModel。如果没有ViewModel存在,则默认值已经是dependence property=问题已解决的默认值

至于DataContext:如果您试图在列表中使用此控件将item属性绑定到它,您将始终必须通过父容器DataContext引用它,因为控件已被重写,绑定{binding Text}将不会引用该item文本属性,而是引用控件ViewModel文本属性,您必须执行以下操作{Binding DataContext.Text,RelativeSource={RelativeSource FindAncestor,AncestorType=Grid}。这在任何设计中都是不对的

除非我们不会在OnDurationChanged事件中收到默认值

让类遵守INotifyPropertyChanged,然后让VM或任何需要信息的东西订阅控件之外的事件。这样,当它更改或设置值时,会通知使用者

看到这个了吗?这个问题的答案是什么

关于如何将默认值传播到ViewModel的任何好的建议,仅当默认值是在endnot other值集中使用的值时

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

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

除非我们不会在OnDurationChanged事件中收到默认值

让类遵守INotifyPropertyChanged,然后让VM或任何需要信息的东西订阅控件之外的事件。这样,当它更改或设置值时,会通知使用者

看到这个了吗?这个问题的答案是什么

关于如何将默认值传播到ViewModel的任何好的建议,仅当默认值是在endnot other值集中使用的值时

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


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

它不是真正的MVVM。在视图中创建ViewModel通常是个坏主意,对于主窗口来说可能还可以。关键是UserControl通常是ViewModel的datatemplates,这意味着您将在某个地方拥有ViewModel,但您的控件不能用作datatemplate,因为它设置了它的DataCo
ntext显式。接下来,您应该反其道而行之:无论是默认值还是新值,视图都会从ViewModel中获取值。这可以在加载事件中完成。您将100%获得此事件。@Sinatr这有点离题,但很抱歉,我不同意,MVVM可以是视图优先或视图模型优先。对于视图,我们使用Prism ViewModelLocator获取ViewModel。在本例中,这不是一个真正的视图,而是一个可以在应用程序中的许多不同上下文中使用的UserControl。难道你不认为如果你的文本块必须自己找到它必须显示的字段会很奇怪吗@Sinatr我有一个ViewModel,它只包含我的内部可绑定属性。我同意这个名称可能不是最好的,但它包含的逻辑与真实的ViewModelcommands、INotifyPropertyChange实现相同,…您不认为如果您的TextBlock必须自己找到它必须显示的字段会很奇怪吗?不。绑定已经使View元素不知道任何事情。TextBlock不知道最后会显示什么。如果创建了两个ViewModel实例,则它们在同一视图中显示的值可能不同。因此,不要从视图中获取值。视图应该从ViewModel中获取值。@Sinatr Nope。正如我所说,这个用户控件将在许多不同的视图中使用,这些视图已经有了自己的ViewModel。更具体地说,我的UserControl只有一个Point集合和一个Duration,它是要显示的点数。它不知道这些点来自哪里,这将完全取决于在哪个视图中使用这些点。和TextBlock一样,它不是真正的MVVM。在视图中创建ViewModel通常是一个坏主意,对于主窗口来说可能还可以。关键是UserControl通常是ViewModel的datatemplates这意味着您将在某个地方拥有ViewModel,但您的控件不能用作datatemplate,因为它显式地设置了其DataContext。接下来,您应该反其道而行之:无论是默认值还是新值,视图都会从ViewModel中获取值。这可以在加载事件中完成。您将100%获得此事件。@Sinatr这有点离题,但很抱歉,我不同意,MVVM可以是视图优先或视图模型优先。对于视图,我们使用Prism ViewModelLocator获取ViewModel。在本例中,这不是一个真正的视图,而是一个可以在应用程序中的许多不同上下文中使用的UserControl。难道你不认为如果你的文本块必须自己找到它必须显示的字段会很奇怪吗@Sinatr我有一个ViewModel,它只包含我的内部可绑定属性。我同意这个名称可能不是最好的,但它包含的逻辑与真实的ViewModelcommands、INotifyPropertyChange实现相同,…您不认为如果您的TextBlock必须自己找到它必须显示的字段会很奇怪吗?不。绑定已经使View元素不知道任何事情。TextBlock不知道最后会显示什么。如果创建了两个ViewModel实例,则它们在同一视图中显示的值可能不同。因此,不要从视图中获取值。视图应该从ViewModel中获取值。@Sinatr Nope。正如我所说,这个用户控件将在许多不同的视图中使用,这些视图已经有了自己的ViewModel。更具体地说,我的UserControl只有一个Point集合和一个Duration,它是要显示的点数。它不知道这些点来自哪里,这将完全取决于在哪个视图中使用这些点。与TextBlock相同。我的问题不是我的ViewModel值没有传播到UserControl,而是相反,我的DependencyProperty的默认值没有提供给UserControl模型。它实际上是一个UserControl,而不仅仅是一个视图。@J4N发生的是一个争用条件。无法绑定控件以通知对不存在的内容所做的更改…首先创建控件,然后设置默认值,然后您的控件绑定到它,当然所有这些都是在运行时进行的。操作在设计时设置到位,但这并不影响流程本身。我理解,但什么是好的解决方法?有没有一种方法可以向DP表明我们希望在它指定默认值之前注册?我的问题不是我的ViewModel值没有传播到UserControl,而是相反,我的DependencyProperty的默认值没有提供给UserControl模型它实际上是一个UserControl,不仅仅是一个视图。@J4N正在发生的是一个竞争条件。无法绑定控件以通知对不存在的内容所做的更改…首先创建控件,然后设置默认值,然后您的控件绑定到它,当然所有这些都是在运行时进行的。操作在设计时设置到位,但这并不影响流程本身。我理解,但什么是好的解决方法?在DP分配默认值之前,是否有方法向DP表明我们希望注册?您可以
理解正确,但这就是为什么我有一个ViewModel或Model:1。根据我从dependency属性得到的值,我必须计算其他几个值。因此,拥有一个模型允许我从dependency属性设置值,并直接计算将绑定到userControl中的所有其他值。2.它允许我轻松地单元测试背后的所有逻辑:如果用户将某些内容绑定到DependencyProperty,我可以检查我的计算字段中是否有预期的结果。关于DataContext,我没有在控件上设置它,而是在它的网格上。另外,我同意,这是在设计一个CustomControl上,但我没有这样解释它,因为我认为CustomControl没有XAML,只有适用于它的样式,不是吗?我不认为自定义控件需要视图模型,只需要页面+1您理解正确,但这就是为什么我要使用ViewModel或Model:1。根据我从dependency属性得到的值,我必须计算其他几个值。因此,拥有一个模型允许我从dependency属性设置值,并直接计算将绑定到userControl中的所有其他值。2.它允许我轻松地单元测试背后的所有逻辑:如果用户将某些内容绑定到DependencyProperty,我可以检查我的计算字段中是否有预期的结果。关于DataContext,我没有在控件上设置它,而是在它的网格上。另外,我同意,这是在设计一个CustomControl上,但我没有这样解释它,因为我认为CustomControl没有XAML,只有适用于它的样式,不是吗?我不认为自定义控件需要视图模型,只需要页面+1.