我正在使用 MVVM Light Toolkit 开发 WPF 应用程序。我只想显示一个嵌套的Observablecollection
,它将Employee Attendance详细信息保存到DataGrid
并在内部网格中执行一些CRUD功能,并根据这些更改自动重新计算外部收集记录。内部集合(PunchDetailModels
)显示在RowDetailsTemplate
的{{1}}中。
以下是模特:
DataGrid
ViewModel:
public class AttendanceModel : ObservableObject
{
public const string EmpNamePropertyName = "EmpName";
private string _empName = string.Empty;
public string EmpName
{
get
{
return _empName;
}
set
{
Set(EmpNamePropertyName, ref _empName, value);
}
}
public const string PunchDetailModelsPropertyName = "PunchDetailModels";
private ObservableCollection<PunchDetailModel> _punchDetailModels = null;
public ObservableCollection<PunchDetailModel> PunchDetailModels
{
get
{
return _punchDetailModels;
}
set
{
Set(PunchDetailModelsPropertyName, ref _punchDetailModels, value);
}
}
private string _inOutCount;
public string InOutCount
{
get
{
return PunchDetailModels != null
? string.Format("{0}/{1}", PunchDetailModels.Count(i => i.PunchStatus == Enums.PunchType.CheckIn),
PunchDetailModels.Count(i => i.PunchStatus == Enums.PunchType.CheckOut))
: null;
}
}
public TimeSpan? FirstCheckIn
{
get
{
if (_punchDetailModels != null)
{
var firstCheckIn =
_punchDetailModels.OrderBy(t => t.PunchTime)
.FirstOrDefault(i => i.PunchStatus == Enums.PunchType.CheckIn);
if (firstCheckIn != null)
return firstCheckIn.PunchTime;
}
return null;
}
}
public TimeSpan? LastCheckOut
{
get
{
if (_punchDetailModels != null)
{
var lastCheckOut =
_punchDetailModels.OrderBy(t => t.PunchTime)
.LastOrDefault(o => o.PunchStatus == Enums.PunchType.CheckOut);
if (lastCheckOut != null)
return lastCheckOut.PunchTime;
}
return null;
}
}
public TimeSpan? TotalInTime
{
get
{
TimeSpan totalInTime = TimeSpan.Zero;
if (_punchDetailModels != null)
{
if (!IsValidRecord()) return null;
for (int inTime = 0; inTime < _punchDetailModels.Count; inTime += 2)
{
totalInTime += _punchDetailModels[inTime + 1].PunchTime - _punchDetailModels[inTime].PunchTime;
}
}
return totalInTime;
}
}
public TimeSpan? TotalOutTime
{
get
{
TimeSpan totalInTime = TimeSpan.Zero;
if (_punchDetailModels != null)
{
if (!IsValidRecord()) return null;
for (int inTime = 1; inTime < _punchDetailModels.Count - 1; inTime += 2)
{
totalInTime += _punchDetailModels[inTime + 1].PunchTime - _punchDetailModels[inTime].PunchTime;
}
}
return totalInTime;
}
}
}
public class PunchDetailModel : ObservableObject
{
public const string PunchStatusPropertyName = "PunchStatus";
private Enums.PunchType _punchStatus;
public Enums.PunchType PunchStatus
{
get
{
return _punchStatus;
}
set
{
Set(PunchStatusPropertyName, ref _punchStatus, value);
}
}
public const string PunchTimePropertyName = "PunchTime";
private TimeSpan _punchTime = TimeSpan.Zero;
public TimeSpan PunchTime
{
get
{
return _punchTime;
}
set
{
Set(PunchTimePropertyName, ref _punchTime, value);
}
}
}
查看:
我面临的问题:
1)当用户 ADD 或删除来自Inner DataGrid的特定记录时,我需要在视图模型中获取通知。我知道通过为ObservableCollection注册一个集合更改事件是可能的。但内部public const string AttendanceCollectionPropertyName = "AttendanceCollection";
private ObservableCollection<AttendanceModel> _attendanceCollection = null;
public ObservableCollection<AttendanceModel> AttendanceCollection
{
get
{
if (_attendanceCollection == null)
{
_attendanceCollection = new ObservableCollection<AttendanceModel>();
//_attendanceCollection.CollectionChanged+=_attendanceCollection_CollectionChanged;
}
return _attendanceCollection;
}
set
{
Set(AttendanceCollectionPropertyName, ref _attendanceCollection, value);
}
}
怎么可能?
2)我需要在viewmodel中获取Inner DataGrid中CheckIn或Checkout字段的任何更改通知,以便我可以重新计算TotalInTime,TotalOutTime等字段。
我该怎么做?我目前仍然坚持这种情况。请提出您的宝贵意见。
答案 0 :(得分:5)
我猜测ObservableObject
类是您自己的INotifyPropertyChanged
接口实现。现在来解决你的问题:
您应该在CollectionChanged
中注册_punchDetailModels
事件并在处理程序中为该变量举起PropertyChanged
事件,如下所示:
public ObservableCollection<PunchDetailModel> PunchDetailModels
{
get
{
return _punchDetailModels;
}
set
{
Set(PunchDetailModelsPropertyName, ref _punchDetailModels, value);
_punchDetailModels.CollectionChanged += handler;
}
}
private void handler(object sender, NotifyCollectionChangedEventArgs e)
{
base.RaisePropertyChanged(PunchDetailModelsPropertyName); // If you don't have a method with such signature in ObservableObject (one that takes a string and raises PropertyChanged for it) you'll have to write it.
}
这样,在从内部集合添加或删除元素时,视图应自动重新加载。
没有其他方式可以订阅在这些字段上收听PropertyChanged
。这就是View
所做的事情,这也是ViewModel
应该做的事情。像这样:
public const string AttendanceCollectionPropertyName = "AttendanceCollection";
private ObservableCollection<AttendanceModel> _attendanceCollection = null;
public ObservableCollection<AttendanceModel> AttendanceCollection
{
get
{
if (_attendanceCollection == null)
{
_attendanceCollection = new ObservableCollection<AttendanceModel>();
}
return _attendanceCollection;
}
set
{
Set(AttendanceCollectionPropertyName, ref _attendanceCollection, value);
_attendanceCollection.CollectionChanged+= handler
}
}
private void handler(object sender, NotifyCollectionChangedEventArgs e)
{
foreach (AttendanceModel model in AttendanceCollection)
model.PropertyChanged += somethingChanged;
}
// Very ineffective to subscribe to all elements every time a list changes but I leave optimization to you.
private somethingChanged (object obj, PropertyChangedEventArgs args)
{
if ( args.PropertyName == "CheckIn" ) // for example
{
AttendanceModel ModelToRecalculate = obj as AttendanceModel;
// You can do anything you want on that model.
}
}
当然,当您认为有必要时,您需要在PropertyChanged
课程中使用string
CheckIn
参数值AttendanceModel
来提升handler
(例如{{1}方法)
修改强>
回答你的评论问题:
“来到第二个 - 我需要重新计算出勤模型属性,如InOutCount,TotalInTime,PunchTime字段更新中的TotalOutTime。”
答案是:您无需在ViewModel
中执行任何操作即可“重新计算”。用户界面订阅PropertyChange
InOutCount
,FirstCheckIn
...等等。这是Binding
(它自动完成)。
所以,您需要做的就是通知UI需要重新计算给定模型的是调用RaisePropertyChanged("InOutCount")
,RaisePropertyChanged("FirstCheckIn")
。
用户界面将理解它需要获取这些属性,并且因为你在属性获取器中有这些计算,它将被重新计算。
因此,我发现每次INNER列表更改时都需要重新计算UI,因此您需要做的就是将handler
代码更改为CollectionChanged
PunchDetailModels
:
// the handler for CollectionChanged for the INNER collection (PunchDetailModels)
private void handler(object sender, NotifyCollectionChangedEventArgs e)
{
base.RaisePropertyChanged(PunchDetailModelsPropertyName); // If you don't have a method with such signature in ObservableObject (one that takes a string and raises PropertyChanged for it) you'll have to write it.
base.RaisePropertyChanged("InOutCount")
base.RaisePropertyChanged("FirstCheckIn")
base.RaisePropertyChanged("LastCheckOut")
// and so on for all the properties that need to be refreshed
}