C# 用户控件DependencyPropertyChanged事件触发,但绑定的viewmodel未更新。。。什么';发生什么事了?
我最近偶然发现了WPFs DatePicker控件。基本上,当您:C# 用户控件DependencyPropertyChanged事件触发,但绑定的viewmodel未更新。。。什么';发生什么事了?,c#,wpf,datepicker,dependency-properties,C#,Wpf,Datepicker,Dependency Properties,我最近偶然发现了WPFs DatePicker控件。基本上,当您: 通过样式设置DatePickers控件模板 将IsEnabled绑定到ViewModel上的属性 运行程序并启用日期选择器 下拉日历弹出窗口 禁用日期选择器,然后重新启用它 再次下拉日历弹出窗口 我知道这是一个非常特殊的场景,但在我的应用程序中,这种情况经常发生。一个完全由控件组成的整个用户控件从禁用开始,用户选择一条记录(填写表单)、编辑、保存、重复。第二次运行时,从日期选择器中选择日历会使其崩溃 我将DatePicker控件
public partial class DisableableDatePicker : UserControl
{
public static readonly DependencyProperty SelectedDateProperty = DependencyProperty.Register("SelectedDate", typeof(DateTime?), typeof(DisableableDatePicker), new PropertyMetadata(DateChanged));
private static void DateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine(((DateTime)e.NewValue).ToString("yyyy-MM-dd"));
}
public DateTime? SelectedDate
{
get { return (DateTime?)GetValue(SelectedDateProperty); }
set { SetValue(SelectedDateProperty, value); }
}
public static readonly DependencyProperty DisplayDateEndProperty = DependencyProperty.Register("DisplayDateEnd", typeof(DateTime?), typeof(DisableableDatePicker));
public DateTime? DisplayDateEnd
{
get { return (DateTime?)GetValue(DisplayDateEndProperty); }
set { SetValue(DisplayDateEndProperty, value); }
}
public static readonly DependencyProperty DisplayDateStartProperty = DependencyProperty.Register("DisplayDateStart", typeof(DateTime?), typeof(DisableableDatePicker));
public DateTime? DisplayDateStart
{
get { return (DateTime?)GetValue(DisplayDateStartProperty); }
set { SetValue(DisplayDateStartProperty, value); }
}
public static readonly DependencyProperty BlackoutDatesProperty = DependencyProperty.Register("BlackoutDates", typeof(List<DateTime>), typeof(DisableableDatePicker));
public List<DateTime> BlackoutDates
{
get { return (List<DateTime>)GetValue(BlackoutDatesProperty); }
set { SetValue(BlackoutDatesProperty, value); }
}
public DisableableDatePicker()
{
InitializeComponent();
ClearValue(SelectedDateProperty);
ClearValue(DisplayDateEndProperty);
ClearValue(DisplayDateStartProperty);
ClearValue(BlackoutDatesProperty);
ClearValue(IsEnabledProperty);
}
}
因此,当我在ViewModels getter和setter中放置断点时,getter被正确地命中,但setter从未被调用。我觉得这很奇怪,因为我的dependency属性报告它看到了新的值
这个谜题的最后一个部分是我如何绑定停电日期。我希望能够绑定到一个简单的日期列表。为了方便地执行此操作,我在附加属性中添加了以下内容:
public static readonly DependencyProperty BlackoutDatesProperty =
DependencyProperty.RegisterAttached("BlackoutDates", typeof(List<DateTime>),
typeof(DependencyPropertyHost),
new FrameworkPropertyMetadata(null, OnBlackoutDatesChanged));
public static List<DateTime> GetBlackoutDates(DependencyObject d)
{
return (List<DateTime>)d.GetValue(BlackoutDatesProperty);
}
public static void SetBlackoutDates(DependencyObject d, List<DateTime> value)
{
d.SetValue(BlackoutDatesProperty, value);
}
private static void OnBlackoutDatesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DatePicker datePicker = d as DatePicker;
if (e.NewValue != null && datePicker != null)
{
List<DateTime> blackoutDates = (List<DateTime>)e.NewValue;
var toRemove = datePicker.BlackoutDates.Select
(x => x.Start).Except
(blackoutDates.Select(y => y)).ToList();
foreach (DateTime date in toRemove)
{
datePicker.BlackoutDates.Remove(datePicker.BlackoutDates.Single(x => x.Start == date));
}
foreach (DateTime date in blackoutDates)
{
if (!datePicker.BlackoutDates.Contains(date) &&
date >= datePicker.DisplayDateStart &&
date <= datePicker.DisplayDateEnd &&
datePicker.SelectedDate != date)
{
datePicker.BlackoutDates.Add(new CalendarDateRange(date));
}
}
}
}
公共静态只读从属属性BlackoutDatesProperty=
从属财产。注册(“停电日期”,类型(列表),
类型(DependencyPropertyHost),
新的FrameworkPropertyMetadata(null,OnBlackoutDatesChanged));
公共静态列表GetBlackoutDates(DependencyObject d)
{
返回(列表)d.GetValue(BlackoutDatesProperty);
}
公共静态void SetBlackoutDates(DependencyObject d,列表值)
{
d、 设置值(BlackoutDatesProperty,value);
}
BlackoutDateSchanged上的私有静态无效(DependencyObject d、DependencyPropertyChangedEventArgs e)
{
DatePicker DatePicker=d作为日期选择器;
if(e.NewValue!=null&&datePicker!=null)
{
列表停电日期=(列表)e.NewValue;
var toRemove=datePicker.BlackoutDates.Select
(x=>x.Start)。除了
(blackoutDates.Select(y=>y)).ToList();
foreach(删除中的日期时间日期)
{
删除(datePicker.BlackoutDates.Single(x=>x.Start==date));
}
foreach(blackoutDates中的日期时间日期)
{
如果(!datePicker.BlackoutDates.Contains(date)&&
日期>=datePicker.DisplayDateStart&&
日期默认情况下,依赖项属性是单向绑定的。这意味着它将从视图模型中读取,但不会写入。您有两个选项来修复此问题
选项1:在绑定中,将模式设置为双向
<view:DisableableDatePicker Margin="0,0,0,3"
SelectedDate="{Binding HighlightDate}"
DisplayDateStart="{Binding StartDate}"
DisplayDateEnd="{Binding EndDate}"
IsEnabled="{Binding IsEditing}"
BlackoutDates="{Binding BlackoutDates, Mode=TwoWay}" />
选项2:在代码隐藏中将默认绑定模式设置为双向
public static readonly DependencyProperty BlackoutDatesProperty =
DependencyProperty.RegisterAttached("BlackoutDates", typeof(List<DateTime>),
typeof(DependencyPropertyHost),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnBlackoutDatesChanged));
公共静态只读从属属性BlackoutDatesProperty=
从属财产。注册(“停电日期”,类型(列表),
类型(DependencyPropertyHost),
新的FrameworkPropertyMetadata(空,FrameworkPropertyMetadata选项.bindstwoway默认,OnBlackoutDatesChanged));
在这里,我想我已经掌握了WPF的诀窍……然后像依赖项属性的默认绑定模式这样简单的东西让我追了好几天。感谢您快速简单的回复!
public static readonly DependencyProperty BlackoutDatesProperty =
DependencyProperty.RegisterAttached("BlackoutDates", typeof(List<DateTime>),
typeof(DependencyPropertyHost),
new FrameworkPropertyMetadata(null, OnBlackoutDatesChanged));
public static List<DateTime> GetBlackoutDates(DependencyObject d)
{
return (List<DateTime>)d.GetValue(BlackoutDatesProperty);
}
public static void SetBlackoutDates(DependencyObject d, List<DateTime> value)
{
d.SetValue(BlackoutDatesProperty, value);
}
private static void OnBlackoutDatesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DatePicker datePicker = d as DatePicker;
if (e.NewValue != null && datePicker != null)
{
List<DateTime> blackoutDates = (List<DateTime>)e.NewValue;
var toRemove = datePicker.BlackoutDates.Select
(x => x.Start).Except
(blackoutDates.Select(y => y)).ToList();
foreach (DateTime date in toRemove)
{
datePicker.BlackoutDates.Remove(datePicker.BlackoutDates.Single(x => x.Start == date));
}
foreach (DateTime date in blackoutDates)
{
if (!datePicker.BlackoutDates.Contains(date) &&
date >= datePicker.DisplayDateStart &&
date <= datePicker.DisplayDateEnd &&
datePicker.SelectedDate != date)
{
datePicker.BlackoutDates.Add(new CalendarDateRange(date));
}
}
}
}
<view:DisableableDatePicker Margin="0,0,0,3"
SelectedDate="{Binding HighlightDate}"
DisplayDateStart="{Binding StartDate}"
DisplayDateEnd="{Binding EndDate}"
IsEnabled="{Binding IsEditing}"
BlackoutDates="{Binding BlackoutDates, Mode=TwoWay}" />
public static readonly DependencyProperty BlackoutDatesProperty =
DependencyProperty.RegisterAttached("BlackoutDates", typeof(List<DateTime>),
typeof(DependencyPropertyHost),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnBlackoutDatesChanged));