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を参照したくありません。誰かがそれについて私を助けることができますか?
助けてくれてありがとう!
このために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
これが役立つかどうかはわかりませんが、それでも試してみることができます。
Property
からDebugger
を保持するCollection
にBL
を追加します。
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
ドロップダウンに新しい値が表示されます。
この blog には、BindingOperationsの操作方法に関する簡単なチュートリアルがあります...非常に簡単です。