PropertyChangedEventHandler都是null,但绑定只能工作一次,为什么?

时间:2014-06-23 13:46:28

标签: c# wpf data-binding mvvm user-controls

我做了一个小项目来了解MVVM。这是一个计算器,可以计算您下班回家的时间。

我使用两个文本框和一个标签作为简单的“TimePicker”创建了UserControl。这个Usercontrol有一个ViewModel(Mainwindow甚至有一个),它管理一个Timepicker的时间。它有三个属性:一个名为TimeValue的int,它只是小时和分钟的值,还有两个名为HoursMinutes的整数。我的两个文本框绑定到它们并显示它们。通过文本框设置一个值也会重置时间,设置时间(通过属性)重置小时和分钟,两个文本框在设置此值后都会更新。

这项工作很好。现在我想添加一个名为ReadOnly的属性。 TimePicker需要ReadOnly才能显示时间。手动设置这个时间是没有意义的,所以我想有可能设置两个Textboxes IsReadOnly属性。

ReadOnly现在是UserControl的第二个属性。因为我很懒,我想通过UserControl直接绑定Property和两个Textbox,并仅将IsReadOnly - Property绑定到UserControl。

这是我的想法代码(Usercontrol):

public partial class TimeBox : UserControl, INotifyPropertyChanged
{
    private SingleTimeViewModel viewModel;

    //... other Properties

    public static DependencyProperty ReadOnlyProperty = DependencyProperty.Register("ReadOnly", typeof(Boolean), typeof(TimeBox), new PropertyMetadata(false));

    // Schnittstellen-Ereignis  
    public event PropertyChangedEventHandler PropertyChanged;
    protected internal void OnPropertyChanged(string propertyname)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
    }

    public TimeBox()
    {
        InitializeComponent();
        viewModel = new SingleTimeViewModel(SingleTime.CreateSingleTime());
        this.DataContext = viewModel;
    }

    //... Code of other Properties

    private bool _ReadOnly;
    public bool ReadOnly
    {
        get
        {
            return _ReadOnly;
        }
        set
        {
            if (_ReadOnly == value)
                return;
            _ReadOnly = value;
            OnPropertyChanged("ReadOnly");
        }
    }

    //... Other Methods
}

这通过XAML绑定到两个文本框(Text的绑定导致ViewModel,IsReadOnly应该绑定到TimeBox):

<UserControl x:Name="TimeBoxControl" x:Class="TimeCalculator.TimeBox"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         ... >
    <WrapPanel Grid.Column="7" HorizontalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="1" VerticalAlignment="Center" >
        <TextBox x:Name="txtBxHours" ... Text="{Binding Hours}" ... IsReadOnly="{Binding ReadOnly, ElementName=TimeBoxControl}" />
        <Label x:Name="lblSeparator" ... />
        <TextBox x:Name="txtBxMinutes" ... Text="{Binding Minutes}" ...  IsReadOnly="{Binding ReadOnly, ElementName=TimeBoxControl}" />
   </WrapPanel>
</UserControl>

InitializeComponent之后,我在项目主窗口的构造函数中只读取了值。{1}}。因此我使用了以下几行:

this.TmBxMayGo.ReadOnly = true;
this.TmBxMustGo.ReadOnly = true;
this.TmBxTimeUntilMayGo.ReadOnly = true;
this.TmBxTimeUntilMustGo.ReadOnly = true;
this.TmBxCurrentOvertime.ReadOnly = true;

这不起作用,经过一些调试我发现它没有,因为PropertyChangedEventHandler PropertyChanged总是null。 我经常搜索找到这个问题的解决方案,但我没有犯任何常见错误(例如忘记: INotifyPropertyChanged,错误的字符串中的错误名称或其他)。

我终于放弃了并通过ViewModel制作了它。但是当我通过ViewModel设置PropertyChangedEventHandler PropertyChanged时,null也是PropertyChanged,但是在调用后文本框是ReadOnly。

现在我有两个问题:

  1. 为单个Usercontrol创建自己的ViewModel是否有意义?
  2. 为什么会这样?怎么可能null是{{1}}两次但只能工作一次?

2 个答案:

答案 0 :(得分:0)

  1. 是的,为单一的立即逻辑分离的UI提供单个ViewModel是有意义的。它将责任与mainviewmodel分开。因此,将所有属性放在ViewModel中。

  2. 当您将该对象设置为视图的DataContext时,WPF仅将处理程序附加到INotifyPropertyChanged对象的PropertyChanged事件,这就是在设置usercontrol的DataContext之前PropertyChanged为null的原因。您的文本框仍然处于禁用状态,因为在初始化绑定时,会调用这些属性的getter,并使用您提供的默认值更新UI。

答案 1 :(得分:0)

一个更简洁的做法就是这样的

    public bool ReadOnly
    {
        get { return (bool)GetValue(ReadOnlyProperty); }
        set { SetValue(ReadOnlyProperty, value); }
    }

    // Using a DependencyProperty as the backing store for ReadOnly.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ReadOnlyProperty =
        DependencyProperty.Register("ReadOnly", typeof(bool), typeof(TimeBox), new PropertyMetadata(false, null, CoerceReadOnly));

    private static object CoerceReadOnly(DependencyObject d, object baseValue)
    {
        if ((d as TimeBox)._Enabled == baseValue)
            return DependencyProperty.UnsetValue;
        return baseValue;
    }

这里我使用Coerce Value回调来检查条件,所以通过返回DependencyProperty.UnsetValue将取消属性更改,否则它将继续

更多关于Coerce的信息 http://msdn.microsoft.com/en-us/library/ms745795(v=vs.110).aspx#Advanced

对于其他问题,我会说你可能不需要创建一个usercontrol,除非你想部署一个控件库。请尝试利用相同的数据模板。

其次,由于您绑定到依赖于框架的用户控件,因此常规通知可能无法按预期工作。