2011-04-26 1 views
9

VisibileItems라는 DependencyProperty를 노출하는 사용자 정의 컨트롤이 있습니다. 해당 속성이 업데이트 될 때마다 다른 이벤트를 트리거해야합니다. 이를 위해 PropertyChangedCallback 이벤트와 함께 FrameworkPropertyMetadata를 추가했습니다.WPF : PropertyChangedCallback이 한 번만 실행되었습니다.

어떤 이유로이 이벤트는 한 번만 호출되며 다음에 VisibleItems가 변경되면 트리거되지 않습니다.

XAML :

<cc:MyFilterList VisibleItems="{Binding CurrentTables}" /> 

CurrentTables는 MyViewModel에 DependencyProperty에 있습니다. 현재 테이블은 자주 변경됩니다. 다른 WPF 컨트롤을 CurrentTables에 바인딩 할 수 있으며 UI의 변경 사항을 볼 수 있습니다. 여기

내가 VisiblePropertyChanged에 스테핑 PropertyChangedCallback

public static readonly DependencyProperty VisibleItemsProperty = 
    DependencyProperty.Register(
    "VisibleItems", 
    typeof(IList), 
    typeof(MyFilterList), 
    new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(VisiblePropertyChanged)) 

    ); 

public IList VisibleItems { 
    get { return (IList)GetValue(VisibleItemsProperty); } 
    set { SetValue(VisibleItemsProperty, value); } 
} 

VisibleItems와 유선 방식으로, 난은 세트 도착 처음 CurrentTables 트리거 도착 것을 알 수있다. 후속 시간은 아닙니다.

여러분 중 일부는 CurrentTables 수정하는 방식에 의문을 제기으로,이 변화에 완전히 할당 재

UPDATE :

OnDBChange()... 
CurrentTables = new List<string>(MainDatabaseDataAdapter.GetTables(this.SelectedServer, this.SelectedDatabase)); 

이 라인은 모든 변경에 호출되는,하지만 내 VisiblePropertyChanged 핸들러가 호출되는 처음으로. 내가 직접 VisibleItems를 할당하는 경우

UPDATE는

, 핸들러는 때마다 호출되는 않습니다! 문제가 다른 DependencyProperty에 (CurrentTables)를보고하는 DependencyProperty (VisibleItems)에서 유래처럼

TestFilterList.VisibleItems = new List<string>(Enumerable.Range(1, DateTime.Now.Second).ToList().Select(s => s.ToString()).ToList()); 

그래서, 그것은 보인다. 어떻게 든 바인딩은 첫 번째 속성 변경시 작동하지만 후속 속성 변경은 수행하지 않습니다. 당신이 제안한대로 스눕으로이 문제를 검사하려고 시도했습니다.

+0

당신이 그대로 바인딩 남아 하시겠습니까? Snoop을 사용하여 바인딩이 VisibleItems 속성을 올바르게 업데이트하는지 확인합니다. –

+0

당신은 암시하고 있지만 명시 적은 아닙니다 : 속성 변경 콜백이 호출되지 않고'VisibleItems'가 실제로 다른 값으로 변경되었는지 확인합니까? –

+0

릭, 네, 확신합니다. 단계를 밟아 전화가 걸려서 전화가 왔을 때 모든 전화에서 "새 목록"을 만들어서 같은 인스턴스가 아닌지 확인합니다. 또한 코드를 보면 매번 데이터베이스에서 목록을 가져 오는 것을 볼 수 있습니다. –

답변

13

당신이 '지역'값을 설정하고이 경우에 당신은 ObservableCollection에 사용하고이 같은 뭔가를 할 것입니다 OneWay 바인딩에? 그렇다면 다른 로컬 값을 설정하면, 당신은 바인딩을 제거하는 것을 의미합니다 로컬 값으로 처리하는 the MSDN dependency property overview:

바인딩에 언급 한 바와 같이, 로컬 값을 설정하면, 바인딩이 제거됩니다.

종속성 속성 메커니즘은 종속성 속성에 로컬 값을 저장하도록 요청받을 때 수행 할 수있는 작업이 많지 않습니다. 바인딩은 '포인트'가 잘못된 방식이기 때문에 바인딩을 통해 값을 보낼 수 없습니다. 로컬 값으로 설정된 후에는 더 이상 바인딩에서 가져온 값을 표시하지 않습니다. 더 이상 바인딩에서 값을 표시하지 않으므로 바인딩을 제거합니다.

바인딩이 사라지면 바인딩 소스 속성이 값을 변경하면 PropertyChangedCallback이 더 이상 호출되지 않습니다. 이것이 콜백이 호출되지 않는 이유 일 수 있습니다.

바인딩을 TwoWay으로 설정하면 바인딩 시스템에서 사용자가 설정 한 '로컬'값을 저장할 위치가 바인딩의 원본 속성에 있습니다. 이 경우 종속성 속성 메커니즘이 값을 source 속성에 저장할 수 있기 때문에 바인딩을 제거 할 필요가 없습니다. 다음과 같은 상황이 발생하기 때문에

이 상황은 스택 오버 플로우가 발생하지 않습니다 :

  • 종속성 속성은 '지역'값을받습니다.
  • 종속성 속성기구는 소스 속성의 새로운 값을 확인하여,
  • 종속성 속성기구 PropertyChanged 이벤트를 수신
  • 소스 속성은 속성 값과 화재 PropertyChanged 설정 소스 속성에 바인딩의 '뒤로'값을 보내는 것이 여태 것을 발견 바뀌지 않고 더 이상 아무것도하지 않습니다. 여기

중요한 점은 값이을 변경되지 않은 속성 에 대한 PropertyChanged 이벤트가 발생하는 경우, 사용자의 속성에 바인딩 종속성 속성에 어떤 PropertyChangedCallback의 호출되지 않을 것입니다.

위의 내용을 간략하게하기 위해 나는 IValueConverter을 무시했습니다. 변환기가 있으면 양방향으로 값을 올바르게 변환하고 있는지 확인하십시오. 또한 다른 끝에있는 속성은 INotifyPropertyChanged을 구현하는 객체의 뷰 모델 속성이라고 가정했습니다. 바인딩의 소스 끝에 다른 종속성 속성이있을 수있었습니다. 종속성 속성 메커니즘도이를 처리 할 수 ​​있습니다.

WPF (및 Silverlight)에는 스택 오버플로가 감지되지 않습니다. PropertyChangedCallback에서 종속성 속성의 값을 새 값과 다르게 설정하면 (예 : 정수 값 속성을 증가 시키거나 문자열을 문자열 값 속성에 추가하여) 스택 오버플로가 발생합니다.

+0

아니요, 이벤트 내에서 VisibleItems를 설정하지 않습니다. 이벤트가 번역을 수행하고 내 사용자 정의 컨트롤에 다른 속성을 설정합니다. –

+0

@ 루크 우드워드 : 천 번 감사드립니다! 귀하의 게시물은 적어도 디버깅 하루를 절약 해주었습니다. –

0

컬렉션의 내용은 변경되지만 실제 인스턴스는 변경되지 않는 문제가 발생할 수 있습니다. 또한이있는 종속성 속성에 (즉, 종속성 속성 세터에 직접 할당)

public static readonly DependencyProperty VisibleItemsProperty = 
    DependencyProperty.Register(
    "VisibleItems", 
    typeof(IList), 
    typeof(MyFilterList), 
    new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(VisibleItemsChanged))); 

    private static void VisibleItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var myList = d as MyFilterList; 
     if (myList == null) return; 

     myList.OnVisibleItemsChanged(e.NewValue as IList, e.OldValue as IList); 
    } 

    protected virtual void OnVisibleItemsChanged(IList newValue, IList oldValue) 
    { 
     var oldCollection = oldValue as INotifyCollectionChanged; 
     if (oldCollection != null) 
     { 
      oldCollection.CollectionChanged -= VisibleItems_CollectionChanged; 
     } 
     var newCollection = newValue as INotifyCollectionChanged; 
     if (newCollection != null) 
     { 
      newCollection.CollectionChanged += VisibleItems_CollectionChanged; 
     } 
    } 
+0

컬렉션이 반복적으로 다시 할당되고 있습니다. –

+1

원본 질문에 더 많은 코드를 게시 할 수 있습니까? – bendewey

+0

더 많은 코드가 추가되었습니다. thanks –

1

그냥 인스턴스화 MyFilterList과 같은 코드를 통해 VisibleItems을 설정 한 경우 :

var control = new MyFilterList(); 
control.VisibleItems = new List<string>(); 
control.VisibleItems = new List<string>(); 

당신은 가능성이 PropertyChangedCallback 때마다 일어날 볼 수 있습니다. 즉, 문제는 콜백이 아니라 바인딩 때문입니다. 바인딩 오류가없고 코드를 VisibleItems (예 : VisibleItems을 설정하여 바인딩이 깨지지 않는 경우) PropertyChanged을 제기하고 있습니다.

+0

오른쪽 위에 그 속성을 직접 할당하고 매번 작동하지 .. 그래서 문제가 한 dep 속성을 다른 ..보고 어떻게 든 변경 통보를 한 번 발생합니다 줄기 것 같습니다 ... –

4

내 코드에 동일한 문제가 있으며 루크가 맞습니다. PropertyChangedCallback 내에서 SetValue를 호출하여 잠재적 인 무한 루프를 일으켰습니다. WPF는 콜백을 자동으로 비활성화하는 것을 방지합니다!

내 WPF UserControl을가

PatchRenderer 

내 C# 속성은 주입니다 :

[Description("Note displayed with star icons"), 
    Category("Data"), 
    Browsable(true), 
    EditorBrowsable(EditorBrowsableState.Always), 
    DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] 
    public int Note 
    { 
     get { return (int)GetValue(NoteProperty); } 
     set { SetValue(NoteProperty, value); /* don't put anything more here */ } 
    } 

내 WPF 속성

public static readonly DependencyProperty 
     NoteProperty = DependencyProperty.Register("Note", 
     typeof(int), typeof(PatchRenderer), 
     new PropertyMetadata(
      new PropertyChangedCallback(PatchRenderer.onNoteChanged) 
      )); 

    private static void onNoteChanged(DependencyObject d, 
       DependencyPropertyChangedEventArgs e) 
    { 
     // this is the bug: calling the setter in the callback 
     //((PatchRenderer)d).Note = (int)e.NewValue; 

     // the following was wrongly placed in the Note setter. 
     // it make sence to put it here. 
     // this method is intended to display stars icons 
     // to represent the Note 
     ((PatchRenderer)d).UpdateNoteIcons(); 
    }