WPF 如何在绑定到集合时正确更新
本文关键字:更新 集合 绑定 WPF | 更新日期: 2023-09-27 18:28:10
在下面的绑定中,
<TextBlock Text="{Binding Path=., StringFormat=TotalPages:{0}, Converter={StaticResource totalPagesConverter}}"/>
totalPagesConverter
取ObservableCollection<MyFileInfo>
并返回总页数。
这仅适用于第一次,但在更改MyFileInfo
对象的属性时不会更新。请注意,我确实为MyFileInfo
实现了INotifyPropertyChanged
,并且当它们绑定到MyFileInfo
属性时,事情会正确更新。但显然,当绑定到此类对象的Collection
时缺少某些东西。如何绑定到集合以使其正确更新?谢谢!
更新 谢谢大家! MyFileInfo
和转换器的属性如下所示:
MyFileInfo
类
public class MyFileInfo : INotifyPropertyChanged
{
private Boolean ifPrint;
private Boolean isValid;
public string Filename {
get; set;
}
public string Filepath {
get; set;
}
public int Copies {
get; set;
}
public int Pages {
get; set;
}
public Boolean IfPrint {
get{
return this.ifPrint;
}
set{
if (this.ifPrint != value){
this.ifPrint = value;
onPropertyChanged("IfPrint");
}
}
}
public Boolean IsValid {
get {
return this.isValid;
}
set {
if (this.isValid!= value) {
this.isValid = value;
onPropertyChanged("IsValid");
}
}
}
private void onPropertyChanged(string propertyName) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
转换器类
public class TotalPagesConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
ObservableCollection<MyFileInfo> lst = (ObservableCollection<MyFileInfo>)value;
int t = 0;
foreach(MyFileInfo f in lst){
t += (f.IsValid && f.IfPrint) ? f.Pages : 0;
}
return t.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
关于转换器的一点:我有一个CheckBox
供用户选择要打印的文件。每次切换CheckBox
时,都会翻转MyFileInfo
对象的IfPrint
布尔属性。此转换器遍历所有IfPrint && IsValid
文件以重新计算总页数,并在 GUI 上更新新的要打印的总页数(理想情况下(。这不是那么有效,但列表很短(<10(。欢迎任何其他想法!
这仅适用于第一次,但在更改 MyFileInfo 对象的属性时不会更新。
ObservableCollection
只会在集合本身发生更改(例如添加或删除项(时引发通知事件。
如果希望集合在集合中的任何项发生更改时也触发"我已更改"消息,则必须自己挂接属性更改处理程序。
public MyViewModel()
{
FileInfoCollection = new ObservableCollection<MyFileInfo>();
// Hook up initial changed handler. Could also be done in setter
FileInfoCollection.CollectionChanged += FileInfoCollection_CollectionChanged;
}
void FileInfoCollection_CollectionChanged(object sender, CollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach(MyFileInfo item in e.NewItems)
{
item.PropertyChanged += MyFileInfo_PropertyChanged;
}
}
if (e.OldItems != null)
{
foreach(MyFileInfo item in e.OldItems)
{
item.PropertyChanged -= MyFileInfo_PropertyChanged;
}
}
}
void MyFileInfo_PropertyChanged(object sender, PropertyChange e)
{
// Whenever a FileInfo changes, raise change notification for collection
RaisePropertyChanged("FileInfoCollection");
}
也就是说,我不明白为什么每当集合中项目的属性发生更改时,都需要重新评估集合中项目计数的绑定,除非您在转换器内进行某种过滤。
另外,如果您只是绑定到集合的计数,那么不能只绑定到ObservableCollection<T>
上的.Count
属性吗?
<TextBlock Text="{Binding Path=Count, StringFormat=TotalPages:{0}}" />
如果我理解正确,您会在某种列表/网格中列出这些项目,并且每个项目都有一个绑定到 IfPrint 属性的复选框。当用户单击该复选框时,您希望文本块更新并显示将 IfPrint 和 IsValid 设置为 true 的 MyFileInfos 的数量,对吗?
绑定只侦听对象(即其 DataContext(上的 PropertyChanged 事件。这意味着唯一会对 IfPrint 更改做出反应的控件是复选框。任何其他控件做出反应的唯一方法是手动传达更改 - 最简单的方法是创建一个事件:
public class MyFileInfo : INotifyPropertyChanged
{
private Boolean ifPrint;
private Boolean isValid;
...
public Boolean IfPrint {
get{
return this.ifPrint;
}
set{
if (this.ifPrint != value){
this.ifPrint = value;
onPropertyChanged("IfPrint");
OnCanPrintChanged();
}
}
}
public Boolean IsValid {
get {
return this.isValid;
}
set {
if (this.isValid!= value) {
this.isValid = value;
onPropertyChanged("IsValid");
OnCanPrintChanged();
}
}
}
private void onPropertyChanged(string propertyName) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private void OnCanPrintChanged()
{
if (CanPrintChanged != null)
{
CanPrintChanged(this, EventArgs.Empty);
}
}
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler CanPrintChanged;
}
在代码隐藏类中,您为列表中的每个 MyFileInfo 挂接到此事件,并更新代码隐藏/视图模型中的属性:
public class MyCodeBehind : INotifyPropertyChanged
{
private int _printablePageCount = 0;
public int PrintablePageCount
{
get { return _printablePageCount; }
set
{
return _printablePageCount = value;
OnPropertyChanged("PrintablePageCount");
}
}
private void OnCanPrintChanged(object sender, EventArgs arg)
{
int t = 0;
foreach(MyFileInfo f in lst)
{
if (f.IsValid && f.IfPrint)
{
t++;
}
}
PrintablePageCount = t;
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
在 XAML 中,改为绑定到 PrintablePageCount(无需转换器(:
<TextBlock Text="{Binding Path=PrintablePageCount, StringFormat=TotalPages:{0}}"/>
不要忘记在设置 ObservableCollection 属性的值时引发 OnPropertyChanged 事件。
public ObservableCollection<MyFileInfo> FileInfos
{
get{return fileInfos;}
set
{
if(fileInfos != value)
{
fileInfos = value;
OnPropertyChanged("FileInfos");
}
}}
将模式 = 双向添加到绑定。
<TextBlock Text="{Binding Path=FileInfos, Mode=TwoWay, StringFormat=TotalPages:{0}, Converter={StaticResource totalPagesConverter}}"/>