2011-09-28 59 views
1

我对Silverlight颇为陌生,并且对通知机制有疑问。我的解决方案是一个MVVM应用程序堆叠是这样的:GUI /视图不会注意视图模型中的更改。谁应该通知?

VIEW包含绑定到视图模型集合的RadGridView,该数据是entitycollection。 GridView的SelectedItem被绑定到viewmodel中的相应属性。

视图模型 存放低于在GridView被绑定到的属性和实现INotifyPropertyChanged。 •SelectList - 继承ObservableCollection的实体集合。当SelectList被设置时,它运行一个通知呼叫。 •SelectedItem - 为其自身属性还实现INotifyPropertyChanged的实体。当SelectedItem设置时,它运行一个通知调用。

我的问题是,谁应该进行通知调用,以便GridView知道值已更改?偶尔,实体中的一个属性直接在视图模型中以编程方式设置。就目前而言,虽然属性正确获取新值,但GUI中没有任何事情发生。

问候,柯乐

- 使用代码------------------------- UPDATE

VIEW

<UserControl 
    xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" 
    x:Class="X.Y.Z.MonthReport.MonthReportView" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit" 
    mc:Ignorable="d" 
    d:DesignHeight="300" d:DesignWidth="400"> 

    <Grid x:Name="LayoutRoot"> 
     <telerik:RadGridView x:Name="MonthReportGrid" 
          Grid.Row="1" 
          ItemsSource="{Binding SelectList}" 
          SelectedItem="{Binding SelectedItem, Mode=TwoWay}" 
          AutoGenerateColumns="False"> 
      <telerik:RadGridView.Columns> 
       <!-- The other columns have been cut out of this example --> 
       <telerik:GridViewDataColumn DataMemberBinding="{Binding curDate, Mode=TwoWay, TargetNullValue=''}" DataFormatString="{} {0:d}" Header="Avläst datum" UniqueName="curDate" IsVisible="True" IsReadOnly="False"> 
        <telerik:GridViewDataColumn.CellEditTemplate> 
         <DataTemplate> 
          <telerik:RadDateTimePicker SelectedValue="{Binding curDate, Mode=TwoWay, TargetNullValue=''}" InputMode="DatePicker" DateTimeWatermarkContent="ÅÅÅÅ-MM-DD" /> 
         </DataTemplate> 
        </telerik:GridViewDataColumn.CellEditTemplate> 
       </telerik:GridViewDataColumn> 
       <telerik:GridViewDataColumn DataMemberBinding="{Binding curValue, Mode=TwoWay, TargetNullValue=''}" Header="Avläst värde" UniqueName="curValue" IsVisible="True" IsReadOnly="False" /> 
     </telerik:RadGridView> 
    </Grid> 
</UserControl> 

VIEW .CS

using System; 
using System.Collections.Generic; 
using System.Windows.Data; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Windows.Controls; 
using Telerik.Windows.Controls; 
using Telerik.Windows.Controls.GridView; 


namespace X.Y.Z.MonthReport 
{ 

    public partial class MonthReportView : UserControl, IMonthReportView 
    { 
     /// <summary> 
     /// ViewModel attached to the View 
     /// </summary> 
     public IMonthReportViewModel Model 
     { 
      get { return this.DataContext as IMonthReportViewModel; } 
      set { this.DataContext = value; } 
     } 

     public MonthReportView() 
     { 
      InitializeComponent(); 
      this.MonthReportGrid.CellEditEnded += new EventHandler<GridViewCellEditEndedEventArgs>(MonthReportGrid_OnCellEditEnded); 
     } 


     public void MonthReportGrid_OnCellEditEnded(object sender, GridViewCellEditEndedEventArgs e) 
     { 
      if (e.Cell.Column.UniqueName == "curValue") 
      { 
       // ... 
       this.Model.SetAutomaticReadingDate(); 
      } 

      if (e.Cell.Column.UniqueName == "curDate") 
      { 
       this.Model.UpdateAutomaticReadingDate(); 
      } 
     } 
    } 
} 

视图模型

using System; 
using Microsoft.Practices.Prism.Events; 
using Microsoft.Practices.Prism.Modularity; 
using Microsoft.Practices.Unity; 
using Microsoft.Practices.Prism.Commands; 


namespace X.Y.Z.MonthReport 
{ 
    public class MonthReportViewModel : ViewModel<IMonthReportView>, IMonthReportViewModel 
    { 
     private readonly IEventAggregator eventAggregator; 
     private readonly IMonthReportService dataService; 
     private readonly IMonthReportController dataController; 


     private DateTime? _newReadingDate; 
     public DateTime? NewReadingDate 
     { 
      get { return _newReadingDate; } 
      set { _newReadingDate = value; } 
     } 

     //Holds the selected entity 
     private MonthReportEntity _selectedItem; 
     public MonthReportEntity SelectedItem 
     { 
      get { return _selectedItem; } 
      set 
      { 
       if (_selectedItem != value) 
       { 
        _selectedItem = value; 
        //The INotifyPropertyChanged implementation inherited from ViewModel-base. 
        Notify(() => this.SelectedItem); 
       } 
      } 
     } 

     //The entitycollection 
     private MonthReportEntityCollection _selectList; 
     public MonthReportEntityCollection SelectList 
     { 
      get { return _selectList; } 
      set 
      { 
       if (_selectList != value) 
       { 
        _selectList = value; 
        //The INotifyPropertyChanged implementation inherited from ViewModel-base. 
        Notify(() => this.SelectList); 
       } 
      } 
     } 

     public MonthReportViewModel(IMonthReportView view, 
      IEventAggregator eventAggregator, IMonthReportService dataService, IMonthReportController dataController) 
     { 
      this.InitializeCommands(); 
      this.eventAggregator = eventAggregator; 
      this.dataController = dataController; 
      this.dataService = dataService; 
      this.View = view; 
      this.View.Model = this; 

      dataService.onGetMonthReportComplete += new EventHandler<MonthReportEventArgs>(OnGetMonthReportComplete); 
      dataService.onSaveMonthReportComplete += new EventHandler<MonthReportEventArgs>(OnSaveMonthReportComplete); 

      InitializeData(); 
     } 

     public void InitializeCommands() 
     { 
      // ... 
     } 

     public void InitializeData() 
     { 
      GetMonthReport(); 
     } 

     //This function is not working as I want it to. 
     //The gridview doesn't notice the new value. 
     //If a user edits the grid row, he should not need to 
     //add the date manually, Therefor I use this code snippet. 
     public void SetAutomaticReadingDate() 
     { 
      if ((NewReadingDate.HasValue) && (!SelectedItem.curDate.HasValue)) 
      { 
       SelectedItem.curDate = NewReadingDate; 
       //The INotifyPropertyChanged implementation inherited from ViewModel-base. 
       Notify(() => this.SelectedItem.curDate); 
      } 
     } 

     public void GetMonthReport() 
     { 
      dataService.GetMonthReport(); 
     } 

     public void SaveMonthReport() 
     { 
      dataService.SaveMonthReport(SelectList);    
     } 

     void OnGetMonthReportComplete(object sender, MonthReportEventArgs e) 
     { 
      // ... 
     } 

     void OnSaveMonthReportComplete(object sender, MonthReportEventArgs e) 
     { 
      // ...  
     } 

     #region ICleanable 
     public override void Clean() 
     { 
      base.Clean(); 
     } 
     #endregion 
    } 
} 
+0

你是否在后台线程中操作viewmodel?如果不是,请张贴一些代码 – thumbmunkeys

回答

1

如果你做你结合这样

<telerik:GridViewDataColumn DataMemberBinding="{Binding curValue, Mode=TwoWay, TargetNullValue=''}" Header="Avläst värde" UniqueName="curValue" IsVisible="True" IsReadOnly="False" /> 

,你只需要看看约束力知道你要调用的PropertyChanged和你的绑定说:

类蒙山属性“curValue “必须实施INotifyProperyChanged以获得通知。

public void SetAutomaticReadingDate() 
    { 
     if ((NewReadingDate.HasValue) && (!SelectedItem.curDate.HasValue)) 
     { 
      //this is enough if the class of SelectedItem implements INotifyPropertyChanged 
      //and the curDate Poperty raise the event 
      SelectedItem.curDate = NewReadingDate;    
     } 
    } 

顺便说一句不好的代码风格名称的属性CURDATE!它应该是CurDate,带有camlCase的属性会伤害我的眼睛:)

+0

谢谢!它为我解决了这个问题,但一开始并没有解决。 INotifyPropertyChanged的实现在基础实体中是不正确的,所以我也被困住了。 – Clas

0

您的“MonthReportEntityCollection”必须实现接口“INotifyCollectionChanged”以允许向UI通知有关集合更改(项目添加/删除)的信息。 您的“MonthReportEntity”必须实现“INotifyPropertyChanged”接口,以便通知用户界面关于物件属性更改的信息。 其他东西看起来是正确的。

+0

该集合应该是一个ObservableCollection,它可以通知收集的变化,不需要创建一个新的集合。 – Aligned

+0

对齐,并不总是ObservableCollection提供足够的功能(对于分页支持,例如最好使用PagedCollectionView)。如果您需要任何带有附加功能的自定义集合,则必须实施INotifyCollectionChanged以通知UI有关集合更改的信息。 毫无疑问,您可以从ObservableCollection继承自定义集合。 – Andris