2009-11-30 1 views
0

LOB 프로젝트에서 MVVM 패턴을 구현하는 첫 번째 시도. 나는이 질문에 답을 달고있다.WPF MasterDetail ViewModel 마스터 목록의 업데이트가 지연됩니다. (그리드가 자동으로 업데이트되지 않도록)

Protype 창은 항목 목록의 기본적인 마스터 - 세부보기이다. (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_ UPDATED 메시지를 등록하고, 메시지가 수신 될 때 그리드를 바인드.

'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 버전은 변경 사항을 게시하지 않습니다. 이렇게하면 뷰가 업데이트되는 시점을 VM이 제어하게됩니다.

답변

0

Infragistics 데이터 그리드의 필드 레이아웃을 선언 할 때 필드 대신 UnboundField를 사용할 수 있습니다. 이 클래스는 기본 바인딩에 대한 BindingPath 및 BindingMode 속성을 제공합니다. 이 기술을 사용하면 실시간 업데이트를 제거 할 수 있으며 사용자 지정 컨트롤 템플릿이 필요하지 않습니다. VM에 논리를 이동에

내 생각은 :

그리드의 데이터 소스와 nViewModel.List의 바인딩 방법 중 하나를 만듭니다. 그런 다음 ApplyChanges는 BindingOperations.GetBindingExpressionBase(dependencyObject, dependencyProperty).UpdateTarget();을 호출하여 대상 속성 DataSource를 강제로 새로 고칩니다. 불행히도이 작업은 바인딩에 사용자의 VM을 연결하지만보기에는 코드가 생성되지 않습니다.

큰 문제는이 지연된 바인딩 시나리오가있는 경우 ApplyChanges는 뷰에 실제로 IoC가 약간 필요할 것입니다. 뷰에서만 실제로 업데이트를 수행하는 방법을 알고 있기 때문입니다 (Bindings를 사용하는 것이 든간에) . 결국 체인을 따라 무언가가 목록의 두 인스턴스, 즉 뷰의 인스턴스와 VM의 실제 인스턴스를 관리하게됩니다.이 특정 시나리오에서 지연된 업데이트는보기의 동작 인 것 같습니다. 그러나 VM의 UpdateChanges 명령은 실제로 해당 동작을 VM에 연결합니다.이 경우 VM에 두 개의 목록 인스턴스를 저장하는 것이 좋습니다.

희망이 도움이됩니다.

+0

Infragistics 팁 주셔서 감사합니다. 언젠가는 그렇게 될 것입니다! – TheZenker

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 클래스가 필요합니다

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

은 그래서 SoundEdit에 결합 :

여기
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이 패드의 모습입니다 옵션에 있지만 나머지 응용 프로그램은 Sound 속성에 바인딩 될 수 있으며 NotifyPropertyChanged 이벤트를 기반으로 업데이트 될 수 있습니다.