2009-11-30 44 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事件進行更新。