可观察DataContracts和双向绑定

可观察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

我可以解释我是如何做到这一点的。数据契约是普通的对象。视图模型接收数据协定作为构造函数参数并填写自己的属性。用户单击保存按钮后 - 视图模型收集新值,构建新的数据合同并将其发送到服务。 – vorrtex

+0

是的,但我没有“保存”按钮,每次更改属性以反映数据库中的更改时,我都需要调用该服务。 –

+0

这更加困难,增加了数据库的工作量。但是如果我有这样的要求,我会这样做:创建新的数据合同并将其发送到服务,即使数据合同只有一个属性而不同。如果向RaisePropertyChanged方法添加其他方法调用,则可以处理实时更改。调用主视图模型中的Save方法或使用子项中的某种Messenger类。 – vorrtex

好吧,那么一段时间后,经过思考,我实现了它这样的现在,但它可能会改变,因为我与专家下周一(如果他给我一个更好的主意,我会更新)见面。

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); 
} 
+0

这正是我所想的,除非我不知道如何构建这个类。当ViewModel请求数据时,该对象由WCF服务创建,并且WCF对象(合同)实现INotifyPropertyChanged。它不能自称。你需要编写上面的代码才能工作。 –

+0

该应用程序将由几十名用户同时使用,但WCF双工不在话题中(稍后会提供)。 –

+0

@Baboon我实际上有WCF与数据传输对象(DTOs)的工作,并使用类似'AutoMapper'来将DTO映射到模型。因此,ViewModel实际上负责创建Model对象,并将其传递给构造函数中的当前WCF服务实例,构造函数将使用WCF服务从服务器获取DTO并映射其属性。查看我的编辑。即使不使用DTO,ViewModel仍然应该负责创建对象,因此它可以将它传递给服务引用。 – Rachel