初始化属性后,有没有办法在 Xamarin 视图模型中设置脏标志?



我想为视图模型中的任何必需属性设置脏标志。我在构造函数中将IsDirty初始化为 false。不幸的是,我的属性中的所有 setter 都是在构造函数之后调用的。有没有办法在所有二传手之后将IsDirty设置为 false?二传手都有一条线IsDirty=true;

我将 Prism框架与 Xamarin 4.0 一起使用,但 Prism 文档没有任何关于 ViewModel 生命周期的内容。

我的编辑构造函数如下所示:

public SomeDetailsViewModel(INavigationService navigationService) : base(navigationService)
{
Sample = new SampleDTO();
InitializeLookupValues();
_samplesService = new SampleService(BaseUrl);
TextChangedCommand = new Command(() => OnTextChanged());
AddSampleCommand = new Command(() => AddCurrentSample());
CancelCommand = new Command(() => Cancel());
IsDirty = false;
}

编辑3:

构造函数调用InitializeLookupValues()。这些似乎是罪魁祸首。

private async Task InitializeLookupValues()
{
App app = Prism.PrismApplicationBase.Current as App;
string baseUrl = app.Properties["ApiBaseAddress"] as string;
_lookupService = new LookupDataService(baseUrl);
int TbId = app.CurrentProtocol.TbId;
int accessionId = CollectionModel.Instance.Accession.AccessionId;
Parts = await _lookupService.GetParts(accessionId);//HACK
Containers = await _lookupService.GetSampleContainers(TbId);
Additives = await _lookupService.GetAdditives(TbId);
UnitsOfMeasure = await _lookupService.GetUnitsOfMeasure();

// with a few more awaits not included.
}

退出构造函数后,将设置每个属性。他们看起来像这个。

public ObservableCollection<PartDTO> Parts
{
get
{
return parts;
}
set
{
SetProperty(ref parts, value);
}
}
private PartDTO part;
public PartDTO SelectedPart
{
get
{
return part;
}
set
{
SetProperty(ref part, value);

IsDirty = true;
}
}

其中 IsDirty 的定义如下:

private bool isDirty;
public bool IsDirty
{
get
{
return isDirty;
}
set
{
SetProperty(ref isDirty, value);
Sample.DirtyFlag = value;
}
}

我还没有显式设置任何属性。我想避免它们被自动初始化,或者在它们之后调用一些东西。

Edit
只是给我一直在调试的每个人的笔记,以找出我能做什么。我发现在每个数据绑定属性中,getter 被调用两次,然后调用 setter。我查看了我能找到的生成代码,并且没有明显的数据绑定显式调用 setter 的位置。

编辑 2
我以前没有显示的内容,现在看起来这是一个关键信息,是我用对服务的异步调用填充ObservableCollection。据我所知,由于 XAML 数据绑定,调用了SelectedPart属性setter。如果我慢慢调试,这个开始在某些地方显示。我已经在上面添加了异步调用。

有没有办法在所有二传手之后将IsDirty设置为 false?

二传手不是自己叫的,总得有人叫他们。你应该确定谁在这样做,要么阻止他在没有充分理由的情况下设置东西(首选(,要么让他在他完成后重置脏标志。

正如注释中所建议的,在 setter 中添加断点并查看堆栈跟踪是查找设置源的良好起点......如果我不得不猜测,我会怀疑一些与导航相关的回调。

但是你应该尽量确保视图模型是在构造函数之后初始化的,并且IsDirty实际上意味着"已通过视图更改",而不是"可能被用户更改,也可能只是延迟初始化的一部分"。

在您多次编辑后,我的编辑:

您应该修改体系结构以考虑视图模型的异步初始化。只是并行运行所有内容并希望最好的很少奏效。

例如,您可以将属性设置为只读,直到初始化完成,并在InitializeLookupValues末尾将IsDirty设置为false

伪代码:

Constructor()
{
Task.Run( async () => await InitializeAsync() );
}
string Property
{
get => _backingField;
set
{
if (_isInitialized && SetProperty( ref _backingField, value ))
_isDirty = true;
}
}
private async Task InitializeAsync()
{
await SomeAsynchronousStuff();
_isInitialized = true;
}
private bool _isInitialized;
private bool _isDirty;

您可能希望将_isInitialized作为属性公开给视图以显示一些沙漏,并使用ManualResetEvent而不是简单的bool......但你明白了。

由于SetProperty方法都是可重写的,因此您可以注入一些自定义逻辑。当您有需要验证的对象是否已更改时,这可能非常有用。

public class StatefulObject : Prism.Mvvm.BindableBase
{
private bool _isDirty;
public bool IsDirty
{
get => _isDirty;
private set => SetProperty(ref _isDirty, value);
}
protected override bool SetProperty<T>(ref T storage, T value, Action onChanged, [CallerMemberName] string propertyName = null)
{
var isDirty = base.SetProperty(ref storage, value, onChanged, propertyName);
if(isDirty && propertyName != nameof(isDirty))
{
IsDirty = true;
}
return isDirty;
}
public void Reset() => IsDirty = false;
}

请记住,当您初始化此IsDirty中的字段时,将为 true,因此在绑定之前,您需要调用Reset方法将IsDirty设置回 false,这样您就可以可靠地知道字段何时被更改。

请注意,如何处理此问题在某种程度上取决于您。例如,您可以使用 Linq...

var fooDTOs = someService.GetDTOs().Select(x => { x.Reset(); return x; });

您还可以强制实施如下模式:

public class FooDTO : StatefulObject
{
public FooDTO(string prop1, string prop2)
{
// Set the properties...
Prop1 = prop1;
// Ensure IsDirty is false;
Reset(); 
}
}

相关内容

  • 没有找到相关文章

最新更新