Issues with ImportConstructor in WinRT

Sep 12, 2012 at 4:49 AM

I'm trying to simulate the Standard ItemDetailView where you can browse thru the Items one by one, so I wanted to use the same datasource when a user clicks the item in the group view. The problem I have it that it does not share my DataSource, it re-creates it every time.

The code is posted below. when I click on a customer in the listview and it calls my constructor on the DetailViewModel it calls the Constructor of the DataSource even though the ListView just used the same DataSource.

I use this code to call the DetailView from my groups (As this is called it calls the constructor on the DataSource again):

        private void itemGridView_ItemClick(object sender, ItemClickEventArgs e)
        {
            ((CustomersViewModel)this.DataContext).NavigationManager.NavigateTo("CustomerDetail", e.ClickedItem);
        }

My DataSource :

    [Export(typeof(ICustomerDataSource))]
    [Shared]
    public class CustomerDataSource : ICustomerDataSource
    {
        private Api _api;
        private IList<Customer> _customers;
        private CustomerDataListSource _dataListSource;

        public CustomerDataSource()
        {
            _api = new Api();
            _dataListSource = new CustomerDataListSource(_api);
        }

        public IList<Customer> GetCustomers()
        {
            // Create a single instance of the customer list
            // This can then be shared between multiple view models
            if (_customers == null)
            {
                _customers = (IList<Customer>)new VirtualizingDataList<Customer>(_dataListSource);
            }

            return _customers;
        }
    }

My DetailViewModel (when it calls this.Customers = dataSource.GetCustomers(); the _customers is null):

    public class CustomerDetailViewModel : BindableBase, IActivatable<Customer, string>
    {
        private Customer _selectedCustomer;
        private IList<Customer> _customers;

        protected CustomerDetailViewModel()
        {
            this.GoBackCommand = new DelegateCommand(GoBack);

            CustomerDataSource dataSource = new CustomerDataSource();
            this.Customers = dataSource.GetCustomers();
        }

        [ImportingConstructor]
        public CustomerDetailViewModel(ICustomerDataSource dataSource)
        {
            this.GoBackCommand = new DelegateCommand(GoBack);

            this.Customers = dataSource.GetCustomers();
        }

        [Import]
        public INavigationManager NavigationManager { get; set; }

        public ICommand GoBackCommand { get; private set; }

        public bool CanGoBack
        {
            get { return NavigationManager.CanGoBack; }
        }

        public void GoBack()
        {
            NavigationManager.GoBack();
        }

        public void Activate(Customer arguments, string state)
        {
            if (arguments == null)
                this._selectedCustomer = this.Customers.FirstOrDefault();
            else
                this._selectedCustomer = arguments;
        }

        public string SaveState()
        {
            return null;
        }

        public Customer SelectedCustomer
        {
            get 
            {
                if (this._selectedCustomer == null)
                {
                    this.SelectedCustomer = this.Customers.FirstOrDefault();
                }

                return this._selectedCustomer; 
            }
            set
            {
                if (_selectedCustomer != value)
                {
                    _selectedCustomer = value;
                    SetProperty(ref _selectedCustomer, value);
                }
            }
        }

        public IList<Customer> Customers
        {
            get
            {
                return _customers;
            }
            set
            {
                if (_customers != value)
                {
                    _customers = value;
                    SetProperty(ref _customers, value);
                }
            }
        }
    }

 

 

Sep 12, 2012 at 5:38 AM

I think I figured it out. I added the following Constructor to the CustomersViewModel (the group page which calls the detail) and it seems to work now, but is this standard behavior? The previous constructor was just the default constructor. Why would this make a difference?

        [ImportingConstructor]
        public CustomersViewModel(ICustomerDataSource dataSource)
        {
            this.GoBackCommand = new DelegateCommand(GoBack);

            this.Customers = dataSource.GetCustomers();
        }

Previous constructor:

        public CustomersViewModel()
        {
            this.GoBackCommand = new DelegateCommand(GoBack);
            
            CustomerDataSource dataSource = new CustomerDataSource();
            this.Customers = dataSource.GetCustomers();
        }

 

Sep 12, 2012 at 10:43 PM

Hi there,

This change makes sense - MEF can only control (and share) parts that you import. In the first version, you create a new datasource with the 'new' operator:

CustomerDataSource dataSource = new CustomerDataSource();

MEF does't change the meaning of 'new', so with this line of code you'd get a brand new data source every time.

Hope this helps!

Nick