2009-11-30 59 views
0

首先尝试在业务线项目上实现MVVM模式。我遇到了一些问题,我假设有这样的eaiser答案:WPF MasterDetail ViewModel延迟更新到主列表。 (防止网格自动更新)

原型窗口是项目列表的基本主从视图。 (Person对象列表)。该视图包含主列表的Infragistics xamDataGrid。由于在网格中选择了该项目,因此您可以在下面的详细信息面板中编辑详细信息,并在详细信息面板中标注字段时更新显示在网格数据中的“实时”。唯一的事情是我不想“presto”,我想要“等到我按下”Apply Changes“按钮”。

我一直希望避免创建一个单独的列表实例来从我在细节面板中添加/删除/修改的项目的工作集中分离出主列表。

我已经下降的路径:

我推翻了CellValuePresenter风格在网格领域,所以我可以设置绑定为“单向”。这阻止了实时更新。

<ControlTemplate TargetType="{x:Type igDP:CellValuePresenter}"> 
    <ControlTemplate.Resources> 
    <Style TargetType="TextBlock"> 
     <Setter Property="Background" Value="{Binding Path=DataItem.NameUIProperty.IsDirty, Converter={StaticResource BooleanBrushConverter}}" /> 
     <Setter Property="IsEnabled" Value="{Binding Path=DataItem.NameUIProperty.IsEditable}" /> 
    </Style> 
    </ControlTemplate.Resources> 
    <ContentControl> 
    <TextBlock Text="{Binding Path=DataItem.Name, Mode=OneTime}" /> 
    </ContentControl>              
</ControlTemplate>  

然后我添加一个“的ApplyUpdates”命令(RelayCommand)我PersonListViewModel。这引发了“PERSON _ITEM_ UPDATED”消息。我正在使用MVVM Foundation Messenger和RelayCommand类的VB端口。

#Region "ApplyUpdates Command" 

Private mApplyUpdatesCommand As New RelayCommand(AddressOf ApplyUpdates) 
Public ReadOnly Property ApplyUpdatesCommand() As ICommand 
    Get 
     Return mApplyUpdatesCommand 
    End Get 
End Property 

Private Sub ApplyUpdates() 
    'the changes are already in the object in the list so we don't have to do anything here except fire off the Applied message 
    Messages.AppMessenger.NotifyColleagues(Messages.PERSON_ITEM_UPDATED) 
End Sub 

#End Region 

的PersonView登记该PERSON _ITEM_更新的消息和接收到消息时重新绑定网格。

'In Loaded Event 

'register for window messages we care about 
Messages.AppMessenger.Register(Messages.PERSON_ITEM_UPDATED, AddressOf OnPersonItemUpdated) 

'EventHandler 
Private Sub OnPersonItemUpdated() 
    PersonGrid.DataSource = Nothing 
    PersonGrid.DataSource = mViewModel.List 
End Sub 

所以,那有效,但它smells wrong。该视图似乎有太多的逻辑,并且ViewModel没有规定UI的状态,视图是。

我错过了什么?你会用什么方法让ViewModel延迟发布视图的更改?

更新:我现在正在为网格创建自定义ViewModel的路径(只读,没有Propertychanged通知)和可编辑的ViewModel的细节区域。两个VM都会包装相同的业务对象,但ReadOnly版本不会发布更改。这会让虚拟机控制视图更新的时间。

回答

0

当为infragistics数据网格声明您的字段布局时,可以使用UnboundField而不是Field。该类公开底层绑定的BindingPath和BindingMode属性。使用这种技术,您可以摆脱实时更新,并且您不需要自定义控件模板。

我对移动逻辑到虚拟机的想法:

创建单向网格的数据源和nViewModel.List的结合。 ApplyChanges然后可以调用:BindingOperations.GetBindingExpressionBase(dependencyObject, dependencyProperty).UpdateTarget();来强制目标属性DataSource刷新。不幸的是,这会将您的虚拟机绑定到绑定,但在您的视图中不会生成任何代码。

这里的一个大问题是,如果你有这个延迟绑定场景,ApplyChanges是真的需要一些IoC到视图中,因为只有视图会知道如何真正做更新(使用绑定或其他) 。最后,链中的东西将管理列表的两个实例:视图中的实例和虚拟机中的实际实例。在这种特定情况下,延迟更新似乎是View的行为。但是,虚拟机上的UpdateChanges命令实际上将该行为耦合到虚拟机,在这种情况下,我认为将这两个列表实例存储在虚拟机中是有意义的。

希望这会有所帮助。

+0

感谢Infragistics提示。有一天这将派上用场! – TheZenker 2009-11-30 19:44:10

0

我有一个类似的问题,在MVVM中实现一个选项对话框。您希望能够让用户编辑您的ViewModel的属性,但只在应用时才提交更改。我找到了合理的解决方案。下面是只有一个布尔属性“声音”为simplest of the options pads代码:

class PinBallOptionsPad : AbstractOptionsPad 
{ 
    public PinBallOptionsPad() 
    { 
     Name = "PinBallOptionsPad"; 
    } 

    public override void Commit() 
    { 
     base.Commit(); 
     Properties.Settings.Default.Save(); 
    } 

    #region "Sound" 

    public bool SoundEdit 
    { 
     get 
     { 
      return m_SoundEdit; 
     } 
     set 
     { 
      if (m_SoundEdit != value) 
      { 
       m_SoundEdit = value; 
       CommitActions.Add(
        () => Properties.Settings.Default.Sound = m_SoundEdit); 
       CancelActions.Add(
        () => 
        { 
         m_SoundEdit = Properties.Settings.Default.Sound; 
         NotifyPropertyChanged(m_SoundEditArgs); 
         NotifyPropertyChanged(m_SoundArgs); 
        }); 
       NotifyOptionChanged(); 
       NotifyPropertyChanged(m_SoundEditArgs); 
      } 
     } 
    } 
    private bool m_SoundEdit = Properties.Settings.Default.Sound; 
    static readonly PropertyChangedEventArgs m_SoundEditArgs = 
     NotifyPropertyChangedHelper.CreateArgs<PinBallOptionsPad>(o => o.SoundEdit); 

    public bool Sound 
    { 
     get 
     { 
      return Properties.Settings.Default.Sound; 
     } 
    } 
    static readonly PropertyChangedEventArgs m_SoundArgs = 
     NotifyPropertyChangedHelper.CreateArgs<PinBallOptionsPad>(o => o.Sound); 
    #endregion 

} 

它看起来像一个很大的一个属性,但好处是,一切都为整个物业包含在声音区域内。因此,如果您复制并粘贴该内容并执行搜索和替换,则可以相对快速地创建新属性。为了了解如何CommitActions和CancelActions的作品,你需要的AbstractOptionsPad类以及:

public abstract class AbstractOptionsPad : AbstractPad, IOptionsPad 
{ 
    #region " Commit " 

    /// <summary> 
    /// If overriding this method, make sure to call base.Commit first. 
    /// </summary> 
    public virtual void Commit() 
    { 
     foreach (var commitAction in CommitActions) 
     { 
      commitAction(); 
     } 
     CommitActions.Clear(); 
     CancelActions.Clear(); 
    } 

    protected IList<Action> CommitActions 
    { 
     get 
     { 
      return m_commitActions; 
     } 
    } 
    private readonly IList<Action> m_commitActions = new List<Action>(); 

    #endregion 

    #region " Cancel " 

    /// <summary> 
    /// If overriding this method, make sure to call base.Cancel first. 
    /// </summary> 
    public virtual void Cancel() 
    { 
     foreach (var cancelAction in CancelActions) 
     { 
      cancelAction(); 
     } 
     CancelActions.Clear(); 
     CommitActions.Clear(); 
    } 

    protected IList<Action> CancelActions 
    { 
     get 
     { 
      return m_cancelActions; 
     } 
    } 
    private readonly IList<Action> m_cancelActions = new List<Action>(); 

    #endregion 

    public event EventHandler OptionChanged; 

    protected void NotifyOptionChanged() 
    { 
     var evt = OptionChanged; 
     if (evt != null) 
     { 
      evt(this, new EventArgs()); 
     } 
    } 
} 

这里的View是什么样子这种垫:

<DataTemplate DataType="{x:Type local:PinBallOptionsPad}"> 
    <CheckBox IsChecked="{Binding SoundEdit}"> 
     <TextBlock Text="Sound"/> 
    </CheckBox> 
</DataTemplate> 

因此,结合SoundEdit在选项中,但应用程序的其余部分可以绑定到Sound属性,并根据NotifyPropertyChanged事件进行更新。