以下是对本质上是一个简单问题的冗长解释。我正在使用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绑定到代码隐藏中的Data,如下所示:
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的时间太长了,我已经忘记了。
任何?
答案 0 :(得分:1)
简而言之,我认为您的诊断是正确的:如果ObservableCollection中的对象发生更改,您通常不会收到CollectionChanged通知,即使该对象实现了INotifyPropertyChanged。据我所知,没有一种简单的方法可以通过内置的Silverlight类获得您想要的行为。
我知道有三种可能的解决方法:
(1)一个选项是为ImplencyList创建自己的集合,继承自ObservableCollection,实现此行为,即,它订阅添加到集合的每个对象的INPC通知,并触发CollectionChanged事件当发生这种情况时。
(2)第二种方法是使用类似ReactiveUI framework的东西,它有自己的ReactiveCollection来实现这种行为。
(3)第三种选择是通过Obtics or Continuous Linq之类的东西创建您的urgencyList。他们返回的集合会自动实现此行为。
答案 1 :(得分:0)
这就是我正在使用的。肯在Ken(n)(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();
}
}
。一旦我更新值(使用setter),绑定就会自动更新。
{{1}}
答案 2 :(得分:0)
您需要我的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>