如果您使用的是 MVVM 模式,您可以执行以下操作:
为记录的视图模型创建一个抽象
public interface ILoggedViewModel : INotifyPropertyChanged
{
bool IsChangingProgrammatically { get; }
}
您必须记录的每个视图模型(很可能是每个视图模型)都将实现此接口(包括由INotifyPropertyChanged 定义的PropertyChanged 事件)。当您创建视图模型时(一个非常好的解决方案是 DI,例如使用 Prisms ViewModelLocator - 也许您必须手动注册视图模型)您使用订阅 PropertyChanged 事件的类注册它们。
在以编程方式更改任何属性之前,您将IsChangingProgrammatically 设置为true,然后将其重置(可能在finally-block 中,以防止在发生异常时不会重置属性)。每当引发PropertyChangedEvent 时,都会调用记录器上的处理程序。然后记录器检查发件人上的IsChangingProgrammatically,如果是false,则假定更改已由用户启动并记录活动。
编辑:
为了使其正常工作,您不能在开始长时间运行操作之前设置IsChangingProgrammatically,然后再将其重置,而只能围绕设置值,否则长时间运行操作期间的用户交互不会记录下来。
private async void UpdateData()
{
try
{
var data = await _repository.GetData();
this.IsChangingProgrammatically = true;
Data = data;
}
finally
{
this.IsChangingProgrammatically = false;
}
}
编辑 2:
有一种方法可以避免这些时间问题。您可以从 PropertyChangedEventArgs 创建一个派生类,该派生类被抛出以指示属性已以编程方式更改
class PropertyChangedProgrammaticallyEventArgs : PropertyChangedEventArgs
{
public PropertyChangedProgrammaticallyEventArgs (string propertyName) : base(propertyName) { }
}
在您的基本视图模型中,您声明类似
void ChangePropertyBackingField<T>(ref T backingField, string propertyName, T value)
{
backingField = value;
PropertyChanged?.Invoke(this, new PropertyChangedProgrammaticallyEventArgs(propertyName));
}
当属性以编程方式更改但仍导致 UI 更新时,创建一个特殊的 PropertyChangedEventArgs 实现。通过这种方式,您可以根据记录器中的其他更改更改编程更改
void OnPropertyChanged(object sender, PropertyChangedEventArgs eventArgs)
{
if(!(eventArgs is PropertyChangedProgrammaticallyEventArgs ))
{
// log user interaction
}
}