.NET MAUI中的MVVM:从CollectionView绑定SelectedItemCommand和SelectedItemParameter

3

我正在使用SQLite和CommunityToolkit.Mvvm.ComponentModel技术。

我有一个包含朋友信息的数据表,可以将其绑定到CollectionView上。我遵循https://www.youtube.com/watch?v=8_cqUvriwM8的教程,但尝试使用MVVM方法。

我可以通过SelectionChanged事件使其正常工作,但无法通过SelectionChangedCommand访问列表中的Friend项目。

以下是相关的XAML代码:

    <CollectionView Grid.Row="2"
                    x:Name="FriendsList"
                    SelectionMode="Single"
                    SelectionChangedCommand="{Binding SelectionChangedCommand}" 
                    SelectionChangedCommandParameter="{Binding .}"
                    SelectionChanged="OnSelectionChanged" >

以下是相关代码部分(我只是为了测试而使用xaml的代码):
    public MainPage()
    {
        InitializeComponent();

    this.BindingContext = this;  //cool for binding the xaml to the code behind.
  }
...


//This works fine (so why do I bother with mvvm?)
  public void OnSelectionChanged(Object sender, SelectionChangedEventArgs e)
  {
    Console.WriteLine("Selection changed click");
    Friend f = e.CurrentSelection[0] as Friend;
    Console.WriteLine(f.LName);
  }

//Can't get this to work, though it will register the click
  public ICommand SelectionChangedCommand => new Command(SelectionChangedControl);
  public void SelectionChangedControl()
  {
    Console.WriteLine("selection made");

  }

我的想法是,如果我可以这样做来获取Friend项目,因为CommandParameter是提供对象的,按照我理解的方式?

  public ICommand SelectionChangedCommand => new Command<Friend>(SelectionChangedControl);
  public void SelectionChangedControl(Friend f)
  {
    Console.WriteLine("selection made");
  }

但是这个命令现在甚至都没有触发。显然我错得离谱。

请问你有什么想法?(哦,顺便说一句,我已经尝试注释掉其中一个,以防万一。)

顺便说一下,是否有一份参考资料(不是微软文档),可以用初学者的术语解释这些东西?是否有一个关于.NET Maui的API参考?

编辑:来自https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/collectionview/selection的文档:

单选 当SelectionMode属性设置为Single时,CollectionView中只能选择一个项目。当选定一个项目时,SelectedItem属性将被设置为所选项目的值。 当此属性更改时,SelectionChangedCommand将被执行(使用SelectionChangedCommandParameter的值传递给ICommand),并且SelectionChanged事件将被触发。

我如何获取SelectionChangedCommandParameter的值,即行对象,即我的Friend对象?

编辑2:我想我需要获取CurrentSelection[0]的值,但我不知道该怎么做。

我了解到我可以像这样做(从文档中):

SelectionChangedCommand="{Binding SelectionChangedCommand}"
SelectionChangedCommandParameter="Hello G"

并且

public ICommand SelectionChangedCommand => new Command<string>( (String s) =>
{
  Console.WriteLine($"selection made {s}");
});

命令被捕获并显示,因此我的想法是不使用 {Binding .} 是我不想要的,但我应该绑定什么?

SelectionChangedCommandParameter ={Binding ???}

感谢,G。

我不确定在这种情况下SelectionChangedCommandParameter是否真的有用。你应该能够将SelectedItems绑定到VM属性,并在SelectionChangedCommand中使用该属性。 - Jason
我知道我不应该问,但什么是VM属性,如何绑定它? :) - gfmoore
"VM" 是 MVVM 的 ViewModel。您可以使用已经在使用的绑定语法 - {Binding SomePropertyName} 来绑定属性。 - Jason
好的,但就像OnSelectionChanged(Object sender, SelectionChangedEventArgs e)一样,我想要整个对象,也就是Friend。所以在OnSelectionChanged中,EventArgs e可以转换为Friend。我怎么才能得到行项目本身呢?抱歉,我真的很困扰。我怀疑我错过了MVVM的一个重要点 :叹气 - gfmoore
4个回答

6

多选项注意事项

我曾经尝试将多个选择绑定到视图模型中,但在不链接代码后台的情况下遇到了问题。尽管这篇文章是唯一相关的搜索结果并且帮助了我很多,但它缺少了关于多选项的一些重要信息。

View.xaml

<CollectionView ItemsSource="{Binding DataItems}"
                SelectedItems="{Binding SelectedData}"
                SelectionMode="Multiple"
                SelectionChangedCommand="{Binding SelectionChangedCommand}">
 ....

关于视图模型,有几点需要提及。我正在使用CommunityToolkit.Mvvm,因此[ObservableProperty]注释会在适当的驼峰式中为您创建属性,而[RelayCommand]用于OnMethodName将丢弃“On”并只使用MethodNameCommand。

ViewModel.cs

[ObservableProperty]
ObservableCollection<CustomDataItem> dataItems;

[ObservableProperty]
ObservableCollection<object> selectedData;

[RelayCommand]
void OnSelectionChanged()
{
  foreach(var o in SelectedData)
  {
      if(o is CustomDataItem i)
        ...
  }
}

重点是SelectedItems必须是一个List<object>,不能是<CustomDataItem>。我花了几个小时搜索和尝试不同的方法,最终放弃并在代码后台中链接事件处理程序。但是,直到我将它们更改为对象列表,我才能像这里描述的那样预选项。因此,该列表将以两种方式填充,您只需要将其转换为您正在使用的数据类型即可。

无论如何,对于一些人可能很明显,但对于像我这样假设SelectedItems与所选项(SelectedItem)相同但以列表形式存在的人可能会有所帮助。


在多选的情况下,SelectedItems不会触发。使用普通命令而不是继电器命令。 - Suchith
非常感谢您的分享。在这里使用object而不是CustomDataItem确实有很大的区别! - Sam

5

当你在CollectionView.SelectionChangedCommandParameter中使用.时,它指向其父视图的BindingContext
例如,如果你的CollectionViewContentPage中,.指向ContentPageBindingContext

如果您想要FriendsList每个项目的引用,其中一个解决方案是使用SelectedItem
尝试像这样的代码:

<CollectionView 
    Grid.Row="2"
    x:Name="FriendsList"
    SelectionMode="Single"
    SelectionChangedCommand="{Binding SelectionChangedCommand}" 
    SelectionChangedCommandParameter="{Binding Path=SelectedItem, Source={x:Reference FriendsList}}">

或者
<CollectionView 
    Grid.Row="2"
    SelectionMode="Single"
    SelectionChangedCommand="{Binding SelectionChangedCommand}" 
    SelectionChangedCommandParameter="{Binding Path=SelectedItem, Source={RelativeSource Self}}">

参考:
绑定到自身 (Source={RelativeSource Self}}):
https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/data-binding/relative-bindings#bind-to-self


这其实是正确的做法,将SelectedProperty添加到您的VM只会创建不必要的混乱。 - Kalixt
只是一条注释。视图模型中的命令参数需要是一个对象,否则将无法正常工作。 - undefined

4

首先,绑定SelectedItem

SelectedItem="{Binding SelectedFriend}"

在您的虚拟机中为该绑定项创建一个属性。
public Friend SelectedFriend { get; set; }

然后在您的命令中,您可以使用该属性。

public void SelectionChangedControl()
{
  Console.WriteLine(SelectedFriend.Name);
}

0

@Jason 我笑得很厉害,我刚刚弄明白了然后就来发布帖子,看到了你的答案。非常感谢你的帮助。

记录一下,我在这篇文章中找到了解决方法 https://www.mfractor.com/blogs/news/migrating-listview-to-collectionview-in-xamarin-forms-interactivity

最终我发现我需要像你指出的那样使用 SelectedItem。我认为这是因为在 SelectionChanged 单击事件中不需要(或者是隐含的)。

无论如何,在我的 XAML 中

    <CollectionView Grid.Row="2"
                    x:Name="FriendsList"
                    SelectionMode="Single"
                    SelectedItem="{Binding SelectedItem}"
                    SelectionChangedCommand="{Binding SelectionChangedCommand}"
                    SelectionChangedCommandParameter="{Binding .}" >

在我的代码中

  public Friend SelectedItem { get; set; }

  //respond to item select
  public ICommand SelectionChangedCommand => new Command<Object>((Object e) =>
  {
    Console.WriteLine($"selection made {SelectedItem.FName}");
  });

你的代码当然更简单。

你指出SelectionChangedCommandParameter="{Binding .}"(可能)不是必需的,那它的目的是什么?

对象e在我的代码中被返回,它是什么?我假设它与SelectionChangedCommandParameter有关?

在我的立即窗口中,我得到:

e
{Census.MainPage}
    base: {Microsoft.Maui.Controls.ContentPage}
    AddFriendCommand: {Microsoft.Maui.Controls.Command}
    SelectedItem: {Census.Classes.Friend}
    SelectionChangedCommand: {Microsoft.Maui.Controls.Command<object>}

能否从XAML跟踪到代码呢?例如,当我试图弄清楚事情的时候,我希望在XAML中捕获项目单击事件并查看它正在做什么(特别是因为有时它没有触发我的代码中的断点)。

这只是一些闲问题,不需要回答,除非有人愿意。

再次感谢@Jason,你是个明星! :)


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