Den*_*nis 7 c# wpf mvvm mvvm-light
我正在使用MVVM Light Toolkit开发WPF应用程序.我只想显示一个嵌套,它将Employee Attendance详细信息保存到内部网格中并在内部网格中执行一些CRUD功能,并根据这些更改我必须自动重新计算外部收集记录.内收集()是表示在的. ObservablecollectionDataGridPunchDetailModelsRowDetailsTemplateDataGrid
这是模特:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
ViewModel:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
查看:

我面临的问题:
1)当用户从Inner DataGrid 添加或删除特定记录时,我需要在视图模型中获取通知.我知道通过为ObservableCollection注册一个集合更改事件是可能的.但内心ObservableCollection怎么可能呢?
2)我需要在viewmodel中获取Inner DataGrid中CheckIn或Checkout字段的任何更改的通知,以便我可以重新计算TotalInTime,TotalOutTime等字段.
我怎样才能做到这一点 ?我目前仍然坚持这种情况.请提出您的宝贵意见.
我猜这个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.
}
Run Code Online (Sandbox Code Playgroud)这样,在从内部集合添加或删除元素时,视图应自动重新加载.
没有其他方式可以订阅听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.
}
}
Run Code Online (Sandbox Code Playgroud)当然,你需要提高PropertyChanged与string价值参数CheckIn中AttendanceModel时,您认为这是必要的类(例如在handler法)
编辑:
要回答您的评论问题:
"来到第二个 - 我需要重新计算出勤模型属性,如InOutCount,TotalInTime,PunchTime字段更新中的TotalOutTime."
答案是:你不需要在ViewModel"重新计算"中做任何事情.UI订阅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
}
Run Code Online (Sandbox Code Playgroud)