Silverlight 4 + WCF 与 MVVM:由于某种原因,集合不会被填充
让我先展示一下代码。
WCF 服务契约函数:
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> _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());
}
这是大部分代码。问题是在 MainPage_Loaded 事件中 vvm.GetAllVenues() 不会填充 Venues ObservableCollection。 MessageBox将显示0。我测试了wcf服务良好,fiddler也显示soap良好。另外,如果我在 MainPage_Loaded 事件中调用 wcf 服务,那么它将起作用。如下所示:
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());
}
}
这次 MessageBox 将显示 3,这很好,因为数据库中有 3 条记录。所以看起来 View 和 ViewModel 之间存在问题。我怀疑我在这里遗漏了一件非常基本的事情。另请注意,我知道这不是真正的 MVVM,但我必须以这种方式完成此程序。 我希望我的解释很清楚,谢谢您的帮助。
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> _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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在您的第一种方法中,该方法不起作用,代码流:
与 WCF 方法的异步调用的终止无关!换句话说,您显示了 mbox,但不确定异步调用是否已终止。
当然,您已为 GetVenuesCompleted 事件分配了委托,但 toClient.GetVenuesAsync() 的调用是异步调用,这意味着调用时它不会等待结果(终止)。因此,当您调用:
Then 时
,调用速度比您的 toClient_GetVenuesCompleted 委托更快。
您的第二种方法有效,因为您在异步方法完成时显示消息框(在 GetVenues 回调中)。
在我看来,您可以通过在 VenueViewModel 中添加一个新事件 GetAllVenuesCompleted 来修复它,该事件将在 toClient_GetVenuesCompleted 委托末尾触发。换句话说,我会进一步通过这个事件。另外,我想添加一条注释,指出 GetAllVenues 是一个异步方法。
In your first approach, which does not work, the flow of the code:
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:
Then
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.
我猜问题是,您正在创建新的虚拟机。
我认为这段代码有效。您创建新的 VenueViewModel,用数据填充它,仅此而已。此虚拟机再也不会被使用。
MainPage.xaml 示例的规则相同。
您应该在构造函数/加载处理程序中创建 VenuViewModel 的全局变量,并在 Loaded 事件中调用其 GetAllVenues
I guess the problem is, that you are creating new VM.
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