Question

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

Was it helpful?

Solution

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);
}));
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top