0

This is a continuation of my previous question Here's my mainwindow.cs

   public partial class MainWindow : Window
{
    ObservableCollection<string> store;
    public MainWindow()
    {
     SerialPort _serialPort = new SerialPort(SerialCom.findCOMPort(), 115200, Parity.None, 8, StopBits.One);
     _serialPort.Handshake = Handshake.None;
     string[] query = new string[3] { "t02", "t03", "t04" };
     store = new ObservableCollection<string> { " ", " ", " " };
     this.DataContext = this;
     Thread thread = new Thread(delegate(){Process(store,query,_serialPort);});
     thread.IsBackground = true;
     try
     {
        thread.Start(); //catch sudden serial port closure exception
     }
     catch (Exception)
     {
        thread.Abort();
     }
   }

 public static void Process(ObservableCollection<string> store, string[] query, SerialPort _serialPort)
 {          
     while (true)
     {             
         for (int i = 0; i < 3; i++)
         {
             string add = SerialCom.returnData(query[i], _serialPort);
             if (store[i] != add)
             {
                 store.Insert(i,add);
             }
         }             
         Thread.Sleep(300);

     }
 }

I have a WPF listview control in my XAML page. I would like to bind that control to the ObservableCollection being changed in the non UI thread. It seems like the fly in the ointment is the infinite loop I have to query the device while the program is running.In addition, since ObservableCollection implements INotifyPropertyChanged, when I bind the collection to the control, the control should automatically update without any other code in code behind or XAML?

Community
  • 1
  • 1
bakedpatato
  • 105
  • 5
  • 16
  • 1
    When you bind the observablecollection to the listview, it is owned by the UI thread. If you want to update the collection, I think you need to use Dispatcher.BeginInvoke and all should be well. – MrEdge Oct 31 '12 at 16:00

2 Answers2

1

If you're using the ObservableCollection from the binding system, you are not allowed to update it from a background thread. This is because the update notifications (via INotifyCollectionChanged) occur on the thread that makes the changes. Changes to the ObservableCollection must be dispatched to the UI thread.

Dan Bryant
  • 27,329
  • 4
  • 56
  • 102
1

There's a good solution in 4.5 using the BindingOperations class: http://10rem.net/blog/2012/01/16/wpf-45-observable-collection-cross-thread-change-notification

For earlier versions, you'll either need to update the collection on the dispatcher thread, or push the change notification events to the dispatcher thread: http://julmar.com/blog/mark/?p=41

Richard Deeming
  • 29,830
  • 10
  • 79
  • 151
  • Exactly what I needed. Thankfully I can distribute with net 4.5! – bakedpatato Oct 31 '12 at 16:36
  • Ok, I tried making my list view's ItemSource "store" via codebehind but it throws "TargetInvocationException", InnerException being "NullReferenceException" Here's my new code `store = new ObservableCollection { "a", "b", "c" }; BindingOperations.EnableCollectionSynchronization(store, _lock); //allows for cross thread binding Thread thread = new Thread(delegate(){Process(store,query,_serialPort);}); thread.IsBackground = true; listview1.ItemsSource = store;` – bakedpatato Oct 31 '12 at 17:20
  • The code doesn't come out too well in the comments. The most obvious thing to check would be that you've set the `_lock` object before calling the `EnableCollectionSynchronization` method.. – Richard Deeming Oct 31 '12 at 17:54
  • What's strange is that I get the exact same exception if I comment out the BindingOperations call._lock is newed before I call the method. I made sure that the ObservableCollection has some values before I set it to be the ItemsSource. Would the problems listed in [this](http://10rem.net/blog/2012/01/20/wpf-45-cross-thread-collection-synchronization-redux) be relevant? – bakedpatato Oct 31 '12 at 18:17
  • Sounds like it could be, particularly the need to lock your own access to the collection. Try adding a `lock(_lock){...}` block around the `if (store[i] != add) { store.Insert(i, add); }` block. – Richard Deeming Oct 31 '12 at 18:25
  • I found that putting the ItemSource call in a WindowLoaded event solved this. I found while debugging that MainWindow would get to the ItemSource call before drawing the windows(which is a bit obvious but still!). – bakedpatato Oct 31 '12 at 18:46