我做了一个小项目来了解MVVM。这是一个计算器,可以计算您下班回家的时间。
我使用两个文本框和一个标签作为简单的“TimePicker”创建了UserControl
。这个Usercontrol有一个ViewModel(Mainwindow甚至有一个),它管理一个Timepicker的时间。它有三个属性:一个名为TimeValue
的int,它只是小时和分钟的值,还有两个名为Hours
和Minutes
的整数。我的两个文本框绑定到它们并显示它们。通过文本框设置一个值也会重置时间,设置时间(通过属性)重置小时和分钟,两个文本框在设置此值后都会更新。
这项工作很好。现在我想添加一个名为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。
现在我有两个问题:
null
是{{1}}两次但只能工作一次?答案 0 :(得分:0)
是的,为单一的立即逻辑分离的UI提供单个ViewModel是有意义的。它将责任与mainviewmodel分开。因此,将所有属性放在ViewModel中。
当您将该对象设置为视图的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,除非你想部署一个控件库。请尝试利用相同的数据模板。
其次,由于您绑定到依赖于框架的用户控件,因此常规通知可能无法按预期工作。