这里是对本质上是一个简单问题的冗长解释。我正在使用Telerilk RadDropDownButton,它显示带有复选框的列表项。
<Controls:RadDropDownButton AutoOpenDelay="0:0:0.0" x:Name="Urgency" VerticalAlignment="Center" Width="150" Content="{Binding Path=ItemsSource, ElementName=UrgencyList, Mode=TwoWay, Converter={StaticResource ButtonTextConverter}}" HorizontalContentAlignment="Left">
<Controls:RadDropDownButton.DropDownContent>
<ListBox x:Name="UrgencyList">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Content="{Binding Name}" ClickMode="Press" IsChecked="{Binding IsChecked, Mode=TwoWay}" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Controls:RadDropDownButton.DropDownContent>
</Controls:RadDropDownButton>
如您所见,我将Content属性绑定到Converter。我想要的是,如果未选择任何内容,则内容将显示为“全部”,并且如果选中某项,则将显示所选(已选中)项的列表。
public class ButtonTextConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Debug.WriteLine("I'm Binding");
int numChecked = 0;
if (value != null)
numChecked = ((ObservableCollection<UrgencyItem>) value).Count(urgencyItem => urgencyItem.IsChecked);
return numChecked > 0 ? string.Format("{0} Items Selected", numChecked) : "All";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
我要绑定的类根据需要实现INotifyPropertyChanged。在此部分列出:
public class UrgencyItem : INotifyPropertyChanged
{
private int _id;
private bool _isChecked;
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged("Name");
}
}
并且我将ListBox绑定到后面代码中的数据上,如下所示:
private void SearchParamsVertical_Loaded(object sender, RoutedEventArgs e)
{
urgencyList = new ObservableCollection<UrgencyItem>
{
new UrgencyItem {ID = 1, IsChecked = false, Name = "Non Emergent"},
new UrgencyItem {ID = 2, IsChecked = false, Name = "Emergent"},
new UrgencyItem {ID = 3, IsChecked = false, Name = "Stat Emergent"},
new UrgencyItem {ID = 4, IsChecked = false, Name = "Stroke Protocol"}
};
urgencyList.CollectionChanged += urgencyList_CollectionChanged;
UrgencyList.ItemsSource = urgencyList;
}
这里是问题...
选中复选框后,内容的值应更新。不是。
之所以不是这样,是因为尽管该通知发出了IsChecked已更改的通知,但该通知基本上没有任何进展。 UrgencyItem对象不知道它是ObservableCollection的一部分。关于ObservableCollection的事情是,仅当将项目添加到集合中或从集合中删除项目时,它才向其绑定发送通知。换句话说,更改集合中项目的属性不会触发CollectionChanged事件,因为没有添加/删除对象。
我需要做的是在修改集合的属性时触发collectionChanged事件。我曾经知道如何做到这一点,但是离Silverlight太多了,我已经忘记了如何做。
有人吗?
简而言之,我认为您的诊断是正确的:如果ObservableCollection中的对象发生更改,即使该对象实现了INotifyPropertyChanged,通常也不会收到CollectionChanged通知。据我所知,还没有一种直接的方法可以通过内置的Silverlight类获得所需的行为。
我知道有三种可能的解决方法:
((1)一种选择是从servableCollection继承,为urgencyList创建自己的集合,该集合实现此行为,即,它订阅添加到集合中的每个对象的INPC通知,并在出现时触发CollectionChanged事件那会发生。
((2)的第二种选择是使用类似ReactiveUI framework的东西,它具有实现此行为的自己的ReactiveCollection。
(3)第三种选择是通过类似Obtics or Continuous Linq的方式创建您的urgencyList。他们返回的集合会自动实现此行为。
这就是我正在使用的。我想肯在nr(1)中提出的建议:
public class Person: INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set {
_name = value;
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
我在PLObservableNotifyList<Person>
中有这些对象,我在ItemsSource
上将其设置为ItemsControl
。当我更新值(使用设置器)后,绑定将自动更新。
public class PLObservableNotifyList<T> :
ObservableCollection<T> where T : INotifyPropertyChanged
{
public ItemPropertyChangedEventHandler ItemPropertyChanged;
public EventHandler CollectionCleared;
protected override void OnCollectionChanged(
NotifyCollectionChangedEventArgs args)
{
base.OnCollectionChanged(args);
if (args.NewItems != null)
foreach (INotifyPropertyChanged item in args.NewItems)
item.PropertyChanged += OnItemPropertyChanged;
if (args.OldItems != null)
foreach (INotifyPropertyChanged item in args.OldItems)
item.PropertyChanged -= OnItemPropertyChanged;
}
void OnItemPropertyChanged(object sender,
PropertyChangedEventArgs args)
{
if (ItemPropertyChanged != null)
ItemPropertyChanged(this,
new PLItemPropertyChangedEventArgs(sender,
args.PropertyName));
}
protected override void ClearItems()
{
foreach (INotifyPropertyChanged item in Items)
item.PropertyChanged -= OnItemPropertyChanged;
if (CollectionCleared != null)
CollectionCleared(this, EventArgs.Empty);
base.ClearItems();
}
}
您需要我的ObservableComputations库。使用该库,您可以编写代码:
private Computing<string> _checkedUrgencyItemsText;
public Computing<string> CheckedUrgencyItemsText = _selectedUrgencyItemsText ??
Expr.Is(() => UrgencyItems.Filtering(urgencyItem => urgencyItem.IsChecked)
.Using(checkedUrgencyItems =>
checkedUrgencyItems.Count > 0
? string.Format("{0} Items Selected", checkedUrgencyItems.Count)
: "All")).Computing();
<Controls:RadDropDownButton Content="{Binding Path=CheckedUrgencyItemsText.Value}" AutoOpenDelay="0:0:0.0" x:Name="Urgency" VerticalAlignment="Center" Width="150" HorizontalContentAlignment="Left">
<Controls:RadDropDownButton.DropDownContent>
<ListBox x:Name="UrgencyList">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Content="{Binding Name}" ClickMode="Press" IsChecked="{Binding IsChecked, Mode=TwoWay}" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Controls:RadDropDownButton.DropDownContent>
</Controls:RadDropDownButton>