簡體   English   中英

ObservableCollection不能反映摘要計算的更改

[英]ObservableCollection doesn't reflect changes for Summary calculation

我在后面的代碼中有一個DataGrid並帶有手動處理的ObservableCollection<T> 當我向后備列表中添加新項目或刪除其中一個項目時,將更新“ Count ,但不會通知ItemsSource以便更新Price總和。 我什至嘗試了BindingList<T>但即使Count停止更新! 駭客是什么?

XAML:

<Label>
   <Label.Content>
        <TextBlock>
             <TextBlock.Text>
                <MultiBinding StringFormat="{}Count: {0}, Sum: {1}" NotifyOnTargetUpdated="True" NotifyOnSourceUpdated="True">
                    <Binding ElementName="datagrid" Path="ItemsSource.Count" UpdateSourceTrigger="PropertyChanged"/>
                    <Binding ElementName="datagrid" Path="ItemsSource" Converter="{StaticResource SumConverter}" UpdateSourceTrigger="PropertyChanged" />
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
    </Label.Content>
</Label>

SumConverter:

[ValueConversion(typeof(ObservableCollection<DocView>), typeof(String))]
public class SumConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value != null && value is ObservableCollection<DocView>)
        {
            ObservableCollection<DocView> items = (ObservableCollection<DocView>)value;
            return items.Sum(x => x.Price);
        }
        else
            return 0;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

后面的代碼:

ObservableCollection<DocView> list = new ObservableCollection<DocView>();

datagrid.DataContext = list;

我認為問題在於將列表綁定到datagrid以及轉換器不會在ItemSource獲取更改,因為該屬性不會更改。 該集合不會觸發PropertyChanged事件。 嘗試這個:

datagrid.ItemsSource = list;

並且在您的XAML中使用多重綁定上的轉換器。 集合更改時,對ItemsSource.Count的綁定將觸發通知。 注意,您不需要將TextBlock嵌入Label

    <TextBlock Grid.Row="2">
        <TextBlock.Text>
            <MultiBinding Converter="{StaticResource SumConverter}">
                <Binding ElementName="datagrid" Path="ItemsSource" />
                <Binding ElementName="datagrid" Path="ItemsSource.Count"/>
            </MultiBinding>
        </TextBlock.Text>
    </TextBlock>

顯然,您需要相應地修改轉換器。

 public class SumConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (values != null && values.Length > 0 && values[0] is ObservableCollection<string>)
            {
                ObservableCollection<DocView> items = (ObservableCollection<DocView>)values[0];
                return string.Format("Count: {0}, Sum: {1}", items.Count, items.Sum(x => x.Price));
            }
            else
                return "Count: 0, Sum: 0";
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

這是需要最少更改的解決方案。 最好將集合綁定到ViewModel而不是datagrid.ItemsSource

完整的解決方案:

  1. 將所有內容包裝在視圖模型中,並在任何地方實現INPC
  2. 使用BindingList<T>而不是ObservableCollection<T> (如果使用ObservableCollection<T> ,則列表中現有項目的屬性更新不會觸發UI的更新,而BindingList<T>支持此操作)

XAML:

<DataGrid x:Name="datagrid" ItemsSource="{Binding List}" />

    <Label>
       <Label.Content>
            <TextBlock>
                 <TextBlock.Text>
                    <MultiBinding StringFormat="{}Count: {0}, Sum: {1}" NotifyOnTargetUpdated="True" NotifyOnSourceUpdated="True">
                         <Binding ElementName="datagrid" Path="DataContext.Count"/>
                         <Binding ElementName="datagrid" Path="DataContext.Total"/>
                    </MultiBinding>
                </TextBlock.Text>
            </TextBlock>
        </Label.Content>
    </Label>

后面的代碼:

DocViewModel list= new DocViewModel();
datagrid.DataContext = list;

DocViewModel

public class DocViewModel : INotifyPropertyChanged
{
    public DocViewModel()
    {
        this.list = new BindingList<DocView>();
        this.list.ListChanged += list_ListChanged;
    }

    void list_ListChanged(object sender, ListChangedEventArgs e)
    {
        OnPropertyChanged("Total");
        OnPropertyChanged("Count");
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private BindingList<DocView> list;

    public BindingList<DocView> List
    {
        get { return list; }
        set
        {
            list = value;
            OnPropertyChanged("List");
        }
    }

    public decimal Total
    {
        get { return list.Sum(x => x.Price); }
    }

    private void OnPropertyChanged(String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public int Count
    {
        get { return list.Count; }
    }
}

DocView:

public class DocView : INotifyPropertyChanged
{
    private decimal price;

    public decimal Price
    {
        get { return price; }
        set
        {
            price = value;
            OnPropertyChanged("Price");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

編輯

這是視圖模型的更具體的通用實現,使您可以將相同的邏輯用於不同的項目類型,以提供自定義的總計和計數計算器:

public class GenericListViewModel<T> : INotifyPropertyChanged
    {
        Func<BindingList<T>, decimal> totalCalculator;    
        Func<BindingList<T>, int> countCalculator;

        public GenericListViewModel(Func<BindingList<T>, decimal> _totalCalculator, Func<BindingList<T>, int> _countCalculator)
        {
            this.list = new BindingList<T>();
            this.list.ListChanged += list_ListChanged;

            this.totalCalculator = _totalCalculator;
            this.countCalculator = _countCalculator;
        }

        void list_ListChanged(object sender, ListChangedEventArgs e)
        {
            OnPropertyChanged("Total");
            OnPropertyChanged("Count");
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private BindingList<T> list;

        public BindingList<T> List
        {
            get { return list; }
            set
            {
                list = value;
                OnPropertyChanged("List");
            }
        }

        public decimal Total
        {
            get
            {
                if (totalCalculator != null)
                    return totalCalculator.Invoke(list);
                else
                    throw new NotImplementedException("Total Func must be impelmented");
            }
        }

        private void OnPropertyChanged(String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public int Count
        {
            get
            {
                if (countCalculator != null)
                    return countCalculator.Invoke(list);
                else
                    throw new NotImplementedException("Count Func must be implemented");
            }
        }
    }

用法:

public class MyDocViewModel: GenericListViewModel<DocView>
{
    public MyDocViewModel()
        : base(
        (list) =>
        {
            return list.Sum(x => x.Price);
        },
        (list) =>
        {
            return list.Count;
        }
        )
    {

    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM