开发者

Silverlight 4 + WCF with MVVM: Collection won't get populated for some reason

开发者 https://www.devze.com 2023-03-22 15:36 出处:网络
Let me show the code first. WCF servicecontract function: public List<VenueData> GetVenues() { List<VenueData> listOfVenues = new List<VenueData>();

Let me show the code first.

WCF servicecontract function:

   public List<VenueData> GetVenues()
    {
        List<VenueData> listOfVenues = new List<VenueData>();

        string connString = @"....";

        DataContext dc = new DataContext(connString);
        Table<VenueData> venues = dc.GetTable<VenueData>();

        listOfVenues = (from v in venues
                       select v).ToList();

        return listOfVenues;
    }

VenueViewModel.cs

public class VenueViewModel : ViewModelBase
{
    private VenueData _venue;
    private ObservableCollection<VenueData>开发者_如何学Python _venues = new ObservableCollection<VenueData>();        

    public VenueData Venue
    {
        get
        {
            return _venue;
        }
        set
        {
            if (_venue != value)
            {
                _venue = value;
                OnNotifyPropertyChanged("Venue");
            }
        }
    }

    public ObservableCollection<VenueData> Venues
    {
        get
        {
            return _venues;
        }
        set
        {
            if (_venues != value)
            {
                _venues = value;
                OnNotifyPropertyChanged("Venues");
            }
        }
    }

    public void GetAllVenues()
    {

        TicketOrderWcfClient toClient = new TicketOrderWcfClient();
        toClient.GetVenuesCompleted += new EventHandler<GetVenuesCompletedEventArgs>(toClient_GetVenuesCompleted);
        toClient.GetVenuesAsync();            
    }

    void toClient_GetVenuesCompleted(object sender, GetVenuesCompletedEventArgs e)
    {
        if (e.Error == null)
            Venues = e.Result;
    }
}

MainPage.xaml(view)

   public MainPage()
    {
        InitializeComponent();
        Loaded += new RoutedEventHandler(MainPage_Loaded);
    }

    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        VenueViewModel vvm = new VenueViewModel();
        vvm.GetAllVenues();

        MessageBox.Show(vvm.Venues.Count.ToString());

    }

Well this is most of the code. The problem is that in the MainPage_Loaded event vvm.GetAllVenues() will not populate the Venues ObservableCollection. The MessageBox will show 0. I tested the wcf service is good, also fiddler showed the soap fine. Also if i call the wcf service in the MainPage_Loaded event, then it will work. See below:

    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {

        TicketOrderWcfClient toClient = new TicketOrderWcfClient();
        toClient.GetVenuesCompleted += new EventHandler<GetVenuesCompletedEventArgs>(toClient_GetVenuesCompleted);
        toClient.GetVenuesAsync();  
    }


    void toClient_GetVenuesCompleted(object sender, GetVenuesCompletedEventArgs e)
    {
        if(e.Error == null)
        {
            VenueViewModel vvm = new VenueViewModel();
            vvm.Venues = e.Result;
            MessageBox.Show(vvm.Venues.Count.ToString());
        }
    }

This time MessageBox will show 3, which is good because there are 3 records in the db. So it looks like there is a problem between the View and the ViewModel. I suspect i am missing a pretty basic thing here. Also note that i know this is not true MVVM, but i have to accomplish this program this way. I hope my explanation is clear, thank you for your help.


In your first approach, which does not work, the flow of the code:

MessageBox.Show(vvm.Venues.Count.ToString());

is not connected with the termination of your asynchronous call of the WCF method! In other word, you show the mbox, but you do not make sure that the asynchronous call has terminated.

Of course, you had assigned a delegate to GetVenuesCompleted event, but the call of toClient.GetVenuesAsync() is an asynchronous call, which means that when invoked it does not wait for the result (termination). So when you call:

vvm.GetAllVenues();

Then

MessageBox.Show(vvm.Venues.Count.ToString());

is invoked faster then your toClient_GetVenuesCompleted delegate.

Your second approach works because you show the messagebox when the asynchronous method is completed (in your GetVenues callback).

In my opinion, you can repair it for example by adding a new event GetAllVenuesCompleted in VenueViewModel which will be fired at the end of the toClient_GetVenuesCompleted delegate. In other words, I would pass this event further. Additionally I would add a comment that GetAllVenues is an asynchronous method.


I guess the problem is, that you are creating new VM.

void toClient_GetVenuesCompleted(object sender, GetVenuesCompletedEventArgs e)
{
    if(e.Error == null)
    {
        VenueViewModel vvm = new VenueViewModel();
        vvm.Venues = e.Result;
        MessageBox.Show(vvm.Venues.Count.ToString());
    }
}

I think this code works. You create new VenueViewModel, fill it with data and that is all. This VM is never used again. The same rules for MainPage.xaml sample.
You should create global variable of VenuViewModel in constructor/loaded handler and call its GetAllVenues in Loaded event

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号