10

VisibileItemsというDependencyPropertyを公開するユーザーコントロールがあります。そのプロパティが更新されるたびに、別のイベントをトリガーする必要があります。これを実現するために、PropertyChangedCallbackイベントを使用してFrameworkPropertyMetadataを追加しました。

何らかの理由で、このイベントは1回だけ呼び出され、次にVisibleItemsが変更されたときにトリガーされません。

XAML:

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

CurrentTablesは、MyViewModelのDependencyPropertyです。CurrentTablesは頻繁に変更されます。別のWPFコントロールをCurrentTablesにバインドすると、UIに変更が表示されます。

これが、VisibleItemsを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); }
}

VisiblePropertyChangedにステップインすると、CurrentTablesが最初に設定されたときにトリガーされることがわかります。しかし、その後の時間ではありません。

アップデート

CurrentTablesの変更方法について質問された方もいらっしゃると思いますが、変更時に完全に再割り当てされます。

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

この行は変更のたびに呼び出されますが、VisiblePropertyChangedハンドラーは最初にのみ呼び出されます。

アップデート

VisibleItemsを直接割り当てると、ハンドラーは毎回呼び出されます。

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

したがって、問題はDependencyProperty(VisibleItems)が別のDependencyProperty(CurrentTables)を監視していることに起因しているように見えます。どういうわけか、バインディングは最初のプロパティの変更では機能しますが、その後の変更では機能しませんか?あなたの何人かが示唆したように、スヌープでこの問題を調べようとしています。

4

4 に答える 4

15

OneWayバインディングも持つ依存関係プロパティに「ローカル」値を設定していますか (つまり、依存関係プロパティ セッターに直接割り当てていますか)。その場合、MSDN 依存関係プロパティの概要で説明されているように、ローカル値を設定するとバインディングが削除されます。

バインディングはローカル値として扱われます。つまり、別のローカル値を設定すると、バインディングが削除されます。

依存関係プロパティのメカニズムには、依存関係プロパティにローカル値を格納するように求められたときにできることは他にあまりありません。バインディングが間違った方向を「指す」ため、バインディングを介して値を送信できません。ローカル値に設定された後、バインディングから取得した値が表示されなくなりました。バインディングからの値が表示されなくなったため、バインディングを削除します。

バインディングがなくなると、バインディングのPropertyChangedCallback ソース プロパティの値が変更されたときに が呼び出されなくなります。これが、コールバックが呼び出されない理由である可能性があります。

バインディングを に設定した場合TwoWay、バインディング システムには、設定した「ローカル」値を保存する場所があります: バインディングの source プロパティです。この場合、依存関係プロパティ メカニズムはソース プロパティに値を格納できるため、バインディングを削除する必要はありません。

この状況では、次のことが発生するため、スタック オーバーフローは発生しません。

  • 依存関係プロパティは「ローカル」値を受け取ります。
  • 依存関係プロパティ メカニズムは、ソース プロパティへのバインドに沿って値を「逆方向」に送信します。
  • ソース プロパティはプロパティ値を設定し、発火しますPropertyChanged
  • 依存関係プロパティ メカニズムはPropertyChanged、イベントを受け取り、ソース プロパティの新しい値をチェックし、それが変更されていないことを検出し、それ以上何もしません。

ここで重要な点は、値が変更されていないPropertyChangedプロパティのイベントを発生させると、そのプロパティにバインドされている依存関係プロパティの s が呼び出されないことです。PropertyChangedCallback

IValueConverter簡単にするために、上記の sを無視しました。コンバーターがある場合は、両方向で値を正しく変換していることを確認してください。また、反対側のプロパティは、 を実装するオブジェクトのビューモデル プロパティであると仮定しましたINotifyPropertyChanged。バインディングのソース エンドに別の依存関係プロパティが存在する可能性があります。依存関係プロパティのメカニズムもそれを処理できます。

たまたま、WPF (および Silverlight) にはスタック オーバーフローの検出が含まれていません。でPropertyChangedCallback、依存関係プロパティの値を新しい値とは異なる値に設定すると (たとえば、整数値のプロパティをインクリメントするか、文字列値のプロパティに文字列を追加することによって)、スタック オーバーフローが発生します。

于 2011-04-26T22:20:01.023 に答える
5

コードに同じ問題があり、ルークは正しいです。PropertyChangedCallback 内で mystake によって 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();
    }
于 2012-06-05T17:22:47.307 に答える
1

次のようなコードで a をインスタンス化しMyFilterListて設定するだけの場合:VisibleItems

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

PropertyChangedCallback が毎回発生する可能性があります。つまり、問題はコールバックではなくバインディングにあります。バインディング エラーがないこと、 を上げていることPropertyChanged、およびバインディングを壊していないことを確認してください (たとえばVisibleItems、コードで設定することによって)。

于 2011-04-26T22:50:28.720 に答える
1

コレクションの内容は変更されているが、実際のインスタンスは変更されていないという問題が発生している可能性があります。この場合、ObservableCollection を使用して次のようにします。

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;
        }
    }
于 2011-04-26T20:10:49.050 に答える