【发布时间】: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,则所有更新都应该自动处理。您的“这不起作用”行应该完全符合您的要求......并且您不需要手动订阅事件。