web-dev-qa-db-ja.com

BindingOperations.EnableCollectionSynchronizationの使用

2つのWPFアプリケーション「UI」、「デバッガー」、および1つのClassLibrary「BL」があります。デバッガーとBLへのUI参照。 BLへのデバッガー参照。 BLにMyCollectionというコレクションがあります。 UIアプリがデバッガーアプリを起動し、デバッガーがBLのコレクションMyCollectionにバインドします。 UIアプリからMyCollectionコレクションを変更しようとすると、例外が発生します。

A first chance exception of type 'System.NotSupportedException' occurred in PresentationFramework.dll

Additional information: This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.

私はグーグルでこれを見つけました: BindingOperations.EnableCollectionSynchronization 私はそれを使用する方法を理解することができません。 BLプロジェクトのUIdllを参照したくありません。誰かがそれについて私を助けることができますか?

助けてくれてありがとう!

9
Dilshod

このためにStackOverflowで見たすべての例は、間違っています。別のスレッドからコレクションを変更する場合は、コレクションをロックする必要があります

ディスパッチャー(UI)スレッドの場合:

_itemsLock = new object();
Items = new ObservableCollection<Item>();
BindingOperations.EnableCollectionSynchronization(Items, _itemsLock);

次に、別のスレッドから:

lock (_itemsLock)
{
    // Once locked, you can manipulate the collection safely from another thread
    Items.Add(new Item());
    Items.RemoveAt(0);
}

この記事の詳細: http://10rem.net/blog/2012/01/20/wpf-45-cross-thread-collection-synchronization-redux

25
Drew Noakes

これが役立つかどうかはわかりませんが、それでも試してみることができます。

PropertyからDebuggerを保持するCollectionBLを追加します。

private ObservableCollection<string> _data = new ObservableCollection<string>();
private object _lock = new object();

public ObservableCollection<string> Data { get {return _data;} }

コンストラクターに以下の行を追加するだけです

BindingOperations.EnableCollectionSynchronization(_data, _lock);

これはラインより上になり、スレッドセーフを処理します。

以下は例です

ViewModel(Debugger

internal class ViewModelClass : INotifyPropertyChanged
{
    private object _lock = new object ();
    private ObservableCollection<string> _data;

    public ObservableCollection<string> Data
    {
        get { return _data; }
        private set
        {
            _data = value;
            RaisePropertyChanged ("Data");
        }
    }

    private string _enteredText;
    public string EnteredText
    {
        get { return _enteredText; }
        set
        {
            _enteredText = value;
            _data.Add (value); RaisePropertyChanged ("EnteredText");
        }
    }

    private void RaisePropertyChanged (string name)
    {
        var pc = PropertyChanged;
        if (pc != null)
            pc (this, new PropertyChangedEventArgs (name));
    }

    public ViewModelClass ()
    {
        var _model = new ModelClass ();
        Data = _model.Data;
        _data.CollectionChanged += (s, e) => RaisePropertyChanged ("Data");
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

モデル(BL

internal class ModelClass
{
    private ObservableCollection<string> _data;

    public ObservableCollection<string> Data
    {
        get { return _data; }
        private set { _data = value; }
    }

    public ModelClass ()
    {
        _data = new ObservableCollection<string> { "Test1", "Test2", "Test3" };
    }
}

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow ()
    {
        InitializeComponent ();
        this.DataContext = new ViewModelClass ();
    }
}

MainWindow.xaml

<Window x:Class="CollectionSynchronizationTest.MainWindow"
            xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
            Title="MainWindow"
            Height="350"
            Width="525">
<StackPanel>
    <ComboBox IsEditable="True"
                        ItemsSource="{Binding Data}"
                        Text="{Binding EnteredText, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" />
    <Button Content="Test" />
</StackPanel>

ウィンドウが読み込まれたら、ComboBoxに「SomeValue」と入力し、Tabキーを押した後、ComboBoxドロップダウンに新しい値が表示されます。

5
Sandesh

この blog には、BindingOperationsの操作方法に関する簡単なチュートリアルがあります...非常に簡単です。

1
Marco