2

私は WPF アプリケーションに取り組んでおり、バインドされたプロパティのプロパティ変更通知がバックグラウンド スレッドから発生する可能性があることがわかりましたが、observablecollection (アイテムの追加や削除など) を変更するには、UI スレッドから発生する必要があります。私の質問は、なぜそうなのですか?INotifyPropertyChanged と INotifyCollectionChanged の両方が UI コントロールによってサブスクライブされているのに、なぜ INotifyPropertyChanged の例外が発生するのでしょうか?

例えば:

 public class ViewModel : INotifyPropertyChanged
    {
        ObservableCollection<Item> _items = new ObservableCollection<Item>();

        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                //Can fire this from a background thread without any crash and my 
                //Name gets updated in the UI
                InvokePropertyChanged(new PropertyChangedEventArgs("Name"));
            }
        }

        public void Add(Item item)
        {
            //Cant do this from a background thread and has to marshal.
            _items.Add(item);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void InvokePropertyChanged(PropertyChangedEventArgs e)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, e);
        }
    }

: バックグラウンド スレッドからの CollectionChanged イベントはアプリをクラッシュさせますが、バックグラウンド スレッドからの PropertyChanged イベントは問題なく UI を更新します。はい、これは .NET 4.0 にあります。

4

2 に答える 2

1

ほとんどの場合、バインディング メカニズムを意味します。

バインディングは、CLR プロパティおよび PropertyChanged イベントでは直接機能しません。
バインディングは、その作業にリフレクションを使用します。PropertyDescriptor、PropertyInfo、DependencyProperty、DynamicPropertyAccessor の優先順位があります。
ここで確認できます: PropertyPathWorker.SetValue(object item, object value)
同じ理由で、バインディングによってのみプロパティが変更される場合、INotifyPropertyChanged インターフェイスの存在に関係なく監視されます。
また、リフレクションを使用すると、観測されたプロパティがどのフローで変更されるかを心配する必要がなくなります。

コレクションの場合、バインドは、このコレクションが監視対象プロパティに割り当てられるストリームにも影響されません。
ただし、コレクション (INotifyCollectionChanged および IBindingList) の変更の監視は、バインディング メカニズムではなく、ItemsControl クラスの内部ロジックによって提供されます。
監視イベントへの直接のサブスクリプションが既に存在します。これにより、このロジックは、コレクションが変更されるスレッドに敏感になります。UI スレッドでない場合、監視は破棄されるか、例外がスローされます。

これは納得するだけで十分です。

デモ例。コレクションがランダムに変更されるクラス ViewModel。

public class RandomCollectionViewModel
{
    public ObservableCollection<int> Numbers { get; }
        = new ObservableCollection<int>() { 1, 2, 3 };
    private static readonly Random random = new Random();
    private readonly Timer timer = new Timer();

    public RandomCollectionViewModel()
    {
        timer.Interval = 500;
        timer.Elapsed += (s, e)
            => Numbers[random.Next(Numbers.Count)] = random.Next();
        timer.Start();
    }
}

バインディングを使用してコレクション項目を表示する XAML:

<StackPanel>
    <FrameworkElement.DataContext>
        <local:RandomCollectionViewModel/>
    </FrameworkElement.DataContext>
    <TextBlock Text="{Binding Numbers[0]}"/>
    <TextBlock Text="{Binding Numbers[1]}"/>
    <TextBlock Text="{Binding Numbers[2]}"/>
</StackPanel>

ItemsControlコレクション項目の表示に使用する XAML :

<StackPanel>
    <FrameworkElement.DataContext>
        <local:RandomCollectionViewModel/>
    </FrameworkElement.DataContext>
    <ItemsControl ItemsSource="{Binding Numbers}"/>
</StackPanel>

これらの例は、バインディングと の働きの違いを明確に示していますItemsContol

于 2021-04-24T09:36:38.257 に答える