我刚刚回到 WPF/XAML 编程,遇到了一个我无法弄清楚的问题。我有一个对象,可以把它想象成一个订单,每个订单包含一个 Store 对象列表,每个 Store 包含一个 Planogram 对象列表。未处理的订单从数据库中检索并放入一个可观察的集合中。
使用 MVVM 工具包,每个属性都使用 INotify 来指示属性何时更改。这还会设置 IsDirty 标志,指示已进行更改。 Store 对象和 Order 对象都有一个事件处理程序附加到子列表的集合更改事件,以指示某些包含的元素已更改。
视图模型包含可观察的订单集合,并且在该列表的集合更改事件上还有一个事件处理程序,因此可以更新表单。
表单有一个包含订单名称的列表框。选择订单后,详细信息将显示在表单的其他控件上。
这就是背景...这就是问题所在...我在每个订单名称的列表框的项目模板中都有一个文本块。使用 BoolToVis 转换器将可见性绑定到 Is Dirty 标志。如果我选择一个订单并对任何子属性进行更改,更改会向上冒泡到父级并且正确设置了 is dirty 标志。但是源永远不会更新,文本框中的“*”永远不会显示。
但是,可以选择添加空白订单。创建一个空白订单并将其添加到订单列表中。此时,新订单显示在列表框中,订单名称旁边带有 *。
我已经搜索了几天并通读了我能在 SO 上找到的所有内容。我所看到的大多数是人们没有对属性进行 INotify 设置。我知道,我发现的每个答案都说这应该有效……但事实并非如此。
包含 IsDirty 标志的基础对象
public class BaseOneOffStorePlanogram : ObservableObject, IOneOffData
{
private bool _isDirty = false;
public bool IsDirty
{
get { return _isDirty; }
set {
if(value != _isDirty)
{
_isDirty = value;
base.OnPropertyChanged("IsDirty");
}
}
}
public string LastModifiedBy { get; set; }
public DateTime? LastModifiedOn { get; set; }
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (e.PropertyName != "IsDirty")
{
base.OnPropertyChanged(e);
IsDirty = true;
}
}
}
这是 Store 对象的一个片段,其中每个属性都调用 OnPropertyChanged 事件,也是我更改了 Collection 的地方。
public string StoreNumber
{
get { return _StoreNumber; }
set
{
_StoreNumber = value;
base.OnPropertyChanged("StoreNumber");
}
}
public DateTime? PricingDateOverride
{
get
{
return _PricingDateOverride;
}
set
{
_PricingDateOverride = value;
base.OnPropertyChanged("PricingDateOverride");
}
}
public OneOffStore()
{
_OneOffStorePlanograms = new ObservableCollection<OneOffStorePlanogram>();
_OneOffStorePlanograms.CollectionChanged += _OneOffStorePlanograms_CollectionChanged;
}
private void _OneOffStorePlanograms_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if(e.OldItems != null)
{
foreach( OneOffStorePlanogram oosp in e.OldItems)
{
oosp.PropertyChanged += InvokeCollectionChanged;
}
}
if( e.NewItems != null)
{
foreach(OneOffStorePlanogram oosp in e.NewItems)
{
oosp.PropertyChanged += InvokeCollectionChanged;
}
}
}
private void InvokeCollectionChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("OneOffStorePlanograms");
}
这是定义可观察集合的视图模型部分
public ObservableCollection<OneOff> OneOffs
{
get { return _oneOffs; }
}
private void InitializeForm(){
_oneOffs = new ObservableCollection<OneOff>();
_selectedStores = new ObservableCollection<Store>();
_selectedPlanograms = new ObservableCollection<Models.Planogram>();
_useEffectiveDate = false;
LoadPlanograms();
LoadStores();
LoadOneOffs();
//GetUnprocessedOneOffs();
_oneOffs.CollectionChanged += OneOffs_CollectionChanged;`
}
这是 XAML 片段
<ListBox x:Name="lstActiveJobs" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ItemsSource="{Binding OneOffs, NotifyOnSourceUpdated=True,UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding SelectedOneOff}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding OneOffName, PresentationTraceSources.TraceLevel=High}" Grid.Row="0" Grid.Column="0" />
<TextBlock Text="*" x:Name="HasChangeModifier" Grid.Row="0" Grid.Column="1" Visibility="{Binding IsDirty, Converter={StaticResource BoolToVis}, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
我确定这很简单,或者我做错了。让我感到困惑的是,新添加的、未保存的项目有效,但从数据库中检索到的项目却没有。
任何建议将不胜感激。
重写的
OnPropertyChanged(PropertyChangedEventArgs e)
方法不会为 base.OnPropertyChanged(e)
属性调用 IsDirty
,因此不会通知该属性的绑定。
方法应该是这样的:
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (e.PropertyName != nameof(IsDirty))
{
IsDirty = true;
}
base.OnPropertyChanged(e);
}
注意
OnPropertyChanged(PropertyChangedEventArgs e)
被OnPropertyChanged(string propertyName)
调用。