MVVMCross:如何将Xamarin.Android事件绑定到ViewModel命令

7
我正在尝试从一个活动转到另一个活动。我仍在学习MVVMCross,所以整个模式对我来说仍然很新。目前,我只在Xamarin.Android上应用它。
设置如下:
1. MainDashboardActivity具有Android Design Support库的NavigationView。 2. ViewModel MainDashboardViewModel具有IMvxCommand GoToSecondDashboard,它只是一个简单的ShowViewModel到另一个活动。
NavigationView具有NavigationItemSelected事件。通常,我会这样做:
navigationView.NavigationItemSelected += (o, e) =>
{
    if(e.MenuItem.ItemId == Resource.Id.SecondDashboardMenu)
    {
        // make new intent to target activity
    }
};

现在我已经将导航逻辑嵌入到ViewModel的IMvxCommand中,并且我想将其绑定到NavigationView的事件上,不再创建意图和诸如此类的内容。我该如何实现这一点?
我想在代码文件中使用流畅的绑定逻辑,而不是在布局中,就像this answer所做的那样:
protected override void OnViewModelSet()
{
    SetContentView(Resource.Layout.View_Tip);

    var edit = this.FindViewById<EditText>(Resource.Id.FluentEdit);

    var set = this.CreateBindingSet<TipView, TipViewModel>();
    set.Bind(edit).To(vm => vm.SubTotal);
    set.Apply();

    // for non-default properties use 'For':
    // set.Bind(edit).For(ed => ed.Text).To(vm => vm.SubTotal);

    // you can also use:
    //   .WithConversion("converter", "optional parameter")
    //   .OneTime(), .OneWay() or .TwoWay()
}

但是NavigationItemSelected是一个事件。我无法找到一种将事件绑定到命令的方法。在此之前还有过滤ItemId的逻辑,因此这不会是一个简单的事件到命令的绑定。

我不确定这是否是解决这个问题的正确方法。我只想在代码文件中绑定菜单点击到命令,而不是布局文件中。


为了确保,您不想在布局文件中进行绑定吗?我认为正常的做法是:
  1. 为菜单项定义一个模型。
  2. 在您的视图模型中为菜单项定义一个列表。
  3. 为菜单项定义一个布局。
  4. 将菜单项绑定到导航视图上的布局。
  5. 将菜单项的布局设置为您的导航视图。
  6. 定义一个期望菜单项类型的命令。
  7. 将命令绑定到导航视图。
虽然如果您不想在布局中进行操作,我会看看是否能找到我代码中进行操作的部分。
- Noires
我习惯于在代码本身中进行绑定,因为我的(有限的)经验来自直接处理Android,并且它们倾向于从代码中进行绑定。用代码混淆布局对我来说感觉有点陌生,所以就是这样。我所知道的NavigationView很少,它带有一个标题的布局和一个菜单.xml文件(不是布局),那么我们如何绑定它们呢?是否有此过程的示例? - batrand
我会看看是否能稍后发布一个示例,或者询问我的同事谁已经实现了它。虽然它可能感觉有点陌生,但是如果你习惯了,它确实很舒适。我现在只在特殊情况下使用代码绑定。尽管两者都有积极和消极的方面。 - Noires
2个回答

6

由于没有为 NavigationView 定义绑定目标,您将无法像Cyriac在他的帖子中描述的那样进行绑定。

目标绑定在内部的作用就是订阅事件并对其进行反应,并将该数据公开为属性。

因此,由于当前无法将 ItemsSourceNavigationView 绑定,所以您必须像已经做的那样,将 EventHandler 钩子连接到 event 中,并直接调用您的 ViewModel,即调用命令。 这看起来像这样:

navigationView.NavigationItemSelected += ItemSelected;

private void ItemSelected(object sender, NavigationItemSelectedEventArgs args)
{
    ViewModel.NavigateCommand.Execute(args.MenuItem.TitleFormatted.ToString());
}

然后在您的ViewModel中的Command中:

private void DoNavigateCommand(string title)
{
    if (title == "Derp")
        ShowViewModel<DerpViewModel>();
}

或者你可以将这段代码封装在目标绑定中。你可以查看官方MvvmCross github存储库中的实现。


哦,我有点想象绑定是为了它添加的。好知道!谢啦! - Noires
太棒了!听起来就是我想要的。我会在周一进行验证! - batrand
好的,它能工作了,但与您的代码有些不同。我不得不用 (ViewModel as MainDashboardViewModel).NavigateToViewModel("second").Execute(); 转换 ViewModel,在 ViewModel 中,NavigateToViewModel 是一个简单的函数,返回一个命令供 Activity 直接执行。您是如何设置 ViewModel 的,以便 Activity 可以像您一样直接执行命令呢? - batrand
哎呀,我错了,ViewModel甚至不需要被转换。 - batrand

3

我在http://crosscuttingconcerns.com/MvvmCross-Fluent-Databinding上找到了别人的答案,你应该试一试。 我认为你不能直接引用事件,而是必须使用字符串。

protected override void OnViewModelSet ()
{
        SetContentView (Resource.Layout.TermsPage);

        var set = this.CreateBindingSet<TermsView, TermsViewModel>();
        set.Bind(FindViewById<Button>(Resource.Id.acceptTermsButton))
            .For("Click")
            .To(vm => vm.AcceptTermsCommand);
        set.Apply();
}

当然,你需要根据你的事件进行调整。


我已经下班了,所以现在无法测试...周一会更新! - batrand

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接