WPF C# ObservableCollection Not updating GUI

10,036

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);
}));
Share:
10,036
Daniel Lane
Author by

Daniel Lane

Software engineer and web developer by day, inefficient space heater by night.

Updated on July 21, 2022

Comments

  • Daniel Lane
    Daniel Lane almost 2 years

    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

  • Daniel Lane
    Daniel Lane about 10 years
    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.