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

时间:2014-10-29 10:12:26

标签: c# wpf data-binding

在以下绑定中,

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

totalPagesConverterObservableCollection<MyFileInfo>并返回总页数。

这仅适用于第一次,但在MyFileInfo对象的属性更改时不会更新。请注意,我确实为INotifyPropertyChanged实施了MyFileInfo,并且当它们绑定到属性MyFileInfo时,事情会正确更新。但是当绑定到Collection个这样的对象时,显然缺少某些东西。如何绑定到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时,都会翻转IfPrint对象的MyFileInfo布尔属性。此转换器遍历所有IfPrint && IsValid个文件以重新计算总页数,并在GUI上更新新的待打印页面(理想情况下)。这不是那么有效,但是列表很短(&lt; 10)。欢迎任何其他想法!

3 个答案:

答案 0 :(得分:2)

  

这仅适用于第一次,但在更改MyFileInfo对象的属性时不会更新。

ObservableCollection只会在集合本身发生变化时引发通知事件,例如添加或删除项目。

如果你想让这个系列同时发射一个&#34;我已经改变了#34;消息,当集合中的任何项目发生变化时,您必须自己连接属性更改的处理程序。

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");
}

那就是说,我不明白为什么每当集合中项目的属性发生变化时,需要重新评估集合中项目计数的绑定,除非你做某种事情在转换器内进行过滤。

另外,如果您只是绑定到集合的计数,那么您是否只能绑定到.Count上的ObservableCollection<T>属性?

<TextBlock Text="{Binding Path=Count, StringFormat=TotalPages:{0}}" />

答案 1 :(得分:1)

如果我理解正确,您可以在某种列表/网格中列出项目,每个项目都有一个绑定到IfPrint属性的复选框。当用户单击该复选框时,您希望文本块更新并显示将IfPrint和IsValid设置为true的MyFileInfos的数量,对吗?

绑定仅侦听对象上的PropertyChanged事件,该对象是其DataContext。这意味着对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连接此事件,并更新codebehind / viewmodel中的属性:

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}}"/>

答案 2 :(得分:0)

在设置ObservableCollection属性的值时,不要忘记引发OnPropertyChanged事件。

public ObservableCollection<MyFileInfo> FileInfos
{
 get{return fileInfos;}
 set
 {
   if(fileInfos != value)
   {
     fileInfos = value;
     OnPropertyChanged("FileInfos");
   }
}} 

Add Mode = TwoWay to binding。

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