我最近偶然发现了使用WPF DatePicker控件的this issue。基本上,弹出窗口会导致异常:
我知道这是一个非常具体的场景,但它一直在我的应用程序中发生。完整控件的整个用户控件在禁用时启动,用户选择记录(填写表单),编辑,保存,重复。第二次运行它时,从DatePicker中选择日历会使其崩溃。
我已经将DatePicker的控件模板设置为一种样式,以便在禁用时显示禁用的文本框,以更好地适应其余禁用外观的外观(我对组合框执行了同样的操作)。
因此,知道这是我的问题的原因,我开始创建自己的用户控件来处理DatePicker的禁用外观。它只是包装一个Datepicker,提供一些依赖属性来绑定到我关心的DatePicker属性,并在禁用时用文本框交换用户控件的模板。这样我就可以得到我想要的外观,而无需查看DatePicker的错误。
我的问题是,当我在用户控件上选择一个新日期时,DependencyPropertyChanged事件使用新值触发,这些值永远不会进入我绑定到的外部视图模型。我每个方向都看着它,这对我没有任何意义:
后面的DisableableDatePicker代码
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);
}
}
我在DependencyPropertyChanged事件'DateChanged'中添加了在选择一个时将新值打印到控制台。这些新值在控制台中显示没问题。
DisableableDatePicker XAML
<UserControl x:Class="ALERT_Human_Resources.View.DisableableDatePicker"
...
Margin="0,0,0,3" x:Name="root" >
<UserControl.Resources>
<local:InverseBooleanConverter x:Key="DisableableDatePicker_InvertBoolean" />
<system:String x:Key="DateFormat">yyyy-MM-dd</system:String>
</UserControl.Resources>
<UserControl.Style>
<Style TargetType="{x:Type UserControl}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBox IsEnabled="False" Text="{Binding Path=SelectedDate, ElementName=root" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Style>
<DatePicker
local:DependencyPropertyHost.BlackoutDates="{Binding Path=BlackoutDates, ElementName=root}"
DisplayDateEnd="{Binding Path=DisplayDateEnd, ElementName=root}"
DisplayDateStart="{Binding Path=DisplayDateStart, ElementName=root}"
SelectedDate="{Binding Path=SelectedDate, ElementName=root, StringFormat={StaticResource DateFormat}}"
IsEnabled="{Binding Path=IsEnabled, ElementName=root}" />
</UserControl>
DisableableDatePicker用法
<view:DisableableDatePicker Margin="0,0,0,3"
SelectedDate="{Binding HighlightDate, UpdateSourceTrigger=PropertyChanged}"
DisplayDateStart="{Binding StartDate}"
DisplayDateEnd="{Binding EndDate}"
IsEnabled="{Binding IsEditing}"
BlackoutDates="{Binding BlackoutDates}" />
DisableableDatePicker绑定ViewModel属性
public DateTime HighlightDate
{
get
{
return SelectedDailyHighlight == null ? DateTime.MinValue : SelectedDailyHighlight.HighlightDate;
}
set
{
if (SelectedDailyHighlight != null && value != null && SelectedDailyHighlight.HighlightDate == value)
return;
SelectedDailyHighlight.HighlightDate = value;
OnPropertyChanged("HighlightDate");
}
}
因此,当我在ViewModels getter和setter中放置断点时,getter被正确命中,但是从不调用setter。我发现这很奇怪,因为我的依赖属性报告它看到了新的值。
这个难题的最后一部分是我如何约束Blackout日期。我希望能够绑定到一个简单的日期列表。为了方便起见,我在附属物中添加了以下内容:
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));
}
}
}
}
答案 0 :(得分:1)
默认情况下,依赖项属性绑定OneWay。这意味着它将从View Model中读取,但不会写入它。您有2个选项可以解决此问题。
选项1:在绑定中将模式设置为TwoWay。
<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:在后面的代码中将默认绑定模式设置为TwoWay。
public static readonly DependencyProperty BlackoutDatesProperty =
DependencyProperty.RegisterAttached("BlackoutDates", typeof(List<DateTime>),
typeof(DependencyPropertyHost),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnBlackoutDatesChanged));