代码之家  ›  专栏  ›  技术社区  ›  Paul Gibson

ObservableCollection刷新视图MVVM

  •  1
  • Paul Gibson  · 技术社区  · 7 年前

    问题是listbox的displaymember绑定到了一个属性,该属性为该项组合了两个字段:数字和日期。usercontrol允许用户更改日期,我希望它立即反映在列表框中。

    public void FillReports()
    {
        if (oRpt != null) oRpt.Clear();
        _oRpt = new ViewableCollection<Reportinformation>();
        //oRpt.CollectionChanged += CollectionChanged; //<--Don't need this
        foreach (Reportinformation rpt in _dataDc.Reportinformations.Where(x => x.ProjectID == CurrentPrj.ID).OrderByDescending(x => x.Reportnumber))
        {
            oRpt.Add(rpt);
        }
    }
    
    private void CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e != null)
        {
            if (e.OldItems != null)
            {
                foreach (INotifyPropertyChanged rpt in e.OldItems)
                {
                    rpt.PropertyChanged -= item_PropertyChanged;
                }
            }
            if (e.NewItems != null)
            {
                foreach (INotifyPropertyChanged rpt in e.NewItems)
                {
                    rpt.PropertyChanged += item_PropertyChanged;
                }
            }
        }
    }
    
    private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        string s = sender.GetType().ToString();
        if(s.Contains("Reportinformation"))
            RaisePropertyChangedEvent("oRpt"); //This line does get called when I change the date
        else if (s.Contains("Observation"))
        {
            RaisePropertyChangedEvent("oObs");
            RaisePropertyChangedEvent("oObsByDiv");
        }
    }
    

    日期被正确地更改,并且更改会持续并写回数据库,但是更改不会反映在列表框中,除非我实际更改集合(当我在与列表框相同的窗口中的另一个控件上切换作业时会发生这种情况)。my property changed handler中的行引发“oRpt”的change事件,该事件是绑定到ListBox的可观察集合,更改日期会调用调试器验证的处理程序:

        <ListBox x:Name="lsbReports" ItemsSource="{Binding oRpt}" DisplayMemberPath="ReportLabel" SelectedItem="{Binding CurrentRpt}" 
                Grid.Row="1" Grid.Column="0" Height="170" VerticalAlignment="Bottom" BorderBrush="{x:Null}" Margin="0,0,5,0"/>
    

    但是,仅仅提高这个更改似乎并不会触发视图刷新列表框中项目的“名称”。我也尝试过为绑定到displaymberpath的ReportLabel提出请求,但这不起作用(尽管值得一试)。我不知道该怎么办,因为我认为基于更改某个实际项目的日期(因此是名称)来重新加载oRpt集合是一种不好的做法,因为我预计该数据库将快速增长。

    下面是Reportinformation扩展类(这是一个自动生成的LinqToSQL类,下面是我的部分):

    public partial class Reportinformation // : ViewModelBase <-- take this out INPC already hooked up
    {
        public ViewableCollection<Person> lNamesPresent { get; set; }
        public string ShortDate
        {
            get
            {
                DateTime d = (DateTime)Reportdate;
                return d.ToShortDateString();
            }
            set
            {
                DateTime d = DateTime.Parse(value);
                if (d != Reportdate)
                {
                    Reportdate = DateTime.Parse(d.ToShortDateString());
                    SendPropertyChanged("ShortDate");//This works and uses the LinqToSQL call not my ViewModelBase call
                    SendPropertyChanged("ReportLabel"); //use the LinqToSQL call
                     //RaisePropertyChangedEvent("ReportLabel"); //<--This doesn't work
                }
            }
        }
    
        public string ReportLabel
        {
            get
            {
                return string.Format("{0} - {1}", Reportnumber, ShortDate);
            }
        }
    
        public void Refresh()
        {
            RaisePropertyChangedEvent("oRpt");
        }
    
        public string RolledNamesString
        {
            get
            {
                if (lNamesPresent == null) return null;
                return string.Join("|",lNamesPresent.Where(x=>x.Name!= "Present on Site Walk").Select(x=>x.Name).ToArray());
            }
        }
    }
    

    1 回复  |  直到 7 年前
        1
  •  0
  •   Dave M    7 年前

    你可以用两种方法中的一种来解决这个问题。或者你的 ReportInformation 类需要实现 INotifyPropertyChanged 并引发 ReportLabel

    public class ReportInformation : INotifyPropertyChanged
    {
        private int _numberField;
        private DateTime _dateField;
    
        public int NumberField
        {
            get => _numberField;
            set 
            {
                if (_numberField != value)
                {
                    _numberField = value;
                    RaisePropertyChanged();
                    RaisePropertyChanged(nameof(ReportLabel));
                }
            }
        }
    
        public DateTime DateField
        {
            get => _dateField;
            set
            {
                if (_dateField != value)
                {
                    _dateField = value;
                    RaisePropertyChanged();
                    RaisePropertyChanged(nameof(ReportLabel));
                }
            }
        }
    
        public string ReportLabel => $"{NumberField}: {DateField}";
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void RaisePropertyChanged([CallerMemberName]string name = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
    

    或者,你可以用在你的 ListBox ItemTemplate DisplayMemberPath 像这样:

    <ListBox x:Name="lsbReports" 
             ItemsSource="{Binding oRpt}"
             SelectedItem="{Binding CurrentRpt}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding NumberField}"/>
                    <TextBlock Text=": "/>
                    <TextBlock Text="{Binding DateField}"/>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>