2018年2月3日土曜日

EnableCollectionSynchronization使ってたのにExceptionが出た

WPFでDataGridにリストをバインドする際に、ObservableCollectionを使用しますが、
非同期処理でリストを更新するとExceptionが発生します。

そこで、EnableCollectionSynchronizationを使用して、lock処理を行います。


// Lock用のobjectを作成
private object lockObject = new object();

public ObservableCollection<Myitem> Result { get; set; }

public コンストラクタ()
{
    // 非同期での変更を可能にする
    Result = new ObservableCollection<Myitem>();
    BindingOperations.EnableCollectionSynchronization(this.Result, lockObject);
}

// これは非同期処理
private void Test()
{
    Result.Clear();
    for (int i = 0; i < 100; i++)
    {
        var item = new MyItem();
        Result.Add(item);
    }
}


なんとなく、これで良いと思っていたんだけど、これだと稀にExceptionが出るみたい。

非同期で処理すると、
System.ArgumentOutOfRangeException
インデックスは一覧の 範囲内になければなりません
(Index must be within the bounds of the List)



LockはEnableCollectionSynchronizationメソッドを使うだけじゃなくて、
更新される箇所にも使う必要があるみたい。


// Lock用のobjectを作成
private object lockObject = new object();

public ObservableCollection<Myitem> Result { get; set; }

public コンストラクタ()
{
    // 非同期での変更を可能にする 
    Result = new ObservableCollection<Myitem>();

    BindingOperations.EnableCollectionSynchronization(this.Result, lockObject);
}

// これは非同期処理
private void Test()
{
    // Lockを取得
    lock(lockObject)
    {
        Result.Clear();
        for (int i = 0; i < 100; i++)
        {
            var item = new MyItem();
            Result.Add(item);
        }
    }

}


例ではメソッドを全て囲っていますが、ClearやAddのみを対象としたほうがレスポンス良いかなと思います。