Wpf 可观测数据契约和双向绑定
在我的WPF应用程序中,我使用WCF服务获取数据。 因此,很自然,在某个时候,我有一些“复杂”的对象,它们需要将Wpf 可观测数据契约和双向绑定,wpf,wcf,architecture,inotifypropertychanged,Wpf,Wcf,Architecture,Inotifypropertychanged,在我的WPF应用程序中,我使用WCF服务获取数据。 因此,很自然,在某个时候,我有一些“复杂”的对象,它们需要将DataContract作为一个整体传递给WPF应用程序 当然,现在我需要响应更改,并在我的ViewModels上实现INotifyPropertyChanged,但是由于某些对象实际上是DataContract,我必须重新组合这些对象,以便它们实现INotifyPropertyChanged 我觉得很乱 我试图直接在DataContract定义上实现接口,但无法对更改做出正确反应。
DataContract
作为一个整体传递给WPF应用程序
当然,现在我需要响应更改,并在我的ViewModels上实现INotifyPropertyChanged
,但是由于某些对象实际上是DataContract,我必须重新组合这些对象,以便它们实现INotifyPropertyChanged
我觉得很乱
我试图直接在DataContract定义上实现接口,但无法对更改做出正确反应。例如,如果双向数据绑定的
TextBox
的文本发生了更改,则我的ViewModel应该通过更改相应SQL表中的值(通过WCF服务)对其作出反应,但由于对象是在WCF端定义的,因此我无法在属性的setter中执行此操作
我现在要做的是订阅DataContracts的PropertyChanged事件,并使用反射来了解哪个属性发生了更改及其新值。但是这些对象被保存在一个
可观察的集合中
,这是很多事件,它感觉非常脆弱。。。例如,如果我从集合中添加/删除一个元素会怎么样
我是这样做的(我认为这很糟糕):
foreach(ImageInfo imgi in(param.Images as ObservableCollection))
{
imgi.PropertyChanged+=(发送方,参数)=>
{
object newValue=Tools.GetProperty((发送方为ImageInfo),args.PropertyName);
};
}
然后我会将其发送回WCF服务
public class LoadObject(int id)
{
CurrentObject = new MyModel(serviceReference, id);
}
有没有更优雅的解决方案?我是否应该仅在ViewModel上实现INotifyPropertyChanged,并重新组合DataContracts
谢谢 那么您想要一个由WCF服务器链接到数据库的实时系统 如何创建模型对象,这些对象的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");
}
}
}
好吧,经过一段时间的思考,我现在就这样实现了它,但它可能会改变,因为我下周一会与一位专家会面(如果他给我一个更好的想法,我会更新) 1) WCF服务有一个DataContract,例如:
[DataContract]
public class MyWcfData : INotifyPropertyChanged
{
public MyWcfData()
{
MyField = "";
}
[DataMember]
public string MyField;
[DataMember]
public string MyFieldModified;
}
注意:INotifyPropertyChanged是以通常的方式实现的,我只是为了可读性而将其省略,因为我的代码片段没有放在这台计算机上
在服务上调用GetMyData()时,它返回该类的一个实例,只填充“MyField”。MyFieldModified保留为空。在接收MyWcfData的我的DAL(so客户端)上:
public class MyDAL
{
//... some init code
public MyWcfData GetMyWcfData()
{
MyWcfData newData = m_WcfService.GetMyData();
newData.MyFieldModified = newData.MyField;
return newData;
}
}
这里的要点是需要复制数据,以便我能够跟踪更改,并且最终只更新一次属性,即使用户更改了10次(我只是每次都将其与原始值进行比较)。但是我不想通过线路发送重复的数据,所以我在任何业务对象访问数据之前在DAL中进行复制
在“我的视图”的ViewModel上:
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();
}
}
公共类MyViewModel
{
//…一些初始化代码
DelegateCommand _GetDataCommand;
公共DelegateCommand GetDataCommand
{
得到
{
如果(_GetDataCommand==null)
_GetDataCommand=新的DelegateCommand(GetData);
返回_GetDataCommand;
}
}
public void GetData(MyWcfData参数)
{
m_WcfData=m_DAL.GetMyWcfData();
}
}
最后但并非最不重要的一点是,在我的XAML中,我绑定到文本框中的MyFieldModified
(双向绑定)
然后使用
System.Windows.Interactivity
在TextChanged
事件时调用DelegateCommand
。当最后一个命令被触发时,我将更改放入队列(以跟踪更改顺序),并在用户按下保存按钮时将其发送回Wcf服务以实现数据持久性 注意:我实际上使用了一个定制的
ObservableQueue
,这样我可以跟踪更改的数量,并通过绑定相应地启用保存按钮。我会写一篇关于它的博文,请继续关注;-)
注2:我放弃了让它保存即时所做的每一个微小更改,即使在这种情况下,它也会起作用,因为它不是一个经常使用的功能,而且预期的更改很少;但正如其他答案所指出的,这是一种不好的做法。我可以解释我是如何做到这一点的。数据契约是普通对象。viewmodel接收数据协定作为构造函数参数,并填写自己的属性。用户单击“保存”按钮后,视图模型会收集新值,构造新的数据协定并将其发送到服务。是的,但我没有“保存”按钮,每次属性更改时,我都需要调用该服务以反映数据库中的更改。这更难,而且会增加数据库的工作负载。但如果我有这样的要求,我也会这样做:创建新的数据契约并将其发送到服务,即使数据契约只因一个属性不同。如果要向RaisePropertyChanged方法添加额外的方法调用,则可以处理实时更改。在主视图模型中调用Save方法,或者从子项中使用某种Messenger类。这正是我的想法,只是我不知道如何构建该类。当ViewModel请求数据时,该对象由WCF服务创建,并且该WCF对象(契约)实现INotifyPropertyChanged。它不能自称。你需要合成上面的代码才能工作。该应用程序将由几十个用户同时使用,但WCF双工不在主题范围内(稍后再讨论)。@Baboon I实际上会让WCF与数据传输对象(DTO)一起工作,并使用类似于
AutoMapper
的东西将DTO映射到模型。因此ViewModel实际上将负责创建模型obj
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();
}
}