- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我有一个基类实现了我所有 View 模型继承自的 INotifyPropertyChanged:
public class BaseChangeNotify : INotifyPropertyChanged
{
private bool isDirty;
public BaseChangeNotify()
{
}
public event PropertyChangedEventHandler PropertyChanged;
public bool IsDirty
{
get
{
return this.isDirty;
}
set
{
this.isDirty = value;
this.OnPropertyChanged();
}
}
public virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
// Perform the IsDirty check so we don't get stuck in a infinite loop.
if (propertyName != "IsDirty")
{
this.IsDirty = true; // Each time a property value is changed, we set the dirty bool.
}
if (this.PropertyChanged != null)
{
// Invoke the event handlers attached by other objects.
try
{
Application.Current.Dispatcher.Invoke(() =>
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)));
}
catch (Exception exception)
{
throw exception;
}
}
}
}
我有一个主视图模型,实例 subview 模型包装了数据库中的模型。我注册为 subview 模型 PropertyChanged 事件的监听器,这样我可以在更改 subview 模型时使主视图模型“变脏”。然而,问题是当 child 被更改时,我得到一个与父 View 模型关联的空引用异常。
主视图模型(简化):
public class DiaryDescriptionViewModel : BaseViewModel, IDataErrorInfo
{
private Diary diary;
private ObservableCollection<DiaryDescriptionDetailsViewModel> diaryDescriptions;
private DiaryDescriptionDetailsViewModel selectedDiaryDescription;
private List<SectionViewModel> projectSections;
public DiaryDescriptionViewModel()
{
}
public DiaryDescriptionViewModel(Diary diary, UserViewModel user) : base(user)
{
this.diary = diary;
// Restore any previously saved descriptions.
var diaryRepository = new DiaryRepository();
List<DiaryDescription> descriptions = diaryRepository.GetDiaryDescriptionsByDiaryId(diary.DiaryId);
// Fetch sections for selected project.
var projectSections = new List<Section>();
projectSections = diaryRepository.GetSectionsByProjectId(diary.ProjectId);
// Convert the Section model into a view model.
this.projectSections = new List<SectionViewModel>(
(from section in projectSections
select new SectionViewModel(section))
.ToList());
foreach (var projectSection in this.projectSections)
{
// We want to set ourself to Dirty if any child View Model becomes dirty.
projectSection.PropertyChanged += (sender, args) => this.IsDirty = true;
}
// Reconstruct our descriptions
this.DiaryDescriptions = new ObservableCollection<DiaryDescriptionDetailsViewModel>();
foreach (DiaryDescription description in descriptions)
{
SectionViewModel section =
this.projectSections.FirstOrDefault(s => s.Items.Any(i => i.BidItemId == description.BidItemId));
BidItem item = section.Items.FirstOrDefault(i => i.BidItemId == description.BidItemId);
var details = new DiaryDescriptionDetailsViewModel(description, section, item);
// Commenting this out resolves the NULL Reference Exception.
details.PropertyChanged += (sender, args) => this.IsDirty = true;
this.diaryDescriptions.Add(details);
}
this.IsDirty = false;
}
public ObservableCollection<DiaryDescriptionDetailsViewModel> DiaryDescriptions
{
get
{
return this.diaryDescriptions;
}
set
{
this.diaryDescriptions = value;
this.OnPropertyChanged();
}
}
public DiaryDescriptionDetailsViewModel SelectedDiaryDescription
{
get
{
return this.selectedDiaryDescription;
}
set
{
this.selectedDiaryDescription = value;
if (value != null)
{
// If the description contains a biditem DiaryId, then we go fetch the section and biditem
// associated with the diary description.
if (value.BidItemId > 0)
{
SectionViewModel sectionViewModel = this.ProjectSections.FirstOrDefault(
section => section.Items.FirstOrDefault(item => item.BidItemId == value.BidItemId) != null);
if (sectionViewModel != null)
{
BidItem bidItem = sectionViewModel.Items.FirstOrDefault(item => item.BidItemId == value.BidItemId);
this.selectedDiaryDescription.Section = sectionViewModel;
this.selectedDiaryDescription.BidItem = bidItem;
}
}
this.selectedDiaryDescription.IsDirty = false;
}
this.OnPropertyChanged();
this.IsDirty = false;
}
}
public List<SectionViewModel> ProjectSections
{
get
{
return this.projectSections;
}
set
{
this.projectSections = value;
this.OnPropertyChanged();
}
}
subview 模型:
public class DiaryDescriptionDetailsViewModel : BaseChangeNotify
{
private readonly DiaryDescription diaryDescription;
private SectionViewModel section;
private BidItem bidItem;
public DiaryDescriptionDetailsViewModel(DiaryDescription description)
{
this.diaryDescription = description;
// If we have a valid biditem identifier (greater than 0) than we need to go and
// fetch the item and it's associated funding section.
if (description.BidItemId > 0)
{
var repository = new DiaryRepository();
this.section = new SectionViewModel(repository.GetSectionByBidItemId(description.BidItemId));
this.bidItem = repository.GetBidItemById(description.BidItemId);
}
this.IsDirty = false;
}
public DiaryDescriptionDetailsViewModel(DiaryDescription description, SectionViewModel section, BidItem item)
{
this.diaryDescription = description;
if (description.BidItemId > 0)
{
this.section = section;
this.bidItem = item;
}
this.IsDirty = false;
}
public int Id
{
get
{
return this.diaryDescription.DiaryDescriptionId;
}
}
public int DiaryId
{
get
{
return this.diaryDescription.DiaryId;
}
}
public DiaryDescription Description
{
get
{
return this.diaryDescription;
}
}
public int BidItemId
{
get
{
return this.diaryDescription.BidItemId;
}
}
public BidItem BidItem
{
get
{
return this.bidItem;
}
set
{
this.bidItem = value;
this.diaryDescription.BidItemId = value.BidItemId;
this.OnPropertyChanged();
}
}
public SectionViewModel Section
{
get
{
return this.section;
}
set
{
this.section = value;
this.OnPropertyChanged();
}
}
}
因此在我的单元测试中,我使用了以下代码:
var diaryRepository = new DiaryRepository();
Diary diary = diaryRepository.GetDiaryById(DiaryId);
var diaryDescriptionViewModel = new DiaryDescriptionViewModel(diary, new UserViewModel());
// Act
diaryDescriptionViewModel.SelectedDiaryDescription =
diaryDescriptionViewModel.DiaryDescriptions.FirstOrDefault(
desc => desc.Id == DiaryDescriptionId);
这是堆栈跟踪:
Test Name: DeleteDiaryDescriptionsById
Test FullName: UnitTests.ViewModels.DiaryDescriptionViewModelTests.DeleteDiaryDescriptionsById
Test Source: c:\Users\UnitTests\ViewModels\DiaryDescriptionViewModelTests.cs : line 103
Test Outcome: Failed
Test Duration: 0:00:02.678712
Result Message:
Test method Pen.UnitTests.ViewModels.DiaryDescriptionViewModelTests.DeleteDiaryDescriptionsById threw exception:
System.NullReferenceException: Object reference not set to an instance of an object.
Result StackTrace:
at Pen.ViewModels.BaseChangeNotify.OnPropertyChanged(String propertyName) in c:\Users\ViewModels\BaseChangeNotify.cs:line 70
at ViewModels.BaseChangeNotify.set_IsDirty(Boolean value) in c:\Users\ViewModels\BaseChangeNotify.cs:line 43
at ViewModels.BaseChangeNotify.OnPropertyChanged(String propertyName) in c:\Users\ViewModels\BaseChangeNotify.cs:line 57
at ViewModels.DiaryDescriptionDetailsViewModel.set_Section(SectionViewModel value) in c:\Users\ViewModels\DiaryDescriptionDetailsViewModel.cs:line 158
at ViewModels.DiaryDescriptionViewModel.set_SelectedDiaryDescription(DiaryDescriptionDetailsViewModel value) in c:\Users\ViewModels\DiaryDescriptionViewModel.cs:line 163
at UnitTests.ViewModels.DiaryDescriptionViewModelTests.DeleteDiaryDescriptionsById() in c:\Users\UnitTests\ViewModels\DiaryDescriptionViewModelTests.cs:line 112
它似乎在告诉我与 IsDirty 关联的对象为空,但事实并非如此。我通过调试器验证它存在并取消注释 DiaryDetailDescriptionViewModel.PropertyChanged 事件注册它工作正常。我做错了吗?
最佳答案
Application.Current
从单元测试运行时为空。
如果您想在单元测试中为调度程序安排内容,或者注入(inject)调度程序本身,则需要将其抽象到某个接口(interface)后面。
关于c# - INotifyPropertyChanged 奇怪的 NullReferenceException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23697237/
我有这种来自 Google map 自动完成的奇怪行为(或者我可能错过了某事)...想法?奇怪的: 您在输入中输入某物,例如“伦敦” 您按 [ENTER] 你按下 [CLEAR] 按钮 你点击进入'输
这段代码与《Learning Java》(Oracle Press Books)一书中的代码完全一样,但它不起作用。我不明白为什么它不起作用,它应该起作用。我用 OpenJDK 和 Sun JDK 7
示例 1 中究竟发生了什么?这是如何解析的? # doesnt split on , [String]::Join(",",("aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aa
我需要获得方程式系统的解决方案。为此,我使用函数sgesv_()。 一切都很好,它使我感到解决方案的正确结果。 但是我得到一个奇怪的警告。 警告:从不兼容的指针类型传递'sgesv_'的参数3 我正在
我目前在制作动画时遇到一个奇怪的问题: [UIView animateWithDuration:3 delay:0
alert('works'); $(window).load(function () { alert('does not work'); });
我的代码: public class MyTest { public class StringSorter implements Comparator { public
我正在学习 JavaScript。尝试理解代码, function foo (){ var a = b = {name: 'Hai'}; document.write(a.name +''
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况有关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
这按预期工作: [dgorur@ted ~]$ env -i env [dgorur@ted ~]$ 这样做: [dgorur@ted ~]$ env -i which date which: no
struct BLA { int size_; int size()const{ return size_; } } int x; BLA b[ 2 ]; BLA * p = &b[
我有以下代码: #test img {vertical-align: middle;} div#test { border: 1px solid green; height: 150px; li
我想大多数使用过 C/C++ 的人都对预处理器的工作原理有一定的直觉(或多或少)。直到今天我也是这么认为的,但事实证明我的直觉是错误的。故事是这样的: 今天我尝试了一些东西,但我无法解释结果。首先考虑
我想为 TnSettings 做 mock,是的,如果通过以下方法编写代码,它就可以工作,问题是我们需要为每个案例编写 mock 代码,如果我们只 mock 一次然后执行多个案例,那么第二个将报告异常
我的项目中有以下两个结构 typedef volatile struct { unsigned char rx_buf[MAX_UART_BUF]; //Input buffer over U
Regex rx = new Regex(@"[+-]"); string[] substrings = rx.Split(expression); expression = "-9a3dcb
我的两个应用程序遇到了一个奇怪的问题。这是设置: 两个 tomcat/java 应用程序,在同一个网络中运行,连接到相同的 MS-SQL-Server。一个应用程序,恰好按顺序位于 DMZ 中可从互联
我目前正在与 Android Api Lvl 8 上的 OnLongClickListener 作斗争。 拿这段代码: this.webView.setOnLongClickListener(new
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况相关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
只是遇到了奇怪的事情。我有以下代码: -(void)ImageDownloadCompleat { [self performSelectorOnMainThread:@selector(up
我是一名优秀的程序员,十分优秀!