可观察DataContracts和双向绑定
在我的WPF应用程序中,我使用WCF服务来获取数据。 所以很自然,在某些时候,我有一个“复杂”的对象,需要将一个DataContract
作为一个整体传递给WPF应用程序。可观察DataContracts和双向绑定
当然,我需要对变化做出响应,并在我的ViewModels上实现了INotifyPropertyChanged
,但由于某些对象实际上是DataContracts,因此我必须重新组合这些对象,以便它们实现INotifyPropertyChanged。
我觉得它很混乱。
我试图做的是直接在DataContract定义上实现接口,但我无法正确地对变化做出反应。
例如,如果双向数据绑定TextBox
的文本已更改,我的ViewModel应该通过更改相应SQL表中的值(通过WCF服务)对其做出反应,但由于该对象是在WCF端定义的,因此I不能在财产的制定者那里做到这一点。
我现在所做的是订阅DataContracts的PropertyChanged事件,并使用反射来知道哪些属性已更改及其新值。
但是,这些对象被保存在一个ObservableCollection<T>
,这是很多事件,它感觉非常脆弱......如果我从集合中添加/删除元素,例如该怎么办?
我做这样的(这是不好的,我认为):
foreach (ImageInfo imgi in (param.Images as ObservableCollection<ImageInfo>))
{
imgi.PropertyChanged += (sender, args) =>
{
object newValue = Tools.GetProperty((sender as ImageInfo), args.PropertyName);
};
}
然后我会发送回WCF服务。
有没有更优雅的解决方案呢?我应该只在ViewModel上实现INotifyPropertyChanged,并重新构造DataContracts?
谢谢!
好吧,那么一段时间后,经过思考,我实现了它这样的现在,但它可能会改变,因为我与专家下周一(如果他给我一个更好的主意,我会更新)见面。
1)WCF服务有一个DataContract如:
[DataContract]
public class MyWcfData : INotifyPropertyChanged
{
public MyWcfData()
{
MyField = "";
}
[DataMember]
public string MyField;
[DataMember]
public string MyFieldModified;
}
注: INotifyPropertyChanged的实现通常的方式,我刚刚离开它的可读性,因为我没有这个我的片段电脑。
当在服务上调用GetMyData()时,它将返回该类的一个实例,只填充“MyField”。 MyFieldModified留空。
在我的DAL(所以客户端),其中MyWcfData是收到:
public class MyDAL
{
//... some init code
public MyWcfData GetMyWcfData()
{
MyWcfData newData = m_WcfService.GetMyData();
newData.MyFieldModified = newData.MyField;
return newData;
}
}
这里的关键是需要对数据进行复制,这样我可以保持跟踪变化的,只有更新一个属性一次到底,即使用户改变了它10次(我只是每次比较原始值)。但是我不想通过线路发送重复的数据,所以相反,我在DAL中做了重复,之后任何业务对象都可以访问它。
在我的观点的视图模型:
public class MyViewModel
{
//.. some init code
DelegateCommand<MyWcfData> _GetDataCommand;
public DelegateCommand<MyWcfData> GetDataCommand
{
get
{
if (_GetDataCommand == null)
_GetDataCommand = new DelegateCommand<MyWcfData>(GetData);
return _GetDataCommand;
}
}
public void GetData(MyWcfData param)
{
m_WcfData = m_DAL.GetMyWcfData();
}
}
然后最后但并非最不重要的,在我的XAML中,我结合MyFieldModified
(双向绑定)在TextBox
。
然后我使用System.Windows.Interactivity
根据TextChanged
事件调用DelegateCommand
。
当最后一条命令被触发时,我将更改放入队列(以跟踪更改顺序),并在用户按下保存按钮时将其发送回Wcf服务以保持数据持久性。
说明:我实际上使用了一个定制的ObservableQueue<T>
,这样我就可以跟踪更改的次数并通过绑定启用相应的保存按钮。我将一篇博客文章就敬请期待;-)
注2:我放弃了做保存立即做出每一个微小的变化,即使在这种情况下,它会工作,因为它不是一个功能多使用,并且预计很少有变化;但正如其他答案指出的那样,这是不好的做法。
所以你想要一个实时系统,通过WCF服务器链接到数据库?
如何创建Model对象,其Get/Set方法去往/来自数据库?
public class MyModel : INotifyPropertyChanged
{
private IMyModelService service;
public int Id { get; set; }
public MyModel (IMyModelService wcfService, int id)
{
this.Id = id;
this.service = wcfService;
AutoMapper.Map<MyModelDTO, MyModel>(service.GetMyModel(this.Id), this);
}
public int SomeValue
{
get
{
return service.GetSomeValue(this.Id);
}
set
{
service.SetSomeValue(this.Id, value);
RaisePropertyChanged("SomeValue");
}
}
}
您缓存对象的属性在本地也可以,使WCF服务调用异步,这可能会提高性能,但如果超过一个人可以改变一个对象,你可能需要某种形式的消息系统,以提醒当另一用户更新属性时MyObject
已更改的客户端。
客户端上的ViewModel将负责创建模型,而不是WCF服务。
public class LoadObject(int id)
{
CurrentObject = new MyModel(serviceReference, id);
}
这正是我所想的,除非我不知道如何构建这个类。当ViewModel请求数据时,该对象由WCF服务创建,并且WCF对象(合同)实现INotifyPropertyChanged。它不能自称。你需要编写上面的代码才能工作。 –
该应用程序将由几十名用户同时使用,但WCF双工不在话题中(稍后会提供)。 –
@Baboon我实际上有WCF与数据传输对象(DTOs)的工作,并使用类似'AutoMapper'来将DTO映射到模型。因此,ViewModel实际上负责创建Model对象,并将其传递给构造函数中的当前WCF服务实例,构造函数将使用WCF服务从服务器获取DTO并映射其属性。查看我的编辑。即使不使用DTO,ViewModel仍然应该负责创建对象,因此它可以将它传递给服务引用。 – Rachel
我可以解释我是如何做到这一点的。数据契约是普通的对象。视图模型接收数据协定作为构造函数参数并填写自己的属性。用户单击保存按钮后 - 视图模型收集新值,构建新的数据合同并将其发送到服务。 – vorrtex
是的,但我没有“保存”按钮,每次更改属性以反映数据库中的更改时,我都需要调用该服务。 –
这更加困难,增加了数据库的工作量。但是如果我有这样的要求,我会这样做:创建新的数据合同并将其发送到服务,即使数据合同只有一个属性而不同。如果向RaisePropertyChanged方法添加其他方法调用,则可以处理实时更改。调用主视图模型中的Save方法或使用子项中的某种Messenger类。 – vorrtex