My second module has an intermittent behavior

Aug 22, 2012 at 3:47 AM
Edited Aug 22, 2012 at 8:34 AM

I'm using SL5 and Prism 4.1.

I have two modules: CustomersModule and OrdersModule.

The first has a CustomersView that shows customers and the second module has a OrdersView that shows the selected customer's orders.

The shell has two regions, LeftRegion (occupied by CustomersView) and RightRegion (ocuppied by OrdersView).

When the selected customer changes a CustomerSelectedEvent is published, the OrdersModule is subscribed to it and when received it loads the OrdersViewModel with the new customer.

At first load I expect that the first customer gets selected, which in turn have to make the OrdersView to show that first customer's orders.

Sometimes the OrdersView show the first customers orders at data load and others times it doesn't show anything at first load, but if I click on a customer, the solution's behavior is correct from then on.

After I restart the computer the OrdersView doesn't show any order at first load, then I close the IE then run it again and from then on the first customer's orders are correctly shown at data load every time I run it, I can't detect what's the cause of that unpredictable behavior.

At data load I mean before clicking anything on the Views when running.

This is how it looks when it doesn't load the orders at data load:

And this is how it looks when it does load the orders at data load:

Here's the CustomersViewModel:

public class CustomersViewModel : INotifyPropertyChanged {

    private readonly IEventAggregator eventAggregator;
    MiniNorthwindDomainContext _Context = new MiniNorthwindDomainContext();

    public CustomersViewModel() {
        if (!DesignerProperties.IsInDesignTool) {
            this.eventAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>();

            EntityQuery<Customer> queryCustomers = _Context.GetCustomersQuery();
            LoadOperation<Customer> loadOpCustomers = _Context.Load(queryCustomers);
            loadOpCustomers.Completed += new EventHandler(loadOpData_Completed);

            EntityQuery<Country> queryCountries = _Context.GetCountriesQuery();
            LoadOperation<Country> loadOpCountries = _Context.Load(queryCountries);
            loadOpCountries.Completed += new EventHandler(loadOpData_Completed);

            EntityQuery<Title> queryTitles = _Context.GetTitlesQuery();
            LoadOperation<Title> loadOpTitles = _Context.Load(queryTitles);
            loadOpTitles.Completed += new EventHandler(loadOpData_Completed);
        }
    }

    void loadOpData_Completed(object sender, EventArgs e) {
        Customers = new PagedCollectionView(_Context.Customers);
        Customers.CurrentChanged += new EventHandler(Customers_CurrentChanged);

        Countries = _Context.Countries;

        Titles = _Context.Titles;

        CurrentCustomer = _Context.Customers.FirstOrDefault();
        if (CurrentCustomer != null) {
            this.eventAggregator.GetEvent<CustomerSelectedEvent>().Publish(CurrentCustomer);
        }
    }

    void Customers_CurrentChanged(object sender, EventArgs e) {
        CurrentCustomer = this.Customers.CurrentItem as Customer;
        if (CurrentCustomer != null) {
            this.eventAggregator.GetEvent<CustomerSelectedEvent>().Publish(CurrentCustomer);
        }
    }

    PagedCollectionView _Customers;
    public PagedCollectionView Customers {
        get {
            return _Customers;
        }
        set {
            if (value != _Customers) {
                _Customers = value;
                NotifyPropertyChanged("Customers");
            }
        }
    }

    IEnumerable<Country> _Countries;
    public IEnumerable<Country> Countries {
        get {
            return _Countries;
        }
        set {
            if (value != _Countries) {
                _Countries = value;
                NotifyPropertyChanged("Countries");
            }
        }
    }

    IEnumerable<Title> _Titles;
    public IEnumerable<Title> Titles {
        get {
            return _Titles;
        }
        set {
            if (value != _Titles) {
                _Titles = value;
                NotifyPropertyChanged("Titles");
            }
        }
    }

    private Customer _currentCustomer;
    public Customer CurrentCustomer {
        get {
            return this._currentCustomer;
        }
        set {
            this._currentCustomer = value;
            this.NotifyPropertyChanged("CurrentCustomer");
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string propertyName) {
        if (this.PropertyChanged != null) {
            this.PropertyChanged(this, new
                                        PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion

Here's OrdersModule class:

public class OrdersModule : IModule {
    private readonly IEventAggregator eventAggregator;
    private readonly IUnityContainer container;
    private readonly IRegionManager regionManager;

    public OrdersModule(IUnityContainer container, IRegionManager regionManager, IEventAggregator eventAggregator) {
        this.container = container;
        this.regionManager = regionManager;
        this.eventAggregator = eventAggregator;
    }

    public void Initialize() {
        this.eventAggregator.GetEvent<CustomerSelectedEvent>().Subscribe(CustomerSelected, true);

        this.regionManager.RegisterViewWithRegion("RightRegion",
                                                    () =>
                                        this.container.Resolve<OrdersView>());
    }

    private void CustomerSelected(Customer currCustomer) {
        if (currCustomer == null)
            return;

        IRegion rightRegion = this.regionManager.Regions["RightRegion"];
        if (rightRegion == null)
            return;

        OrdersView view = rightRegion.GetView("OrdersView") as OrdersView;
        if (view == null) {
            // Create a new instance of the EmployeeDetailsView using the Unity container.
            view = this.container.Resolve<OrdersView>();

            // Add the view to the main region. This automatically activates the view too.
            rightRegion.Add(view, "OrdersView", true);
        }
        else {
            // The view has already been added to the region so just activate it.
            rightRegion.Activate(view);
        }

        OrdersViewModel viewModel = view.DataContext as OrdersViewModel;
        if (viewModel != null) {
            viewModel.CurrentCustomer = currCustomer;
        }
    }
}

Heres OrdersViewModel:

public class OrdersViewModel : INotifyPropertyChanged {

    MiniNorthwindDomainContext _Context = new MiniNorthwindDomainContext();

    public OrdersViewModel() {
    }

    PagedCollectionView _Orders;
    public PagedCollectionView Orders {
        get {
            return _Orders;
        }
        set {
            if (value != _Orders) {
                _Orders = value;
                NotifyPropertyChanged("Orders");
            }
        }
    }

    private Order _currentOrder;
    public Order CurrentOrder {
        get {
            return this._currentOrder;
        }
        set {
            this._currentOrder = value;
            this.NotifyPropertyChanged("CurrentOrder");
        }
    }

    private Customer _currentCustomer;
    public Customer CurrentCustomer {
        get {
            return this._currentCustomer;
        }
        set {
            this._currentCustomer = value;
            Orders = null;
            _Context.Orders.Clear();
            EntityQuery<Order> queryOrders = _Context.GetCustomerOrdersQuery(_currentCustomer.CustomerID);
            LoadOperation<Order> loadOpOrders = _Context.Load(queryOrders);
            loadOpOrders.Completed += new EventHandler(loadOpData_Completed);
            this.NotifyPropertyChanged("CurrentCustomer");
        }
    }

    void loadOpData_Completed(object sender, EventArgs e) {
        Orders = new PagedCollectionView(_Context.Orders);
        CurrentOrder = _Context.Orders.FirstOrDefault();
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string propertyName) {
        if (this.PropertyChanged != null) {
            this.PropertyChanged(this, new
                                        PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion
}

Here's the project:

https://skydrive.live.com/#cid=228822BB183339A9&id=228822BB183339A9%21234

I have been advancing but this little problem has been around for quite a while.

Rafael

Developer
Aug 22, 2012 at 5:12 PM
Edited Aug 22, 2012 at 5:14 PM

Hi,

Based on my understanding, the cause of this problem is that when you initialize the application two instances of OrdersView are added to the "RightRegion", this is because in your OrdersModule one of this instances is added using the view discovery approach, which does not set the instance with a viewName in the region, hence the GetView method always return null causing a new instance to be added to the region. As a result your region is populated with two OrdersView views, one that is empty and one with the data of the current customer, and due to a timing problem, sometimes your empty view is the one that gets activated.

In order to avoid this, for example you could try removing the adding of the first view using view discovery approach in your OrdersModule, as it seems that this view is not used in your application:

public class OrdersModule : IModule {
       
(...)

        public void Initialize() {
            this.eventAggregator.GetEvent<CustomerSelectedEvent>().Subscribe(CustomerSelected, true);

            //Remove this sentence:       
            //this.regionManager.RegisterViewWithRegion("RightRegion",
            //                                            () =>
            //                             this.container.Resolve<OrdersView>());
        }
(...)

I hope you find this useful,

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

Aug 22, 2012 at 5:45 PM

Thank you Agustin!