2013-05-23 2 views
3

이 문제는 잠시 동안 두통을 유발하여 프로젝트 진행을 방해하고 있습니다. ViewModel에 바인딩 된 컨트롤이 포함 된 WPF XAML 폼을 고려해보십시오. (데이터에 Caliburn.Micro MVVM 프레임 워크와 Entity Framework를 사용하고 있습니다.) Initialize() 메서드는 셸에서 호출하여 데이터베이스에서 폼의 데이터를로드하고 PropertyChanged 이벤트 처리기를 설정합니다. 양식에 변경된 데이터가 있는지 여부를 추적하는 IsDirty 플래그가 있습니다. IsDirty 속성에 바인딩 된 "저장"버튼이있어서 데이터가 변경되면 활성화됩니다.컨트롤이 초기화되는 동안 PropertyChanged가 실행되는 것을 방지하는 방법

// Sample code; forms have many controls.... 

// this is the property that the controls are bound to 
public Entity BoundData { get; set; } 

public void Initialize() 
{ 
    // this is an example line where I query the database from the Entity Framework ObjectContext... 
    BoundData = objectContext.DataTable.Where(entity => entity.ID == 1).SingleOrDefault(); 

    // this is to cause the form bindings to retrieve data from the BoundData entity 
    NotifyOfPropertyChange("BoundData"); 

    // wire up the PropertyChanged event handler 
    BoundData.PropertyChanged += BoundData_PropertyChanged; 

    IsDirty = false; 
} 

void BoundData_PropertyChanged(object sender, PropertyChangedEventArgs e) 
{ 
    IsDirty = true; 
} 

// implementation of the IsDirty flag 
public bool IsDirty 
{ 
    get 
    { 
     return _isDirty; 
    } 
    set 
    { 
     _isDirty = value; 
     NotifyOfPropertyChange("IsDirty"); 
    } 
} 

문제는 Initialize() 방법이 완료된 후 폼에 데이터베이스에서 초기화되고 인해 BoundData_PropertyChanged 이벤트 핸들러가 맞았다는 것이다. 따라서 IsDirty 플래그가 true로 설정되고 양식이 방금로드되어 사용자가 아무 것도 변경하지 않은 경우에도 저장 단추가 사용됩니다. 내가 뭘 놓치고 있니? 분명히 이것은 일반적인 문제이지만 좋은 해결책을 찾을 수 없었습니다. 이것은 첫 번째 MVVM 프로젝트이므로 일부 기본 개념이 누락되었습니다.

UPDATE : 내가 문제가 내가 바인딩의 모두 업데이트 할 때 발광하는 이벤트 또는 콜백에 후크 할 수 있어야한다는 것입니다 생각, 명확히하기 위해, 그래서 다음하여 PropertyChanged 이벤트를 연결할 수 있습니다 핸들러. 값이 실제로 변경된 경우

public virtual bool Prop1 
    { 
     get 
     { 
      return _prop1; 
     } 
     set 
     { 
      if (_prop1 != value) 
      { 
       _prop1 = value; 
       NotifyOfPropertyChange("IsDirty"); 
      } 
     } 

그 방법이 이벤트는 트리거, 그리고 단지 : 당신이 도움이 될 할 수

+0

나는이 질문이 오래전에 물었다는 것을 알고 있으며, 지금까지 해결했을 수도 있습니다. 나는 당신의 문제에 대한 해결책을 가지고 있을지도 모르겠지만, 당신이 제공 한 코드 샘플에는 충분한 코드가 없다. XAML의 이벤트를 포함하여 이러한 속성의 값을 변경하는 코드 숨김에서 처리되는 이벤트가 있습니까? 그렇다면 관련된 컨트롤의 IsLoaded 속성을 검사하여 개체의 속성을 업데이트하기 전에 바인딩이 완료되었는지 확인할 수 있습니다. 또한 바인딩이 완료되면 실행되는 컨트롤에 대한 Loaded 이벤트가 있습니다. – user3308241

+0

이 상황에서 XKCD 툰이 있습니다. redtetrahedron - 해결 했습니까? 나는 지금 같은 문제에 직면하고있다. ... – andrew

답변

0

것은 그래서 같은 변화를 트리거 속성을 설정하는 것입니다 중복 설정. 이것은 물론 여러분의 경우에 값이 실제로 변하지 않는다고 가정합니다.

+0

Entity Framework에 의해 생성 된 엔티티의 속성은 변경되었을 때만 실행되도록 설정자를 지키면 실제로 수행 할 수있다. 요점은 양식이 ** 초기화 중 **이므로 필드의 모든 값이 변경되고 있다는 것입니다. 시작해야 할 가치가 없습니다. – redtetrahedron

+0

Gotcha. 엔티티의 'HasChanges' 속성을 직접 추적하는 대신 추적하는 것은 어떻습니까? –

+1

또는 데이터가 콜백 메소드에 비동기 적으로로드 된 후에 만 ​​이벤트 핸들러를 추가 할 수 있습니다. –

0

제네릭 메서드 호출을 디자인하도록 제안합니다. 예를 들어, ValueChanged 이벤트 처리기 (사용자 지정 이벤트 처리기)가 BoundData_PropertyChanged를 호출합니다. 즉, 컨트롤에 변경이있을 때마다이 ValueChanged 메서드/핸들러를 호출 할 수 있습니다.

void ValueChanged(object sender, EventArgs e) 
    { 
    BoundData_PropertyChanged(); 
    } 

    textBox.TextChanged += new System.EventHandler(ValueChanged); 

구문이 정확하지 않을 수 있습니다하지만 당신은 내가 여기에 제안하고 무엇을 추측 할 수있다.

+0

감사합니다. 그러나 모든 바인딩이 초기화 될 때까지 이벤트가 실행되지 않도록하는 방법을 알 수 없습니다. – redtetrahedron

0

이 솔루션은 오류가 없지만 활용 한 제한된 테스트 케이스에서 저에게 효과적 일 수 있습니다.

첫 번째 항목에서 EntityKey 값은 null입니다. 이것을 확인하십시오.

/// <summary> 
/// Log the invoice status change 
/// </summary> 
/// <param name="value">The value that the invoice status is changing to</param> 
partial void OnInvoiceStatusValueChanging(string value) 
{ 
    var newStatus = ConvertInvoiceStatus(value); 
    if (this.EntityKey != null && InvoiceStatus != newStatus) 
    { 
     AddNewNote(string.Format("Invoice status changing from [{0}] to [{1}]", InvoiceStatus.GetDescription(), newStatus.GetDescription())); 
    } 
} 

/// <summary> 
/// Log the invoice status change 
/// </summary> 
partial void OnInvoiceStatusValueChanged() 
{ 
    if (this.EntityKey != null) 
     AddNewNote(string.Format("Invoice status changed to [{0}]", InvoiceStatus.GetDescription())); 
} 
0

나는이 질문이 고대라는 것을 알고 있지만, 똑같은 문제가있어서 그것을 알아 내는데 어려움을 겪었습니다. 저는 WPF/MVVM에 익숙하지 않았으며 Google에 적절한 것을 모르고 질문했을 것입니다. 여기 내 해결책이있다. 희망적으로 그것은 누군가를 돕는다.

원래 게시물의 것과 거의 동일한 IsDirty 플래그가 있습니다. 유일한 차이점은보기가 렌더링을 끝내면 false로 재설정하는 명령을 추가 한 것입니다.

<UserControl x:Class="MyUserControl" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"> 
<i:Interaction.Triggers> 
    <i:EventTrigger EventName="Loaded"> 
     <i:InvokeCommandAction Command="{Binding Path=OnLoadedCommand}"/> 
    </i:EventTrigger> 
</i:Interaction.Triggers> 

그런 다음 뷰 모델에 이벤트가 발생하면 false로 IsDirty 사용 플래그를 설정 : 기본 개념은 Notify ViewModel when View is rendered/instantiated

화재보기에로드 이벤트에서왔다.

public ICommand OnLoadedCommand { get; private set; } 

// Constructor 
public MyUserControlViewModel() 
{ 
    OnLoadedCommand = new DelegateCommand(OnLoaded); 
} 

public void OnLoaded() 
{ 
    // Ignore any PropertyChanged events that fire 
    // before the UserControl is rendered. 
    IsDirty = false; 
} 
관련 문제