.NET MAUI绑定ListView的ItemSelected事件到ViewModel

2023-11-29

我正在尝试将 ListView 的 ItemSelected 绑定到视图模型,但遇到了一些问题(由于我自己对其工作原理的误解)。

我有观点:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:Local="clr-namespace:FireLearn.ViewModels"
             x:Class="FireLearn.MainPage"
             Title="Categories">

    <ContentPage.BindingContext>
        <Local:CategoryViewModel/>
    </ContentPage.BindingContext>
    <NavigationPage.TitleView>
        <Label Text="Home"/>
    </NavigationPage.TitleView>
    <ListView
        ItemsSource="{Binding Categories}"
        HasUnevenRows="True"
        IsPullToRefreshEnabled="True"
        IsRefreshing="{Binding ListRefreshing, Mode=OneWay}"
        RefreshCommand="{Binding RefreshCommand}"
        ItemSelected="{Binding OnItemTappedChanged}"
        SelectionMode="Single"
        SelectedItem="{Binding SelectedCategory}">

        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <HorizontalStackLayout
                        Padding="8"
                        VerticalOptions="Fill"
                        HorizontalOptions="Fill">

                        <Image Source="cafs_bubbles.png"    
                               HeightRequest="64"
                               MaximumWidthRequest="64"
                               HorizontalOptions="CenterAndExpand"
                               VerticalOptions="CenterAndExpand"/>

                        <VerticalStackLayout
                            Padding="8"
                            VerticalOptions="FillAndExpand"
                            HorizontalOptions="FillAndExpand">
                            <Label Text="{Binding FormattedName}" 
                                       SemanticProperties.HeadingLevel="Level1"
                                       FontSize="Title"
                                       HorizontalOptions="Start"/>
                            <Label Text="{Binding ItemCount}" 
                                   FontSize="Subtitle"/>
                            <Label Text="{Binding Description}" 
                                   HorizontalOptions="Center"
                                   LineBreakMode="WordWrap"
                                   FontSize="Caption"
                                   VerticalOptions="CenterAndExpand"
                                   MaxLines="0"/>
                        </VerticalStackLayout>
                    </HorizontalStackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

这链接到视图模型:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FireLearn.Models;

namespace FireLearn.ViewModels
{
    public partial class CategoryViewModel : ObservableObject
    {
        public ObservableCollection<CategoryModel> categories = new ObservableCollection<CategoryModel>();

        public ObservableCollection<CategoryModel> Categories
        {
            get => categories;
            set => SetProperty(ref categories, value);
        }

        public bool listRefreshing = false;
        public bool ListRefreshing
        {
            get => listRefreshing;
            set => SetProperty(ref listRefreshing, value);
        }

        public CategoryModel selectedCategory = new CategoryModel();
        public CategoryModel SelectedCategory
        {
            get => selectedCategory;
            set
            {
                SetProperty(ref selectedCategory, value);
               // Tap(value);
            }
        }

        public RelayCommand RefreshCommand { get; set; }
        //public RelayCommand TapCellCommand { get; set; }

        public CategoryViewModel()
        {
            loadFromSource();
            RefreshCommand = new RelayCommand(async () =>
            {
                Debug.WriteLine($"STARTED::{ListRefreshing}");
                if (!ListRefreshing)
                {
                    ListRefreshing = true;
                    try
                    {
                        await loadFromSource();
                    }
                    finally
                    {
                        ListRefreshing = false;
                        Debug.WriteLine($"DONE::{ListRefreshing}");
                    }
                }
            });
        }

        public async Task loadFromSource()
        {
            HttpClient httpClient = new()
            {
                Timeout = new TimeSpan(0, 0, 10)
            };

            Uri uri = new Uri("https://somewebsite.co.uk/wp-json/wp/v2/categories");

            HttpResponseMessage msg = await httpClient.GetAsync(uri);

            if (msg.IsSuccessStatusCode)
            {
                var result = CategoryModel.FromJson(await msg.Content.ReadAsStringAsync());
                Categories = new ObservableCollection<CategoryModel>(result);
            }

            Debug.WriteLine("List Refreshed");
        }

        public void OnItemTappedChanged(System.Object sender, Microsoft.Maui.Controls.SelectedItemChangedEventArgs e)
        {
            var x = new ShellNavigationState();
            
            Shell.Current.GoToAsync(nameof(NewPage1),
                new Dictionary<string, object>
                {
                    {
                        nameof(NewPage1),
                        SelectedCategory
                    }
                });
        }
    }
}

我收到编译器错误“没有找到“ItemSelected”的属性、BindableProperty 或事件,或者值和属性之间的类型不匹配”,并且我真的不确定如何解决。如果我让 XAML 为我创建一个新事件,它会将其添加到 MainPage.Xaml.Cs 而不是 VM 中


ItemSelected需要一个事件处理程序,该事件处理程序通常只存在于视图的代码后面。由于 ViewModel 不应该了解有关 View 的任何信息,因此最好不要混合概念。您有几种选择可以解决这个问题而不破坏MVVM模式.

选项 1:使用事件处理程序并调用 ViewModel 的方法

首先,通过构造函数传入 ViewModel 来设置背后的代码,并添加事件处理程序,例如:

public partial class MainPage : ContentPage
{
    private CategoryViewModel _viewModel;

    public MainPage(CategoryViewModel viewModel)
    {
        _viewModel = viewModel;
    }

    public void OnItemSelectedChanged(object sender, SelectedItemChangedEventArgs e)
    {
        //call a method from the ViewModel, e.g.
        _viewModel.DoSomething(e.SelectedItem);
    }

    //...
}

然后使用 XAML 中的事件处理程序:

<ListView
    ItemsSource="{Binding Categories}"
    HasUnevenRows="True"
    IsPullToRefreshEnabled="True"
    IsRefreshing="{Binding ListRefreshing, Mode=OneWay}"
    RefreshCommand="{Binding RefreshCommand}"
    ItemSelected="OnItemSelectedChanged"
    SelectionMode="Single"
    SelectedItem="{Binding SelectedCategory}">

    <!-- skipping irrelevant stuff -->

</ListView>

请注意,这确实not使用绑定。

In your CategoryViewModel然后,您可以定义一个方法,将所选项目作为参数:

public partial class CategoryViewModel : ObservableObject
{
    //...

    public void DoSomething(object item)
    {
        //do something with the item, e.g. cast it to Category
    }
}

选项 2:使用 EventToCommandBehavior

您还可以使用以下方法,而不是从后面的代码中处理 ViewModel 方法的调用事件到命令行为来自 MAUI 社区工具包:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:Local="clr-namespace:FireLearn.ViewModels"
             xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
             x:Class="FireLearn.MainPage"
             Title="Categories">

    <ContentPage.Resources>
         <ResourceDictionary>
             <toolkit:SelectedItemEventArgsConverter x:Key="SelectedItemEventArgsConverter" />
         </ResourceDictionary>
    </ContentPage.Resources>

    <ListView
        ItemsSource="{Binding Categories}"
        HasUnevenRows="True"
        IsPullToRefreshEnabled="True"
        IsRefreshing="{Binding ListRefreshing, Mode=OneWay}"
        RefreshCommand="{Binding RefreshCommand}"
        SelectionMode="Single"
        SelectedItem="{Binding SelectedCategory}">
        <ListView.Behaviors>
            <toolkit:EventToCommandBehavior
                EventName="ItemSelected"
                Command="{Binding ItemSelectedCommand}"
                EventArgsConverter="{StaticResource SelectedItemEventArgsConverter}" />
        </ListView.Behaviors>

        <!-- skipping irrelevant stuff -->

    </ListView>

</ContentPage>

然后,在您的 ViewModel 中,您可以定义ItemSelectedCommand:

public partial class CategoryViewModel : ObservableObject
{
    [RelayCommand]
    private void ItemSelected(object item)
    {
        //do something with the item, e.g. cast it to Category
    }

    // ...
}

这是首选方法。选项 1 只是另一种可能性,但是EventToCommandBehavior是更好的选择。

请注意,这是一个使用 MVVM 源生成器的示例(因为您已经在使用 MVVM 社区工具包)。完整的Command通常会这样实现:

public partial class CategoryViewModel : ObservableObject
{
    private IRelayCommand<object> _itemSelectedCommand;
    public IRelayCommand<object> ItemSelectedCommand => _itemSelectedCommand ?? (_itemSelectedCommand = new RelayCommand<object>(ItemSelected));

    private void ItemSelected(object item)
    {
        //do something with the item, e.g. cast it to Category
    }

    // ...
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

.NET MAUI绑定ListView的ItemSelected事件到ViewModel 的相关文章

随机推荐

  • 全局命名空间中的保留名称

    从我的回答中得出C 中的动态对象数组并作为后续行动在 C 标识符中使用下划线的规则是什么 显然 名字开头 后面跟着的大写字母在全局命名空间中保留 17 4 3 2 1 全局名称 lib global names 某些名称和函数签名集始终保留
  • 如何制作通用链表

    我正在尝试在中创建一个通用链表C编程语言和我 成功了 但我有一个小问题 链接列表 h struct Element void data struct Element nEl typedef struct Element Element st
  • C#7:Out 变量中的下划线 (_) 和星号 (*)

    我正在阅读 C 7 中新的输出变量功能here 我有两个问题 It says 我们也允许 丢弃 作为输出参数 形式为 让你忽略你不关心的参数 p GetCoordinates out var x out I only care about
  • 在C#中,接口可以实例化吗?

    我正在阅读中的代码here 我发现private ITreeModel model 在 TreeList cs 中 namespace Aga Controls Tree public class TreeList ListView reg
  • 通过嵌套字典键对 pandas 数据框进行分组

    我有一个 pandas 数据框 其中一列是字典类型 这是一个示例数据框 import pandas as pd df pd DataFrame a 1 2 3 b 4 5 6 version major 7 minor 1 major 8
  • 递归循环遍历 DOM 树并删除不需要的标签?

    tags array applet gt 1 script gt 1 html file get contents test html dom new DOMdocument dom gt loadHTML html xpath new D
  • 通过常见的键值对组合 JSON

    我目前正在解决一个问题 似乎无法解决这个问题 这是一些数据 以便您了解我在下面所说的内容 foo json Schedule deviceId 123 reservationId 123456 username jdoe deviceId
  • 由于写访问权限,无法使用 Android shell atrace 命令

    如果我尝试atrace工具来自adb壳里emulator atrace atrace error opening sys kernel debug tracing options overwrite No such file or dire
  • 如何创建全局辅助函数?

    我想创建一些全局辅助函数 我知道我必须将它们放在 App Code 中的 cshtml 文件中 我创建了这个文件 helper CreatePostForm string action string controller string id
  • 使用请求登录有问题的站点

    我正在尝试使用 requests 模块在 python 中创建一个脚本来登录到此site 我正在使用我的凭据 但我找不到任何方法来这样做 因为我看不到与请求一起发送所需的参数 在 Chrome 开发工具中 username SIMMTH i
  • 如何使用Maven PDF插件从Surefire Report生成PDF?

    运行 JUnit 测试后 我使用 Maven Surefire Report 插件 http maven apache org plugins maven surefire report plugin 生成 HTML 测试报告 这会产生以下
  • Kubernetes 中的 TCP 入口支持

    似乎 TCP 和 UDP 支持将在下一版本的 ingress nginx 控制器中被弃用 还有其他入口控制器支持 TCP 和 UDP 吗 或任何其他在 kubernetes 之外公开非 http 端口的解决方案 kubernetes 初学者
  • 如何在 Excel 工作表中查找精确值

    如何在 Excel 工作表中找到字符串值 我在尝试objRange Find 但这也给了我错误的地址 例如 我想要 Object paint 的地址 但它也给出 Object paint and stk 的地址 我应该如何获得精确的值 Se
  • Spring boot war文件部署在Tomcat上

    我使用 Spring Boot 1 2 4 RELEASEGS 休息服务源文件 我有 127 0 0 1 18 Jun 2015 09 59 25 0300 GET gs rest service 0 1 0 HTTP 1 1 404 10
  • Mac OS X:_tkinter.TclError:没有显示名称,也没有 $DISPLAY 环境变量

    正如我所说 我已经从 Macports 安装了 Python 3 3 现在 当我重点搜索 空闲 时 Idle Python 3 3 出现 但是 当我尝试单击它时 什么也没有发生 没有显示任何错误或任何东西 它显然不会启动 您认为可能有什么问
  • 为什么要在数据库中创建视图?

    何时以及为何有人决定需要在数据库中创建视图 为什么不直接运行一个普通的存储过程或选择呢 视图提供了多种好处 1 视图可以隐藏复杂性 如果您的查询需要连接多个表 或者具有复杂的逻辑或计算 则可以将所有逻辑编码到视图中 然后像选择表一样从视图中
  • 移动当前可执行文件c#

    我想将当前正在执行的程序集移动到 C 驱动器 当我尝试以下代码时 File Move Assembly GetEntryAssembly Location c 它给了我一个错误 mscorlib dll 中发生 System Unautho
  • Java线程安全数据库连接

    我正在编写一个 servlet 它通过访问和修改数据库中的某些表来处理每个请求 我希望与数据库的连接是线程安全的 我不想为此使用现有的库 框架 spring hibernate 等 我知道我可以通过以下方式使用 java 的 ThreadL
  • 与 multiprocessing.Pool 共享计数器

    我想用multiprocessing Value multiprocessing Lock在不同的进程之间共享一个计数器 例如 import itertools as it import multiprocessing def func x
  • .NET MAUI绑定ListView的ItemSelected事件到ViewModel

    我正在尝试将 ListView 的 ItemSelected 绑定到视图模型 但遇到了一些问题 由于我自己对其工作原理的误解 我有观点