I have a Silverlight control that has my root ViewModel object as it's data source. The ViewModel exposes a list of Cards as well as a SelectedCard property which is bound to a drop-down list at the top of the view. I then have a form of sorts at the bottom that displays the properties of the SelectedCard. My XAML appears as (reduced for simplicity):
<StackPanel Orientation="Vertical">
<ComboBox DisplayMemberPath="Name"
ItemsSource="{Binding Path=Cards}"
SelectedItem="{Binding Path=SelectedCard, Mode=TwoWay}"
/>
<TextBlock Text="{Binding Path=SelectedCard.Name}"
/>
<ListBox DisplayMemberPath="Name"
ItemsSource="{Binding Path=SelectedCard.PendingTransactions}"
/>
</StackPanel>
I would expect the TextBlock and ListBox to update whenever I select a new item in the ComboBox, but this is not the case. I'm sure it has to do with the fact that the TextBlock and ListBox are actually bound to properties of the SelectedCard so it is listening for property change notifications for the properties on that object. But, I would have thought that data-binding would be smart enough to recognize that the parent object in the binding expression had changed and update the entire binding.
It bears noting that the PendingTransactions property (bound to the ListBox) is lazy-loaded. So, the first time I select an item in the ComboBox, I do make the async call and load the list and the UI updates to display the information corresponding to the selected item. However, when I reselect an item, the UI doesn't change!
For开发者_运维技巧 example, if my original list contains three cards, I select the first card by default. Data-binding does attempt to access the PendingTransactions property on that Card object and updates the ListBox correctly. If I select the second card in the list, the same thing happens and I get the list of PendingTransactions for that card displayed. But, if I select the first card again, nothing changes in my UI! Setting a breakpoint, I am able to confirm that the SelectedCard property is being updated correctly.
How can I make this work???
If you are using Silverlight 3 you will need to use INotifyPropertyChanged.
Example:
public class CardViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Card> Cards { get; set; }
private Card _selectedCard;
public SelectedCard
{
get
{
return _selectedCard;
}
set
{
if (value != _selectedCard)
{
_selectedCard = value;
NotifyPropertyChanged("SelectedCard");
}
}
}
public CardViewModel()
{
Cards = new ObservableCollection<Card>();
//Populate Cards collection with objects
}
public void NotifyPropertyChanged(string item)
{
if (PropertyChanged!=null)
{
PropertyChanged(this, new PropertyChangedEventArgs(item));
}
}
}
All you would need to do is set this class to your views DataContext and everything should be happy.
A pattern I've been using recently is to bind the data context of a container of detail info to the selected item of the list box. The XAML in your case becomes:
<StackPanel Orientation="Vertical">
<ComboBox x:Name="_lbxCards" <-- new
DisplayMemberPath="Name"
ItemsSource="{Binding Path=Cards}"
SelectedItem="{Binding Path=SelectedCard, Mode=TwoWay}"
/>
<StackPanel DataContext={Binding ElementName=_lbxCards,Path=SelectedItem}> <-- new
<TextBlock Text="{Binding Path=Name}" <-- updated
/>
<ListBox DisplayMemberPath="Name"
ItemsSource="{Binding Path=PendingTransactions}" <-- updated
/>
</StackPanel> <-- new
</StackPanel>
Turns out the problem isn't in the UI at all. The PendingTransactions class lazy-loads its values using a async WCF call to the server. The async pattern uses events to notify the caller that the operation is complete so the data can be parsed into the class. Because each Card has its own instance of the PendingTransactions class and we used a ServiceFactory to manage our WCF proxies, each instance was wiring up their event handler to the same event (we are using a singleton approach for performance reasons - for the time being). So, each instance received the event each time any of the instances triggered the async operation.
This means that the data-binding was working correctly. The PendingTransactions collections were overwriting themselves each time a new Card was viewed. So, it appeared that selecting a previous card did nothing when, in fact, it was selecting the correct object for binding, it was the data that was screwed up and make it look like nothing was changing.
Thanks for the advice and guidance nonetheless!
精彩评论