5

Thanks to the advice of tencntraze this question has been solved.

See the bottom of this question and tencntraze's answer for the solution.

I have a rather strange bug in my program that I'm currently wrestling with.

I'm developing an MVVM application, and I have a simple ListBox in my View with its ItemsSource bound to an ObservableCollection of strings. Whenever I add an item to this collection, it /does/ fire an event to notify that the collection has changed, yet this isn't reflected on the GUI during run-time unless I attempt to manually resize the window as the program is running, almost as if a resizing the window forces the GUI to refresh.

The XAML for the relevant control is as follows

<ListBox ItemsSource="{Binding Path=LogWindow}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="0,0,0,0" />

Which as you can see is perfectly normal. In my ViewModel for this view, I have a public property called LogWindow defined as follows

public ObservableCollection<string> LogWindow
    {
        get
        {
            return _logWindow;
        }

        set
        {
            _logWindow = value;
            OnPropertyChanged("LogWindow");
        }
    }

The private member for this is as follows

private ObservableCollection<string> _logWindow;

In the ViewModel constructor I initialize _logWindow and wire up LogWindow CollectionChanged event to a handler which in turn fires a PropertyChanged notification.

My code for this is as follows

public MainWindowViewModel(IDialogService dialogService)
        {
            ... skipped for brevity
            LogWindow = new ObservableCollection<string>();
            LogWindow.CollectionChanged += LogWindow_CollectionChanged;
            ...
        }

The CollectionChanged event handler is as follows

void LogWindow_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            OnPropertyChanged("LogWindow");
        }

And finally when my View receives notification that it has received a new message from the Model, I add a new string to the observable collection as follows

LogWindow.Add(_vehicleWeightManager.SerialMessage);

When LogWindow.Add happens, my event listener fires and my breakpoint is hit to confirm the code gets that far as illustrated below Break Point is Hit upon Adding a new string to the collection... Notification Collection Changed Event args

After this my code calls the OnPropertyChanged function which is inherited from my ViewModelBase, this functions fine for my other GUI elements such as labels and the like, so that almost certainly isn't the issue.

I applied a watch to LogWindow to keep track that the collection was indeed being added to, and it is as illustrated below LogWindow watch

So at this stage I'm at a loss. My GUI will only update if I resize the window in some way, otherwise there is no visual update. The data is present in the bound property, and afaik the GUI is alerted when a new item is added to the property... Does anyone have any insight that may help?

Thanks to the advice of tencntraze and kallocain this has been solved.

The problem was caused by another thread attempting to add to the LogWindow collection which was causing erroneous behaviour. Rather than use Dispatcher as suggested, I used a TaskFactory code as follows.

TaskFactory uiFactory;
...
...
public MainWindowViewModel(IDialogService dialogService)
        {
            ...
            _logWindow = new ObservableCollection<string>();
            uiFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
        }
...
...
void _vehicleWeightManager_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            ...
            if (e.PropertyName == "VehicleWeight")
            {
                // Marshal the SerialMessage property to the GUI thread.
                uiFactory.StartNew(() => LogWindow.Add(_vehicleWeightManager.SerialMessage.Replace("\n", ""))).Wait();
            }
        }

Further reading for the above method can be found here

Community
  • 1
  • 1
Daniel Lane
  • 2,575
  • 2
  • 18
  • 33
  • 1
    Just an advice: remove the collection changed handler its unecessary. You basically destroy the whole purpose of the ObservableCollection by telling WPF "Hey i added something to the list, so please get the whole list again". An observable list will raise the proper events, no need to raise that the list itself is changed. This is used when you actually use a completely different list reference wise. – dowhilefor Mar 04 '14 at 15:47
  • You don´t need to raise `PropertyChanged` for LogWindow, just because an element has been added. The ListBox is aware, that it has been bound to an `ObservableCollection` and will subsribe the `CollectionChanged` event of the collection. But that that should not cause your problem. Can you describe, when and how `LogWindow.Add` is called? What does "when my View receives notification" mean? – Thomas Hetzer Mar 04 '14 at 15:52
  • Thanks for the tip, I did it as above with the vague hope that it'd force the GUI to update, initially I had it set up as you suggested haha. – Daniel Lane Mar 04 '14 at 15:52
  • Kallocain, I have another class that deals with serial communications on a serial com port, I have subscribed to a property changed event in the WeightManager class, the variable for that is called _vehicleWeightManager. When the SerialMessage property of the WeightManager class instantiation changes, the view model picks up on this and adds the message it received into an observable collection. – Daniel Lane Mar 04 '14 at 15:56
  • This lends more evidence to my theory that you may need to invoke the update to your collection on the UI thread, I'd give that a shot. – Matt Mar 04 '14 at 15:57
  • I'm giving that a shot now just in case, it can't hurt to try. If you don't mind my asking though, why do you believe it's on another thread? I actually don't spawn any additional threads anywhere in the program. Could it be caused by the way the CLR deals with the program at run time? – Daniel Lane Mar 04 '14 at 15:59
  • Yep, serial communication often involves background threads. Have you tried the solution proposed by tencntraze? – Thomas Hetzer Mar 04 '14 at 16:02
  • Indeed, I have actually just tried this my self, not using dispatcher though. I used a TaskFactory to achieve the same thing. And I'll be damned it did indeed work. I didn't think that the .Net Serial Port class was abstracting away some threaded operations. I should have known. – Daniel Lane Mar 04 '14 at 16:06

1 Answers1

5

If you're receiving the message on another thread, you'll need to invoke it back to the UI thread, otherwise there can be unpredictable behavior. Sometimes this is an exception, other times it works out to do nothing.

this.Dispatcher.BeginInvoke(new Action(() =>
{
    LogWindow.Add(_vehicleWeightManager.SerialMessage);
}));
Matt
  • 2,682
  • 1
  • 17
  • 24
  • Tencntraze, you were absolutely correct in your assumption. As discussed in the comments above, it was caused by threaded operations in the Serial Communication API's included in .Net being abstracted away. I really appreciate the suggestion because it solved the problem I was having there. For the record, I used a TaskFactory in place of dispatcher. I set it's TaskScheduler to the Current Synchronization context to do the same thing, then where I add to the Collection, I started a new task to add to the LogWindow property. – Daniel Lane Mar 04 '14 at 16:10