public class MyModel : INotifyPropertyChanged
{
private MyEnum _myEnumValue;
public MyEnum MyEnumValue
{
get => _myEnumValue;
set
{
if (value == _myEnumValue) return;
_myEnumValue = value;
OnPropertyChanged();
}
}
//OnPropertyChanged things
}
public class MainViewModel
{
private MyModel _myModelVariable;
public MyModel MyModelVariable
{
get=>_myModelVariable;
set{_myModelVariable = value; OnPropertyChanged();}
}
public MainViewModel()
{
MyModelVariable = new();
MyModelVariable.MyEnumValue = MyEnum.Enum1;
}
public void OptionsButton()
{
_windowManager.ShowDialogAsync(new EditViewModel(MyModelVariable));
}
}
public class EditViewModel : INotifyPropertyChanged
{
private MyModel _localModel;
public EditClass(MyModel myModel)
{
_localModel = myModel
}
public MyModel LocalModel
{
get=>_localModel;
set
{
_localModel = value;
OnPropertyChanged();
}
}
public void OkButton()
{
// sending LocalModel to Main View Model with EventAggregator
}
public void CancelButton()
{
CloseWindow();
}
}
给定的代码是根据原始代码模拟的。 在我的 WPF 应用程序中,我使用 Caliburn Micro 并使用视图模型。在我的屏幕之一上,用户按下选项按钮来更新值。当按下按钮时,会打开一个新窗口,该窗口由 EditViewModel 控制。我使用“new”创建窗口并使用 IWindowManager 打开它。打开它时,我发送了我的原始模型。在 EditViewModel 中,我将原始模型分配给局部变量并对其执行操作。绑定前端ComboBox中的Selected值。当用户更改它时,我也会更改局部变量。如果用户按“确定”,我将使用 EventAggregator 将新值发送到 MainViewModel。如果他们按“取消”,我将关闭窗口而不执行任何操作。
问题:当用户改变EditViewModel前端的值时,MainViewModel中原始模型的值也会改变。这是一种可能的情况,因为它将原始值视为参考,但我不希望这种情况发生。我尝试过克隆、复制等解决方案,但无法解决。您有什么建议?
问题解决了
类是引用类型。我将变量添加到 EditViewModel。 我在构造函数中将 _localModel.MyEnumValue 分配给 _myEnum 并解决了问题。在我看来,Enum 是值类型,因此我将 MyModel 的引用类型转换为值类型。private MyEnum _myEnum
为什么你的模型参考是
public
,你绑定到它们了吗?您是否知道模型不能暴露给视图?您必须确保编辑原始实例的副本。编辑过程完成后,您将更新原始实例。
关键是在可编辑对象中添加一个中间数据层,以启用临时数据状态(编辑和提交等编辑流程)和数据回滚。
我建议实现
IEditableObject
界面。
以下示例展示了如何编辑数据并支持提交或取消临时编辑。如果取消,对象的数据更改将恢复。
该示例还展示了如何正确处理对话框和应用程序模型。
ChildViewModel.cs
可编辑的数据对象。
public class ChildViewModel : IEditableObject, INotifyPropertyChanged
{
private class ChildViewModelEditableDataModel : ICloneable
{
public ChildViewModelEditableDataModel()
{
}
// Copy constructor for creating a deep copy
public ChildViewModelEditableDataModel(ChildViewModelEditableDataModel myClassDataModelToCopy)
{
this.NumericValue = myClassDataModelToCopy.NumericValue;
this.TextValue = myClassDataModelToCopy.TextValue;
}
public int NumericValue { get; set; }
public string TextValue { get; set; }
// Create a shallow copy
public ChildViewModelEditableDataModel Clone() => (ChildViewModelEditableDataModel)MemberwiseClone();
// Create a deep copy
public ChildViewModelEditableDataModel DeepClone() => new ChildViewModelEditableDataModel(this);
object ICloneable.Clone() => Clone();
}
public ChildViewModel()
{
this.CurrentDataModel = new ChildViewModelEditableDataModel();
this.BackupDataModel = this.CurrentDataModel.DeepClone();
}
public void BeginEdit() => this.BackupDataModel = this.CurrentDataModel;
public void CancelEdit() => this.CurrentDataModel = this.BackupDataModel;
public void EndEdit() => OnDataChanged();
protected virtual void OnDataChanged()
=> this.DataChanged?.Invoke(this, EventArgs.Empty);
public event EventHandler DataChanged;
public int NumericValue
{
get => this.CurrentDataModel.NumericValue;
set => this.CurrentDataModel.NumericValue = value;
}
public string TextValue
{
get => this.CurrentDataModel.TextValue;
set => this.CurrentDataModel.TextValue = value;
}
private ChildViewModelEditableDataModel CurrentDataModel { get; set; }
private ChildViewModelEditableDataModel BackupDataModel { get; set; }
}
MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
public ChildViewModel ChildViewModel { get; }
// Do not expose the Model to the View
private SomeModelClass SomeModelClass { get; }
public MainViewModel()
{
this.ChildViewModel = new ChildViewModel();
// React to committed data changes of an edit procedure
this.ChildViewModel.DataChanged += OnChildViewModelDataChanged;
this.SomeModelClass = new SomeModelClass();
}
private void OnChildViewModelDataChanged(object? sender, EventArgs e)
{
// Update the Model with the modified data (example).
// Make sure to not pass the view model instance.
// Instead pasas a model type that is defined in the Model.
this.SomeModelClass.Update(this.ChildViewModel.TextValue, this.ChildViewModel.NumericValue);
}
}
MainWindow.xaml.cs
partial class MainWindow : Window
{
public static RoutedCommand ShowEditDialogCommand = new RoutedCommand(nameof(ShowEditDialogCommand), typeof(MainWindow));
public MaainWindow()
{
InitializeComponent();
var showEditDialogCommandBinding = new CommandBinding(ShowEditDialogCommand, ExecuteShowEditDialogCommand, CanExecuteShowEditDialogCommand);
this.CommandBindings.Add(showEditDialogCommandBinding);
this.DataContext = new MainViewModel();
}
private void CanExecuteShowEditDialogCommand(object sender, CanExecuteRoutedEventArgs e)
=> e.CanExecute = e.Parameter is IEditableObject;
private void ExecuteShowEditDialogCommand(object sender, ExecutedRoutedEventArgs e)
{
IEditableObject editableData = (IEditableObject)e.Parameter;
var editDialog = new Window() { DataContext = editableData };
editableData.BeginEdit();
bool? dialogResult = editDialog.ShowDialog();
if (dialogResult == true)
{
editableData.EndEdit();
}
else
{
editableData.CancelEdit();
}
}
}
MainWindow.xaml
<Window>
<StackPanel DataContext="{Binding ChildViewModel}">
<TextBox Text="{Binding TextValue}" />
<TextBox Text="{Binding NumericValue}" />
<!-- Trigger the dialog to edit the data -->
<Button Content="Edit"
Command="{x:Static MainWindow.ShowEditDialogCommand}"
CommandParameter="{Binding}" />
</StackPanel>
</Window>