简体   繁体   English

从 ViewModel 调用 View 的 CodeBehind 中的方法?

[英]Calling a Method in View's CodeBehind from ViewModel?

I have a method within the code behind of my View (this method does something to my UI).我在我的视图后面的代码中有一个方法(这个方法对我的 UI 做了一些事情)。

Anyway, I'd like to trigger this method from my ViewModel.无论如何,我想从我的 ViewModel 触发这个方法。 How could this be done?这怎么可能?

My (and maybe others?) difficulty with MVVM was to understand a simple thing: View knows about ViewModel .我(也许还有其他人?)使用 MVVM 的困难在于理解一个简单的事情: View 知道 ViewModel I was using bindings and commands, but they are simple strings in xaml .我使用的是绑定和命令,但它们是xaml中的简单strings Because of safe resolving at run-time (safe means you can do a typo, but software will not crash) this makes view decoupled from view-model (at compile time at least).由于在运行时安全解析(安全意味着您可以打错字,但软件不会崩溃),这使得视图视图模型分离(至少在编译时)。 And I was always looking for solution to keep this decoupling, to example, behaviors.我一直在寻找解决方案来保持这种脱钩,例如行为。

Truth is, you can get access directly to view model, which is typically a DataContext of window/user control:事实是,您可以直接访问视图模型,它通常是窗口/用户控件的DataContext

var vm = (MyViewModel)this.DataContext;

Knowing that, using events probably the best way to call view method from view model, because view model don't know if there is subscriber, it just firing that event and event can be used by view or another view model.知道这一点,使用事件可能是从视图模型调用视图方法的最佳方式,因为视图模型不知道是否有订阅者,它只是触发该事件并且事件可以被视图或其他视图模型使用。

// 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

You can do it like this in View (code behind).您可以在 View(代码隐藏)中这样做。

It casts to an interface to be implemented by the ViewModel, so that you are not constrained to one specific ViewModel type.它转换为要由 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(); }
        }
    }

According to MVVM pattern ViewModel is not aware of View, so this is not acceptable.根据MVVM 模式, ViewModel 不知道 View,所以这是不可接受的。 To interact with ViewModel View could trigger a command, also you can use bindings.与 ViewModel 交互视图可以触发命令,您也可以使用绑定。 Moreover, you should not move UI-specific things like BusyIndicator to ViewModel level.此外,您不应该将特定于 UI 的东西(例如 BusyIndi​​cator)移动到 ViewModel 级别。

Please provide more details regardign your concrete use case - when you want to call a View's method and what this method does.请提供更多关于您的具体用例的详细信息 - 当您想要调用视图的方法以及该方法的作用时。

Let's say you have a method within the code behind of my Login View, that updates UI by bringing Focus to the PasswordEntry if login fails, then the easiest & most universal way to trigger this method from your ViewModel is using Action delegates .假设您在我的登录视图背后的代码中有一个方法,它通过在登录失败时将焦点移到 PasswordEntry 来更新 UI,那么从您的 ViewModel 触发此方法的最简单和最通用的方法是使用Action delegates

As you can see in this sample , all you need to add, where your services determine that the login has failed and you want the Password Entry to get the focus, is two lines of code in your ViewModel and an action handler in your View.正如您在此示例中所见,您需要添加的所有内容(您的服务确定登录失败并且您希望密码条目获得焦点)是您的 ViewModel 中的两行代码和您的视图中的一个操作处理程序。

ViewModel code:视图模型代码:

  • Declaration of the event: public Action<bool> OnLoginFailed { get; set; }事件声明: public Action<bool> OnLoginFailed { get; set; } public Action<bool> OnLoginFailed { get; set; } public Action<bool> OnLoginFailed { get; set; } & public Action<bool> OnLoginFailed { get; set; } &
  • Then simply, when needed, executing this OnLoginFailed?.Invoke(true);然后简单地,在需要时,执行这个OnLoginFailed?.Invoke(true);

View code:查看代码:

ViewModel.OnLoginFailed = ((obj) =>
{
    PasswordEntry.Focus();
});

I saw youre reply to the answer above, you are saying that you want your ViewModel to retrieve data and then tell your view to stop the busy indicator.我看到您对上述答案的回复,您是说您希望 ViewModel 检索数据,然后告诉您的视图停止繁忙指示器。

I'm not sure if my solution would be the best solution, but you can give it a try, and maybe someone can correct if I'm wrong.我不确定我的解决方案是否是最好的解决方案,但是您可以尝试一下,如果我错了,也许有人可以纠正。

So from your view, you would call a method from ViewModel to start reading the dataset, am I right?所以从你的角度来看,你会从 ViewModel 调用一个方法来开始读取数据集,对吗? In this method, you can pass a delegate (pointing to a method that exists in your view) and when your ViewModel finishes reading the dataset from the server, trigger the delegate (from your viewmodel) that is linked to your method in your view that can stop the busy indicator.在此方法中,您可以传递一个委托(指向视图中存在的方法),当您的 ViewModel 完成从服务器读取数据集时,触发链接到您视图中的方法的委托(来自您的视图模型)可以停止忙指示灯。

so in your view you have所以在你看来你有

void StopBusyIndicator()
{
    this.BusyIndicator.IsBusy = false;
}

and when you call your ViewModel to read dataset,当你调用你的 ViewModel 来读取数据集时,

call it like this:像这样称呼它:

ViewModel.ReadDataSet( ()= >StopBusyIndicator)

which will pass the StopBusyIndicator method as a delegate, which you can call at the end of your ReadDataSet.它将作为委托传递 StopBusyIndi​​cator 方法,您可以在 ReadDataSet 的末尾调用它。

HTH HTH

You could write an action class that accepts a Data Transfer object.您可以编写一个接受数据传输对象的操作类。 Within the DTO, add a property called "View" and assign it the current view.在 DTO 中,添加一个名为“View”的属性并将其指定为当前视图。 Call the action via the controller from within your view's codebehind, unbox the DTO and now you have full control of the view within the action class.从视图的代码隐藏中通过控制器调用操作,取消装箱 DTO,现在您可以完全控制操作类中的视图。

If you truely want to do this in your model, just create the method with a "View" type parameter in your Model and execute it, passing in the current view.如果您真的想在模型中执行此操作,只需在模型中创建具有“视图”类型参数的方法并执行它,并传入当前视图。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM