0

I've added a binding to an IEnumerable collection, that's populated from a async method call. The data is retrieved from a remote database and then added to the CustomerOrders list.

But after running the application, my UI binding isn't showing on the view. The view shows no data.

In order to debug the issue, I checked the following:

  1. Checked binding and data context by binding to a static list of data.

  2. Debugged the CustomerOrders list, after the data call, which shows as being populated after the method returns.

  3. I also checked the thread name, and it shows as being a "main thread". (Not sure if that could be the reason as it's a different thread.) 3.1.I also implemented native INPC on the CustomerOrders property, and set a breakpoint on set which shows the list is populated. See snapshot.

Does anyone have suggestions on what the issue could be here?

The is a summary of CustomerOrderViewModel, set up as follows. A Task property, Initialization is used to call the initialization code from the constructor:

using MongoDBApp.Models;
using MongoDBApp.Services;
using MongoDBApp.Utility;
using PropertyChanged;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MongoDBApp.Extensions;
using System.Windows.Input;
using MongoDBApp.Common;
using MongoDBApp.Messages;

namespace MongoDBApp.ViewModels
{
    [ImplementPropertyChanged]
    public class CustomerOrdersViewModel : IPageViewModel, INotifyPropertyChanged
    {

        private IDataService<OrderModel> _orderDataService;

        public CustomerOrdersViewModel(IDataService<OrderModel> orderDataService)
        {

            _customerOrders = new List<OrderModel>();
            //{

            //    new OrderModel(){Email = "[email protected]", Status = true}
            //};


            this._orderDataService = orderDataService;
            this._dialogService = dialogservice;

            Messenger.Default.Register<ProductModel>(this, OnUpdateProductMessageReceived);

            this.Initialization = InitializeAsync();

        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }

        #region properties

        public string SelectedCustomerEmail { get; set; }

        private IEnumerable<OrderModel> _customerOrders; 
        public IEnumerable<OrderModel> CustomerOrders 
        { 
           get { return this._customerOrders;}

           set 
           {
               _customerOrders = value;
               OnPropertyChanged("CustomerOrders");           
           }
        }

        public OrderModel SelectedOrder { get; set; }

        public Task Initialization { get; set; }

        #endregion

        #region methods

        private async Task InitializeAsync()
        {
            var customer = await AwaitableMessages.NextMessageAsync<CustomerModel>(); 
            SelectedCustomerEmail = customer.Email;
            await LoadCustomerOrdersAsync(SelectedCustomerEmail);
        }

        public async Task LoadCustomerOrdersAsync(string email)
        {
            var ordersResult = await _orderDataService.GetAllByEmailAsync(email);
            CustomerOrders = ordersResult.ToObservableCollection();
        }
        #endregion
    }
}

This is also the associated view showing the binding setup:

 <UserControl x:Class="MongoDBApp.Views.CustomerOrdersView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:boolean_converter="clr-namespace:MongoDBApp.Converters"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
         d:DesignHeight="300"
         d:DesignWidth="300"
         mc:Ignorable="d">

<UserControl.Resources>
    <boolean_converter:BooleanConverter x:Key="BooleanConverter" />
</UserControl.Resources>

<Viewbox>
    <xctk:BusyIndicator IsBusy="{Binding ButtonEnabled}">

        <Grid>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="2*" />
                    <RowDefinition Height="1*" />
                    <RowDefinition Height="1*" />
                    <RowDefinition Height="1*" />
                    <RowDefinition Height="1*" />
                    <RowDefinition Height="1*" />
                    <RowDefinition Height="1*" />
                    <RowDefinition Height="1*" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="1*" />
                    <ColumnDefinition Width="1*" />
                    <ColumnDefinition Width="2*" />
                    <ColumnDefinition Width="1*" />
                    <ColumnDefinition Width="2*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>


                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Loaded">
                        <i:InvokeCommandAction Command="{Binding WindowLoadedCommand}" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>

                <DataGrid x:Name="customersgrid"
                          Grid.Row="0"
                          Grid.RowSpan="3"
                          Grid.Column="1"
                          Grid.ColumnSpan="4"
                          AutoGenerateColumns="False"
                          ItemsSource="{Binding CustomerOrders}"
                          SelectedItem="{Binding SelectedOrder}">
                    <DataGrid.Columns>
                        <DataGridTextColumn Binding="{Binding Email}" Header="Email" />
                        <DataGridTextColumn Binding="{Binding Date}" Header="Date" />
                        <DataGridTextColumn Binding="{Binding Status}" Header="Shipping Status" />
                    </DataGrid.Columns>
                </DataGrid>
                <Label Grid.Row="4"
                       Grid.Column="1"
                       HorizontalAlignment="Left"
                       VerticalAlignment="Top"
                       Content="Date:" />


                <TextBlock Grid.Row="4"
                           Grid.Column="2"
                           HorizontalAlignment="Left"
                           VerticalAlignment="Top"
                           Text="{Binding SelectedOrder.Date}"
                           TextWrapping="Wrap" />
                <Label Grid.Row="4"
                       Grid.Column="3"
                       HorizontalAlignment="Left"
                       VerticalAlignment="Top"
                       Content="Products:" />
                <ComboBox Grid.Row="4"
                          Grid.Column="4"
                          Grid.ColumnSpan="4"
                          Width="120"
                          HorizontalAlignment="Left"
                          VerticalAlignment="Top"
                          DisplayMemberPath="ProductId"
                          ItemsSource="{Binding SelectedOrder.Products}"
                          ScrollViewer.VerticalScrollBarVisibility="Visible"
                          SelectedItem="{Binding SelectedProduct}" />
                <Label Grid.Row="5"
                       Grid.Column="1"
                       HorizontalAlignment="Left"
                       VerticalAlignment="Top"
                       Content="Email:" />


                <TextBlock Grid.Row="5"
                           Grid.Column="2"
                           HorizontalAlignment="Left"
                           VerticalAlignment="Top"
                           Text="{Binding SelectedOrder.Email}"
                           TextWrapping="Wrap" />
                <RadioButton Grid.Row="5"
                             Grid.Column="3"
                             Grid.ColumnSpan="2"
                             HorizontalAlignment="Left"
                             VerticalAlignment="Top"
                             Content="Shipped"
                             IsChecked="{Binding SelectedOrder.Status,
                                                 Converter={StaticResource BooleanConverter},
                                                 ConverterParameter='true',
                                                 Mode=TwoWay}" />

                <RadioButton Grid.Row="5"
                             Grid.Column="4"
                             Grid.ColumnSpan="2"
                             HorizontalAlignment="Left"
                             VerticalAlignment="Top"
                             Content="Processing"
                             IsChecked="{Binding SelectedOrder.Status,
                                                 Converter={StaticResource BooleanConverter},
                                                 ConverterParameter='false',
                                                 Mode=TwoWay}" />
            </Grid>
        </Grid>
    </xctk:BusyIndicator>
</Viewbox>

2 Answers 2

2

There was nothing visibly wrong with your code; In my attempt to replicate the issue, I managed to do it without any problem. I am publishing my code here, Hope that will help.

My OrderModel class;

public class OrderModel
{
    public string Email { get; set; }
    public DateTime Date { get; set; }
    public string Status { get; set; }
}

My ViewModel (I have used a BaseViewModel; that's optional. The way you have done is ok)

public class MainWindowViewModel:BaseViewModel
{
    public MainWindowViewModel()
    {
        _customerOrders = new List<OrderModel>();
        _customerOrders.Add(new OrderModel(){Date = DateTime.Now, Email = "[email protected]", Status = "Active"});
        InitializeAsync();
    }

    private List<OrderModel> _customerOrders;
    private OrderModel _selectedOrder;

    public List<OrderModel> CustomerOrders
    {
        get { return this._customerOrders; }

        set
        {
            _customerOrders = value;
            OnPropertyChanged("CustomerOrders");
        }
    }

    public OrderModel SelectedOrder
    {
        get { return _selectedOrder; }
        set
        {
            _selectedOrder = value;
            OnPropertyChanged("SelectedOrder");
        }
    }

    private async void InitializeAsync()
    {
        CustomerOrders = await LoadCustomerOrdersAsync();
    }

    private async Task<List<OrderModel>> LoadCustomerOrdersAsync()
    {
       return await Task.Run(() => new List<OrderModel>()
       {
           new OrderModel() {Date = DateTime.Now, Email = "[email protected]", Status = "Active"},
           new OrderModel() {Date = DateTime.Now, Email = "[email protected]", Status = "Active"},
           new OrderModel() {Date = DateTime.Now, Email = "[email protected]", Status = "Active"},
           new OrderModel() {Date = DateTime.Now, Email = "[email protected]", Status = "Active"},
           new OrderModel() {Date = DateTime.Now, Email = "[email protected]", Status = "Active"},
           new OrderModel() {Date = DateTime.Now, Email = "[email protected]", Status = "Active"},
       });
    }
}

My View

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <DataGrid HorizontalAlignment="Left" Margin="31,33,0,0" VerticalAlignment="Top" Height="250" Width="455" ItemsSource="{Binding Path=CustomerOrders, Mode=TwoWay}" SelectedItem="{Binding Path=SelectedOrder, Mode=TwoWay}">
        <!--<DataGrid.Columns>
            <DataGridCheckBoxColumn Binding="{x:Null}" ClipboardContentBinding="{x:Null}" Header="Email" HeaderStringFormat="Email"/>
            <DataGridCheckBoxColumn Binding="{x:Null}" ClipboardContentBinding="{x:Null}" Header="Date" HeaderStringFormat="Date"/>
            <DataGridCheckBoxColumn Binding="{x:Null}" ClipboardContentBinding="{x:Null}" Header="Status" HeaderStringFormat="Status"/>
        </DataGrid.Columns>-->
    </DataGrid>

</Grid>

Code Behind;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainWindowViewModel();
    }
}
Sign up to request clarification or add additional context in comments.

7 Comments

I tried the above and added INPC code to CustomerOrders property. But my UI is still blank, any ideas? gist.github.com/BrianJVarley/e5c64f0faeab9fe85492
Are you sure your async method is returning any results? You can verify that by adding a breakpoint to set method of CustomerOrders. How do you fire PropertyChanged? I can't see that in your code.
Ok. I just had a look at the code in your link. You have not implemented INotifyPropertyChanged. You will not see any results on your view without that.
see snap shot of setter on CustomerOrders property, picpaste.com/pics/inpc-2OFgKX9b.1450361091.PNG It's showing that it is populated. I also re-checked the binding with sample data and that's working. So not sure what the issue is..any ideas? gist.github.com/BrianJVarley/e5c64f0faeab9fe85492
It turned out it wasn't an issue with INPC, that was all working correctly. It's an issue with the call to InitializeAsync, which I'm looking into over the weekend. thanks for the advice besides.
|
0

I came to a solution of placing the async call in the Window loaded Task, instead of the constructor.

Solution: (message handler first registers the passed over value, then the async load method is called from the window loaded Task)

    public CustomerOrdersViewModel(IDataService<OrderModel> orderDataService, IDialogService dialogservice)
    {                  
        this._orderDataService = orderDataService;
        this._dialogService = dialogservice;

        Messenger.Default.Register<CustomerModel>(this, OnUpdateOrderMessageReceived);
        LoadCommands();     
    }

    private void OnUpdateOrderMessageReceived(CustomerModel customer)
    {
        SelectedCustomerEmail = customer.Email;
        IsEnabled = true;                
    }

    private async Task WindowLoadedAsync(object obj)
    {
        await LoadCustomerOrdersAsync(SelectedCustomerEmail);
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.