MVVM Event Aggregator and WCF Service. Not updating The UI

Topics: Prism v4 - Silverlight 4
Oct 30, 2012 at 4:24 AM

Calling this piece of code from the Event Aggregator does not work, the collection does not update the UI. Calling the same code from the UI using a button click works?

I think it has to be an issue with the thread

//UI 

private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            ((TopMenuViewModel)t).OnLoggedIn(true);
        }


//View Model

 _eventAggregator.GetEvent<Chameleon.Common.CommonAgent.IsLoggedIn>().Subscribe(OnLoggedIn, ThreadOption.UIThread, false);

public void OnLoggedIn(bool e)
        {
            BusinessService.NavigationBusiness bus = new BusinessService.NavigationBusiness();
            
            bus.GetMenuHeaderCompleted += (svr) =>
            {
                if (svr != null)
                {
                    _dispatcher.BeginInvoke(() =>
                        {
                            PickableCollection.Clear();
                            PickableCollection = (ObservableCollection<NavigationMenuHeader>)svr;
                        });
                }
            };

            bus.GetMenuHeader(1);
        }

Can anybody help thanks.

Developer
Oct 30, 2012 at 1:49 PM

Hi,

I believe you could check that the setter of your PickableCollection is calling for example the RaisePropertyChanged method in order to notify the view the state changes of this property, perhaps this could be related to your problem.

On the other hand, if this is not the case, it would be helpful to know if your the subscriber of the event is being successfully called when publishing to it and also it's not clear if when calling the OnLoggedIn method from the code behind of the view worked for you.

Regards,

Agustin Adami
http://blogs.southworks.net/aadami
,

Oct 30, 2012 at 2:20 PM

RaisePropertyChanged is being hit but the not notifying the view the state changes

 this.RaisePropertyChanged(() => this.PickableCollection);

 

Yes the OnLogged event is also being hit, i can see the webservice being called and the new collection being returned.

My xaml is below, i can put breakpoints on the bindings which get hit when i call the button click. But not when OnLoggedIn event is called.

 <Button Click="Button_Click_1" Height="30" Width="40" Content="Tester" />

<ListBox BorderBrush="Transparent" BorderThickness="0"  VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ItemsSource="{Binding PickableCollection}"
    Margin="0,0,0,0"   ScrollViewer.HorizontalScrollBarVisibility="Disabled">
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal" />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Foreground="Black" Text="{Binding Path=txtLegend}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

Oct 30, 2012 at 2:22 PM
Edited Oct 30, 2012 at 2:23 PM

My ViewModel

namespace Chameleon.Navigation.ViewModel
{
    public class TopMenuViewModel : NotificationObject
    {
        IEventAggregator _eventAggregator;
        Dispatcher _dispatcher;
        public DelegateCommand<object> LoadMenuCommand { get; private set; }

        ObservableCollection<NavigationMenuHeader> pickableCollection;

        public TopMenuViewModel(IEventAggregator evt, IRegionManager regionManager, Dispatcher dis)
        {
            this._eventAggregator = evt;
            this._dispatcher = dis;
     
            PickableCollection = new ObservableCollection<NavigationMenuHeader>();

            //Broadcast logging event 
            Chameleon.Common.CommonAgent.IsLoggedIn loggingIn = _eventAggregator.GetEvent<Chameleon.Common.CommonAgent.IsLoggedIn>();
            _eventAggregator.GetEvent<Chameleon.Common.CommonAgent.IsLoggedIn>().Subscribe(OnLoggedIn, ThreadOption.UIThread, false);

        }

        public ObservableCollection<NavigationMenuHeader> PickableCollection
        {
            get { return this.pickableCollection; }
            set
            {
                this.pickableCollection = value;
                this.RaisePropertyChanged(() => this.PickableCollection);
               // OnPropertyChanged("PickableCollection");

            }
        }

        public void OnLoggedIn(bool e)
        {
            BusinessService.NavigationBusiness bus = new BusinessService.NavigationBusiness();
            
            bus.GetMenuHeaderCompleted += (svr) =>
            {
                if (svr != null)
                {
                    _dispatcher.BeginInvoke(() =>
                        {
                            PickableCollection.Clear();
                            PickableCollection = (ObservableCollection<NavigationMenuHeader>)svr;
                        });
                }
            };

            bus.GetMenuHeader(1);
        }
Oct 30, 2012 at 2:24 PM

My User Control

namespace Chameleon.Navigation.Views
{
    [Export("TopMenuView")]
    public partial class TopMenuView : UserControl, INavigationAware 
    {
        [ImportingConstructor]
        public TopMenuView(IEventAggregator evt, IRegionManager regionManager)
        {
            InitializeComponent();

            _regionManager = regionManager;
            _eventManager = evt;
        }

        [Import]
        public IRegionManager _regionManager;

        [Import]
        public IEventAggregator _eventManager;

        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            return true;
        }

        public void OnNavigatedFrom(NavigationContext navigationContext)
        {

        }

        public void OnNavigatedTo(NavigationContext navigationContext)
        {
            this.DataContext = new TopMenuViewModel(_eventManager, _regionManager, this.Dispatcher);
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            ((TopMenuViewModel)this.DataContext).OnLoggedIn(true);
        }
    }
}
Developer
Oct 30, 2012 at 6:47 PM

Hi,

I tried to reproduce the behavior you are mentioning based on the code snippets you provided and so far I couldn't re-create it. I tried using both approaches, directly calling the OnLoggedIn handler in the code behind of the view and subscribing it through the Event Aggregator. In both cases the UI was updated correctly.

Perhaps it would be helpful if you could provided us with a repro sample application portraying this behavior so we can analyze this in further detail.

On the other hand, one thing I notice is that if you subscribe to the event passing the ThreadOption.UIThread parameter, as far as I know you shouldn't need to call the Dispatcher in the handler of this event as the event will be received in the UI thread and not in the publisher's thread.

Regards,

Agustin Adami
http://blogs.southworks.net/aadami
,