Treeview Checkboxes not updating after bound property changed (SL4)
My problem is simple. I have a treeview bound to an ObservableCollection of objects, and those objects all have their own ObservableCollections. Based on user selection of other criteria on my page I want to dynamically set which checkboxes are checked. Unfortunately my checkboxes fail to update their IsChecked status after I have changed the corresponding bool Property bound to IsChecked. The checkboxes will be in the correct state the first time any node is expanded, but afterwards they stop updating. I suspect this means the objects are not created/evaluated until they are actually due to be shown for the first time.
The structure of data is Silverlight -> ViewModel -> ObservableCollection of StoreGroups LocalStoreGroups -> StoreGroup has ObservableCollection of Store Stores
Through debugging I have noticed that there are no handlers attached to this.PropertyChanged, and am wondering if this is the problem?
Treeview control :
<controls:TreeView ItemsSource="{Binding LocalStoreGroups}" ItemTemplate="{StaticResource TreeviewStoreGroupTemplate}" />
In my project I use a treeview with the following HeirarchalDataTemplates :
<UserControl.Resources>
<sdk:HierarchicalDataTemplate x:Key="TreeviewStoreTemplate">
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" Content="{Binding DTO.Name}" />
</sdk:HierarchicalDataTemplate>
<sdk:HierarchicalDataTemplate x:Key="TreeviewStoreGroupTemplate" ItemsSource="{Binding Stores}" ItemTemplate="{StaticResource TreeviewStoreTemplate}">
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" Content="{Binding DTO.Name}" />
</sdk:HierarchicalDataTemplate>
</UserControl.Resources>
The code for the IsSelected Property (both the StoreGroup object and the Store object have this property :
private bool _IsSelected;
public bool IsSelected
{
get { return _IsSelected; }
set
{
_IsSelected = value;
OnPropertyChanged("IsSelected");
}
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler temp = this.PropertyChanged;
if (null != temp)
temp(this, e);
}
Code to change IsSelected
foreach (Store s in LocalStoreGroups.SelectMany(sg => sg.Stores))
{
s.IsSelected = false;
}
foreach (StoreLink link in links)
{
Store targetStore = (from s in LocalStor开发者_如何学GoeGroups.SelectMany(sg => sg.Stores) where s.DTO.ID == link.DTO.StoreID select s).FirstOrDefault();
targetStore.IsSelected = true;
}
It looks like you are updating the property in response to a load event. It is likely then that you are not on the UI thread when you update the property. Unless the change occurs on the UI thread it will not update the display.
For bound properties and properties that are collections (and not children in observable collections) it is only the OnPropertyChanged that needs to be on the UI thread. The properties can change earlier, but the UI will not change bindings until OnPropertyChanged is called.
All our ViewModels derive from a ViewModelBase we created that implements a helper SendPropertyChanged like below (so we never have to worry about cross-threading).
All our notify properties call that instead of calling OnPropertyChanged directly.
It also exposes a generally useful OnUiThread method so you can execute arbitrary code on the UI thread:
protected delegate void OnUiThreadDelegate();
public event PropertyChangedEventHandler PropertyChanged;
public void SendPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.OnUiThread(() => this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)));
}
}
protected void OnUiThread(OnUiThreadDelegate onUiThreadDelegate)
{
if (Deployment.Current.Dispatcher.CheckAccess())
{
onUiThreadDelegate();
}
else
{
Deployment.Current.Dispatcher.BeginInvoke(onUiThreadDelegate);
}
}
Anyways, the give-away here should have been that nobody was subscribed to the PropertyChanged event. Turns out that although I implemented the PropertyChanged event I forgot to actually give the class the INotifyPropertyChanged interface.
精彩评论