从视图模型绑定到 ListView 项被点击的属性

16

我想绑定ListView的itemtapped属性到我的菜单页面,以便触发一个事件。我正在使用MVVM(Xamarin form labs)框架开发我的应用程序。我的目标是,当用户点击菜单项时,应用程序能够导航到正确的视图。

以下是XAML代码:

<ListView x:Name="list"
        ItemsSource="{Binding MenuItems}" 
        SelectedItem="{Binding SelectedItem}" 
        ItemTapped= SET-BINDING-HERE >
  <ListView.ItemTemplate>
    <DataTemplate>
      <ViewCell>
        <ViewCell.View>
          //setup template here
        </ViewCell.View>
      </ViewCell>
    </DataTemplate>
  </ListView.ItemTemplate> 
</ListView>

我想将itemtapped事件绑定到这个函数:

public void NavigateTo(object sender, ItemTappedEventArgs args)
  {
      var test = args.Item as MenuModel;
      cPageTypes.GetByKey(test.CommandParameter)
                .SwitchRootPage(AIMCore.ViewModels.ElliottBaseViewModel.MasterPage);
      list.SelectedItem = null;
      AIMCore.ViewModels.BaseViewModel.MasterPage.IsPresented = false;
  }

如果我将函数添加到视图的代码后面并设置ItemTapped='NavigatTo',则可以使其正常工作,但这似乎是错误的,因为它违背了MVVM的概念。我真正想做的是在我的ViewModel中绑定事件,实现相同的功能,就像这样:

<ListView x:Name="list"
        ItemsSource="{Binding MenuItems}" 
        SelectedItem="{Binding SelectedItem}" 
        ItemTapped= "{Binding NavigateTo}" > // this binding is to the ViewModel

然而,这并没有起作用或者我没有正确地执行它。当我尝试以这种方式实现时,代码会产生错误。

错误:Xamarin.Forms.Xaml.XamlParseException:在Xamarin.Forms.Xaml.BaseValueNode.SetPropertyValue中找不到名称为ItemTapped的属性。

2个回答

26

我采用了相同的架构,通过创建自定义列表控件,并创建了1个可绑定属性与命令。我使用以下代码覆盖了我的视图模型中的该命令:

在我的PCL中的自定义控件[.cs]页面

using System;
using System.Windows.Input;
using Xamarin.Forms;


namespace YourNS {

    public class ListView : Xamarin.Forms.ListView {

        public static BindableProperty ItemClickCommandProperty = BindableProperty.Create<ListView, ICommand>(x => x.ItemClickCommand, null);


        public ListView() {
            this.ItemTapped += this.OnItemTapped;
        }


        public ICommand ItemClickCommand {
            get { return (ICommand)this.GetValue(ItemClickCommandProperty); }
            set { this.SetValue(ItemClickCommandProperty, value); }
        }


        private void OnItemTapped(object sender, ItemTappedEventArgs e) {
            if (e.Item != null && this.ItemClickCommand != null && this.ItemClickCommand.CanExecute(e)) {
                this.ItemClickCommand.Execute(e.Item);
                this.SelectedItem = null;
            }
        }
    }
}

我的XAML页面

<ContentPage ...
             xmlns:local="clr-namespace:Samples.Views;assembly=Your Assebly Name">

<local:ListView ItemClickCommand="{Binding Select}" 
        ItemsSource="{Binding List}">
在我的视图模型中 [在这个例子中,我只打开了对话框操作表]
private Command<Signature> selectCmd;
        public Command<Signature> Select {
            get {
                this.selectCmd = this.selectCmd ?? new Command<Signature>(s => 
                    this.dialogs.ActionSheet(new ActionSheetConfig()
                        .Add("View", () => {
                            if (!this.fileViewer.Open(s.FilePath))
                                this.dialogs.Alert(String.Format("Could not open file {0}", s.FileName));
                        })
                        .Add("Cancel")
                    )
                );
                return this.selectCmd;
            }
        }

我认为这比我目前拥有的更好,正在考虑开发类似的东西(试图限制代码共享的渲染次数)。我还有一个问题,添加命令参数是否与添加项目单击命令相同?我希望能够将单击的项传递给下一个视图。 - Jared Reeves
抱歉,我知道这是一个老问题,但我遇到了以下错误:'Xamarin.Forms.ListView'不包含名为'ItemClickCommand'的定义,并且没有接受类型为'Xamarin.Forms.ListView'的第一个参数的扩展方法'ItemClickCommand'可以找到(是否缺少使用指令或程序集引用?),有什么想法吗?我已经添加了所有相关的引用,但智能感知没有显示ItemClickCommand。谢谢。 - Thierry
2
@Thierry:你使用的是 <ListView> 还是 local:ListView?ItemClickCommand 在 Xamarin.Forms 自带的 ListView 上没有定义。这里的答案继承了 ListView 来创建自己的自定义 ListView,新增了一个名为 ItemClickCommand 的属性。要使用它,你需要在 XAML 文件的顶部添加正确的 xmlns 定义,并使用 local:ListView 或 <[whatever]:ListView>,其中 [whatever] 是你定义的 xmlns。 - NovaJoe
在我的PCL上完美运行。 - Jon S
不要忘记支持CachingStrategy。当您填充了CachingStrategy属性时,需要实现额外的构造函数以防止“.....ctor' not found.” Xamarin异常。将以下构造函数添加到自定义ListView代码中。public ListViewWithCommands(ListViewCachingStrategy cachingStrategy) : base(cachingStrategy) { ItemTapped += ListViewWithCommands_ItemTapped; } - Ton Snoei
此外,当执行 this.ItemClickCommand.Execute(e.Item) 时,e.item 在 CanExecute 调用中也必须被使用。因此,请使用:this.ItemClickCommand.CanExecute(e.Item) 而不是 this.ItemClickCommand.CanExecute(e) - Ton Snoei

8

或者,您可以使用附加行为(Attached Behavior)

public static class ListViewAttachedBehavior
{
    public static readonly BindableProperty CommandProperty =
        BindableProperty.CreateAttached (
            "Command",
            typeof(ICommand),
            typeof(ListViewAttachedBehavior),
            null,
            propertyChanged:OnCommandChanged);

    static void OnCommandChanged (BindableObject view, object oldValue, object newValue)
    {
        var entry = view as ListView;
        if (entry == null) 
            return;

        entry.ItemTapped += (sender, e) => 
            {
                var command = (newValue as ICommand);
                if(command == null)
                    return;

                if(command.CanExecute(e.Item))
                {
                    command.Execute(e.Item);
                }

            };
    }
}

然后在你的ListView上调用它。
 <ListView
        RowHeight="70"
        x:Name="AcquaintanceListView"
        b:ListViewAttachedBehavior.Command="{Binding ItemSelectedCommand}"
        ItemsSource="{Binding Acquaintances}">

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