绑定 ViewModel 属性 的值更新失败

Value Update of Bound ViewModel Property Fails

我正在尝试将自定义控件 属性 绑定到其视图模型的 属性 并失败。

我已经为设置 StartDate 定义了依赖项 属性 并更新了 PropertyChangeCallback 方法

public static readonly DependencyProperty StartDateProperty = 
           DependencyProperty.Register(StartDatePropertyName,
                                       typeof(DateTime),
                                       typeof(CustomDateTimeControl),
                                       new PropertyMetadata(DateTime.Now.AddYears(-7), 
                                                            OnStartDatePropertyChanged));

private static void OnStartDatePropertyChanged(DependencyObject d, 
                                               DependencyPropertyChangedEventArgs e)
{ 
       DateTime dtNewValue = (DateTime)e.NewValue;
        if (dtNewValue != DateTime.MinValue)
        {
            DateTimeControl dtCtrl = d as DateTimeControl;
            dtCtrl.StartDate = (DateTime)e.NewValue;
            dtCtrl.CoerceValue(StartDateProperty);
        }
 }

StartDate属性 绑定到其 ViewModel 的开始日期,因为 VM 需要执行一些操作,然后用于定义自定义控件的下一个可用视图。

<Setter Property="StartDate" 
        Value="{Binding StartDate, 
                        Mode=OneWayToSource, 
                        UpdateSourceTrigger=PropertyChanged}" />

其中定义的 DependencyProperty 也是从主窗口视图设置的

<CustomDateTimeLib:CustomDateTimeControl  StartDate="01/01/2000 00:00:00"  />

绑定仅使用依赖项的默认值 属性 更新视图模型中的 属性,但不使用上面在 MainView 中设置的值,即使依赖项 属性 正在使用 MainView 中的值进行更新。

ViewModelLocatorclass

    public class ViewModelLocator
    {
    static ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            SimpleIoc.Default.Register<CalendarViewModel>();
        }

        /// <summary>
        /// Gets the Main property.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
            "CA1822:MarkMembersAsStatic",
            Justification = "This non-static member is needed for data binding purposes.")]
        public CustomDateTimeLib:CustomDateTimeControl CalendarVM
        {
            get
            {
                return ServiceLocator.Current.GetInstance<CustomDateTimeControl>();
            }
        }
}

App.Xaml

<Application x:Class="MvvmCustomTestApp.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:vm="clr-namespace:CustomDateTimeLib.ViewModel"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:ignore="http://www.galasoft.ch/ignore"
         StartupUri="MainWindow.xaml"
         mc:Ignorable="d ignore">

<Application.Resources>
    <!--Global View Model Locator-->
    <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
</Application.Resources>

CustomDateTimeControl 中的 ApplyTemplate 覆盖方法class

 public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        CalendarViewModel vm = (CalendarViewModel)this.DataContext;
        vm.StartDate = this.StartDate;

}

还为 StartDate

定义了一个 属性 更改回调方法

如果我理解正确的话,您有一个自定义 DP,您想要绑定到 ViewModel 属性,但是您还想从视图中为此 属性 设置默认值。

这不是 MVVM 或您使用绑定时的理想设置。 MVVM应该有所有的逻辑,包括像"default value for X"这样的东西在ViewModel层,而View层只是用来为用户提供与ViewModel(数据)层交互的可视化方式。

所以你的解决方案是:

  1. 在您的 ViewModel 中设置默认值
  2. 在您的依赖项中提供处理 属性 如果值 == DateTime.Min,请使用不同的默认值
  3. 如果您真的想让视图提供默认值,请使用转换器(DateTime)或 FallbackValue(DateTime?
  4. 使用第二个 DP 来定义应该使用的默认值
  5. 向控件添加 Loaded 事件处理程序以读取 DataContext 并设置默认值

如果您使用 MVVM,选项 1 是最佳解决方案,因为自定义默认值之类的东西应该在 ViewModel 而不是 View 中设置。

如果此默认值特定于此 UserControl,则选项 2 是最佳选择,并且在任何时候使用此控件时都是相同的。

选项 3、4 和 5 适用于无论出于何种原因您确实坚持从 View 层设置默认值的情况。具体用哪一个要看你的情况。


假设您使用 #1,我希望您的最终 XAML 看起来像这样:

<!-- assumes DataContext is of type DateTimeCtrlVM via inheritance or direct binding -->
<CustomDateTimeLib:CustomDateTimeControl  StartDate="{Binding StartDate}"  />

就是这样。

上面 XAML 代码中的 <Setter> 实际上导致了以下情况的发生:

  • 创建的控件默认值为 1/1/2000
  • 属性绑定OneWayToSource到VM属性,也就是说数据只会从Target(Control)流向Source(ViewModel),所以1/1/的值2000 正在持久化到您的 VM

因此摆脱将 属性 绑定为 OneWayToSource 的 Setter,使用上面显示的 XAML 绑定,并在 ViewModel 中设置默认值,它应该可以工作。