问题
I've found that the common way of defining view models doesn't differentiate between changes by the user and changes by the system. For example,
public class PersonViewModel : ViewModelBase
{
private string _name;
public string Name
{
get => _name;
set => Set(ref _name, value);
}
}
Here, the PropertyChanged notification will be raised if the user changes the name in the view. But it will also be raised if the application changes the property for some reason (for example, during initialization or as a response to some event).
I may want to respond differently depending on whether the user made the change or the system made the change. For example, I may want to log only changes made by the user for auditing purposes.
Have others have had to make this distinction, and how did you implement it?
At the moment, the best solution I've come up with is to send a message when the user makes a change, but when the application needs to make a change, it should call a setter method:
public class PersonViewModel : ViewModelBase
{
private string _name;
public string Name
{
get => _name;
set
{
if (SetName(value))
MessengerInstance.Send(new SetNameRequest(this));
}
}
public bool SetName(string value) =>
Set(nameof(Name), ref _name, value);
}
In either case, the PropertyChanged event is raised, so that the view is updated whether or not the user made the change. When the user makes the change, I can handle the message sent in a certain way (perhaps executing a specific use case). When the system makes the change, it just updates the view.
Update: I found a possible solution here. In the case of a TextBox
, the key idea is to set NotifyOnSourceUpdated
to True
, which causes the event SourceUpdated
to fire when the TextBox
updates the binding. I can then invoke a command when this event occurs:
<TextBox Text="{Binding Name, NotifyOnSourceUpdated=True}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SourceUpdated">
<i:InvokeCommandAction Command="{Binding SetNameCommand, Mode=OneWay}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
In my view model, I can handle the command in whatever way I want. In my case, I send a message, so that it's handled by the appropriate use case:
public class PersonViewModel : ViewModelBase
{
private string _name;
public string Name
{
get => _name;
set => Set(ref _name, value);
}
public ICommand SetNameCommand => new RelayCommand(() =>
MessengerInstance.Send(new SetNameRequest(this)));
}
In this way, setting the Name
property from code updates the view but doesn't invoke the command. However, if the user changes the Name
property via the TextBox
, a command is invoked and eventually handled (in addition to updating the view because the PropertyChanged
event was raised).
来源:https://stackoverflow.com/questions/52821280/in-mvvm-how-do-i-distinguish-between-user-changes-and-system-changes-to-the-vie