我在 View 后面的代码中有一个方法(这个方法对我的 UI 有一些作用)。
无论如何,我想从我的 ViewModel 中触发这个方法。怎么可能做到这一点?
我(也许还有其他人?)对 MVVM 的困难是理解一个简单的事情:View 知道 ViewModel。我正在使用绑定和命令,但它们strings
在xaml
. 由于在运行时安全解析(安全意味着您可以打错字,但软件不会崩溃),这使得视图与视图模型分离(至少在编译时)。我一直在寻找解决方案来保持这种脱钩,例如行为。
事实是,您可以直接访问视图模型,这通常是DataContext
窗口/用户控件:
var vm = (MyViewModel)this.DataContext;
知道这一点,使用事件可能是从视图模型调用视图方法的最佳方式,因为视图模型不知道是否有订阅者,它只是触发该事件并且事件可以被视图或其他视图模型使用。
// define in the view model
public delegate void MyEventAction(string someParameter, ...);
public event MyEventAction MyEvent;
// rise event when you need to
MyEvent?.Invoke("123", ...);
// in the view
var vm = (MyViewModel)DataContext;
vm.MyEvent += (someParameter, ...) => ... // do something
您可以在 View 中这样做(代码隐藏)。
它转换为由 ViewModel 实现的接口,因此您不受限于一种特定的 ViewModel 类型。
// CONSTRUCTOR
public SomeView()
{
InitializeComponent();
DataContextChanged += DataContextChangedHandler;
}
void DataContextChangedHandler(object sender, DependencyPropertyChangedEventArgs e)
{
var viewModel = e.NewValue as IInterfaceToBeImplementedByViewModel;
if (viewModel != null)
{
viewModel.SomeEvent += (sender, args) => { someMethod(); }
}
}
根据MVVM 模式ViewModel 不知道 View,所以这是不可接受的。与 ViewModel 交互可以触发命令,也可以使用绑定。此外,您不应将特定于 UI 的内容(例如 BusyIndicator)移至 ViewModel 级别。
请提供有关您的具体用例的更多详细信息-何时要调用视图的方法以及该方法的作用。
我看到你对上面答案的回复,你说你希望你的 ViewModel 检索数据,然后告诉你的视图停止繁忙的指示器。
我不确定我的解决方案是否是最好的解决方案,但是您可以尝试一下,如果我错了,也许有人可以纠正。
所以从你的观点来看,你会从 ViewModel 调用一个方法来开始读取数据集,对吗?在此方法中,您可以传递一个委托(指向您的视图中存在的方法),当您的 ViewModel 完成从服务器读取数据集时,触发链接到您的视图中的方法的委托(来自您的视图模型)可以停止忙碌指示灯。
所以在你看来你有
void StopBusyIndicator()
{
this.BusyIndicator.IsBusy = false;
}
当您调用 ViewModel 来读取数据集时,
像这样称呼它:
ViewModel.ReadDataSet( ()= >StopBusyIndicator)
它将作为委托传递 StopBusyIndicator 方法,您可以在 ReadDataSet 结束时调用该方法。
高温高压
您可以编写一个接受数据传输对象的操作类。在 DTO 中,添加一个名为“View”的属性并将其分配给当前视图。从视图的代码隐藏中通过控制器调用操作,取消装箱 DTO,现在您可以完全控制操作类中的视图。
如果您真的想在模型中执行此操作,只需在模型中创建带有“视图”类型参数的方法并执行它,并传入当前视图。
假设您在我的登录视图后面的代码中有一个方法,如果登录失败,它会通过将焦点带到 PasswordEntry 来更新 UI,那么从您的 ViewModel 触发此方法的最简单和最通用的方法是使用Action delegates。
正如您在此示例中所见,您需要添加的只是 ViewModel 中的两行代码和 View 中的操作处理程序,您的服务确定登录失败并且您希望 Password Entry 获得焦点。
视图模型代码:
public Action<bool> OnLoginFailed { get; set; }
&OnLoginFailed?.Invoke(true);
查看代码:
ViewModel.OnLoginFailed = ((obj) =>
{
PasswordEntry.Focus();
});