【问题标题】:ObservableCollection Refresh View MVVMObservableCollection 刷新视图 MVVM
【发布时间】:2018-09-06 22:32:43
【问题描述】:

我有一个绑定到 ListBox 的 ObservableCollection。在列表框中选择一个项目会根据所选项目使用其自己的视图模型填充用户控件。我正在使用 Linq to SQL DataContext 将数据从我的模型获取到视图模型。

问题在于列表框的显示成员绑定到一个属性,该属性结合了项目的两个字段,一个数字和一个日期。用户控件允许用户更改日期,我希望它立即反映在列表框中。

我初始化集合并添加 CollectionChanged 和 PropertyChanged 处理程序,以便集合监听集合内属性的更改:

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

日期正确更改并且更改持续存在并写回数据库,但更改不会反映在列表框中,除非我实际更改集合(当我在同一窗口中切换另一个控件上的作业时会发生这种情况列表框)。我的属性更改处理程序中的行引发了“oRpt”的更改事件,“oRpt”是绑定到 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"/>

但似乎简单地提高该更改实际上并不会触发视图刷新列表框中项目的“名称”。我也尝试为绑定到 DisplayMemberPath 的 ReportLabel 发起 Raise,但这不起作用(尽管值得一试)。我不知道从哪里开始,因为我认为根据更改其中一个实际项目的日期(因此是名称)来重新加载 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());
        }
    }
}

回答

所以我的错误是我添加到 LinqToSQL 部分类,并在那里使用我的 ViewModelBase,它在自动生成的部分类之上重新实现了所有 INPC 内容。我取消了那个,只使用自动生成的设计师东西中的 INPC,一切都按预期工作。感谢 SledgeHammer 聊天,让我重新思考这一切!

【问题讨论】:

  • Reportinformation 类需要实现INotifyPropertyChanged 并在ReportLabel 的计算值发生变化时为ReportLabel 提升它
  • 请给出Reportinformation类的代码
  • @DaveM 查看我的编辑。我正在按照您的建议进行操作,但对 DisplayMemberPath 绑定的属性进行更改并没有什么不同。
  • 在 ViewModel 中进行了一些反复试验和试验后,我已经验证即使添加一个新元素然后为“oRpt”引发属性更改事件也不会更新列表。唯一能做的就是从我的 item_PropertyChanged 处理程序中调用 FillReports,这似乎太重了。
  • @PaulGibson 你不需要做任何这些。使用 ObservableCollectionT 和 T 应该实现 INotifyPropertyChanged 并且如果您正确绑定并且正确实现 INPC,则所有更新都应该自动处理。您的“这不起作用”行应该完全符合您的要求......并且您不需要手动订阅事件。

标签: c# wpf listbox


【解决方案1】:

您可以通过以下两种方法之一来解决此问题。您的 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>

【讨论】:

  • 谢谢戴夫。 . . Reportinformation 类是一个自动生成的 LinqToSQL 类,我对其进行了扩展以添加标签,该标签基本上可以在模板中执行您正在执行的操作。所以,我在想这个类的实际属性都被处理了(getter 和 setter),我不能重载它们。标签类只是一个 getter,无法设置,因为它只返回两个字段的组合。我已在上面的帖子中添加了 Reportinformation 类的扩展。
  • 这是正确的答案,也是我认为我在做的事情。然而,LinqToSQL 设计器类已经实现了 INPC 的东西,当我扩展以从我的 ViewModelBase 派生时,我正在重新实现它,我认为这会破坏功能。所以我把它拿出来并根据我的编辑更改了调用。
猜你喜欢
  • 2011-08-25
  • 1970-01-01
  • 1970-01-01
  • 2018-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多