WPF 如何在绑定到集合时正确更新

本文关键字:更新 集合 绑定 WPF | 更新日期: 2023-09-27 18:28:10

在下面的绑定中,

<TextBlock Text="{Binding Path=., StringFormat=TotalPages:{0}, Converter={StaticResource totalPagesConverter}}"/>

totalPagesConverterObservableCollection<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(。欢迎任何其他想法!

WPF 如何在绑定到集合时正确更新

这仅适用于第一次,但在更改 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}}"/>