4

ボタン クリック ハンドラで ObservableCollection の .Clear() を呼び出すと、断続的にこのエラーが発生します。他の TPL タスクを実行しているにもかかわらず、すべて ObservableCollection を次の方法で更新しているため、混乱しています。

mainWindow.Dispatcher.Invoke(new Action(delegate()
{
 this.myCollection.Add(myItem);
}));

すべての操作はメイン UI スレッドでアトミックになるため、本質的にディスパッチャーは ObservableCollection スレッド セーフにならないのでしょうか? ところで: DispatcherTimer のコレクション メンバーのいくつかのフィールドも更新していますが、timer.tick ハンドラーでコレクションのアイテムを削除または追加していません。

ジェームズ:

問題を特定するためにサンプル アプリを作成しますが、サンプルはすべて正常に動作します。したがって、私のアプリの他の何かが、別のスレッドでコレクションの変更をトリップしているに違いありません。これはすべて正常に機能します。エラーなしでクリアできます。

<Window x:Class="WpfApplication1.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>
    <Button Name="clear" Height="23" Click="clear_Click"></Button>
    <ListBox ItemsSource="{Binding MyCollection}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Name}"></TextBlock>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</StackPanel>

    public partial class MainWindow : Window
{
    private DispatcherTimer myTimer = null;

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
        Loaded +=new RoutedEventHandler(MainWindow_Loaded);
    }

    private bool isLoaded = false;
    void  MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        if (isLoaded) return;
        isLoaded = true;

        var task = new Task(() => AddObjectsTask());
        task.Start();


        myTimer = new DispatcherTimer();
        myTimer.Interval = new TimeSpan(0, 0, 1);
        myTimer.Tick += new EventHandler(myTimer_Tick);
        myTimer.Start();

        _MyCollection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_MyCollection_CollectionChanged);

    }

    void _MyCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
        {
            var query = (from n in _MyCollection select n).ToList();
            if (query.Any())
            {
                foreach (SomeObject o in query)
                {
                    o.Name = (Convert.ToInt64(o.Name) + 1).ToString();
                }
            }
        }
    }

    void myTimer_Tick(object sender, EventArgs e)
    {
        this._MyCollection.Add(new SomeObject() { Name = 99.ToString() }); 
    }

    void AddObjectsTask()
    {
        int i = 0;
        while(true)
        {
        Thread.Sleep(10);
        this.Dispatcher.Invoke(new Action(delegate() 
        { 
            this._MyCollection.Add(new SomeObject() { Name = i.ToString() }); 
        })); 
            i++;
        }
    }


    private void clear_Click(object sender, RoutedEventArgs e)
    {
        _MyCollection.Clear();
    }

    private ObservableCollection<SomeObject> _MyCollection = new ObservableCollection<SomeObject>();
    public ObservableCollection<SomeObject> MyCollection
    {
        get
        {
            return _MyCollection;
        }
    }

}

public class SomeObject : INotifyPropertyChanged
{
    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    public void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
    #endregion

    private string _Name = string.Empty;

    public string Name
    {
        get
        {
            return _Name;
        }
        set
        {
            _Name = value;
            NotifyPropertyChanged("Name");
        }
    }
}
4

0 に答える 0