Я делаю простой ListView с WinUI и Microsoft.Toolkit.Mvvm на основе TemplateStudio для WinUI.
Я пытаюсь получить простой ListView, который содержит названия фруктов, второй ListView, чтобы получить SelectedItems из первого, и, наконец, TextBox с количеством выбранных элементов.
MainPage.xaml
<Page
x:Class = "ListViewWinUISample.Views.MainPage"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i = "using:Microsoft.Xaml.Interactivity"
xmlns:core = "using:Microsoft.Xaml.Interactions.Core"
mc:Ignorable = "d">
<Grid x:Name = "ContentArea" Margin = "20">
<Grid.RowDefinitions>
<RowDefinition Height = "25" />
<RowDefinition Height = "150" />
<RowDefinition Height = "25" />
<RowDefinition Height = "150" />
<RowDefinition Height = "30" />
</Grid.RowDefinitions>
<TextBlock Grid.Row = "0" Text = "Fruits list :" Style = "{StaticResource PageTitleStyle}" />
<ListView Grid.Row = "1" x:Name = "LvwFruits" ItemsSource = "{x:Bind ViewModel.ListFruits}" BorderBrush = "#212121" SelectionMode = "Multiple">
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName = "SelectionChanged">
<core:InvokeCommandAction Command = "{x:Bind ViewModel.GetSelectedFruits}" CommandParameter = "{Binding SelectedItems, ElementName=LvwFruits}"/>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
</ListView>
<TextBlock Grid.Row = "2" Text = "Selected fruits :" Style = "{StaticResource PageTitleStyle}" />
<ListView Grid.Row = "3" x:Name = "LvwSelectedFruits" ItemsSource = "{x:Bind ViewModel.ListSelectedFruits}" BorderBrush = "#212121"/>
<StackPanel Grid.Row = "4" Orientation = "Horizontal">
<TextBlock Text = "Count selected fruits : " VerticalAlignment = "Center"/>
<TextBox Text = "{Binding SelectedItems.Count, ElementName=LvwFruits}" VerticalAlignment = "Center"/>
</StackPanel>
</Grid>
</Page>
MainViewModel.cs
using System.Collections;
using System.Collections.ObjectModel;
using System.Diagnostics;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace ListViewWinUISample.ViewModels;
public class MainViewModel : ObservableRecipient
{
public List<string> ListFruits;
public List<string> ListSelectedFruits;
public RelayCommand<IList> GetSelectedFruits { get; set; }
public MainViewModel()
{
GetSelectedFruits = new RelayCommand<IList>(GetSelectedFruitsMethod);
var fruits = new List<string>
{
"Apple",
"Banana",
"Strawberry"
};
ListFruits = fruits;
}
private void GetSelectedFruitsMethod(IList selectedFruits)
{
Debug.WriteLine("Command successfully executed");
var listSelectedFruits = new List<string>();
foreach (var item in selectedFruits)
{
listSelectedFruits.Add(item.ToString());
Debug.WriteLine("selected fruit : " + item.ToString());
}
}
}
GetSelectedFruitsMethod успешно запущен, но параметр IList selectedFruits в GetSelectedFruitsMethod всегда null, и я не могу понять, как получить параметр SelectedItems из представления, даже если я передал его как CommandParameter в XAML.
Спасибо и привет.





Вы можете создать собственный элемент управления следующим образом:
ListViewEx.cs
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Collections;
using System.Collections.Generic;
namespace ListViews;
public class ListViewEx : ListView
{
public ListViewEx() : base()
{
this.SelectionChanged += ListViewEx_SelectionChanged;
}
public new IList SelectedItems
{
get => (IList)GetValue(SelectedItemsProperty);
set => SetValue(SelectedItemsProperty, value);
}
public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register(
nameof(SelectedItems),
typeof(IList),
typeof(ListViewEx),
new PropertyMetadata(new List<object>()));
private void ListViewEx_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (object item in e.RemovedItems)
{
SelectedItems.Remove(item);
}
foreach (object item in e.AddedItems)
{
SelectedItems.Add(item);
}
}
}
И используйте это так:
MainPage.xaml
<Grid RowDefinitions = "Auto,*,*">
<StackPanel
Grid.Row = "0"
Orientation = "Horizontal">
<NumberBox
x:Name = "LoadingItemsCount"
Value = "1000" />
<Button
Command = "{x:Bind ViewModel.LoadItemsCommand}"
CommandParameter = "{x:Bind LoadingItemsCount.Value, Mode=OneWay}" />
</StackPanel>
<local:ListViewEx
Grid.Row = "1"
ItemsSource = "{x:Bind ViewModel.Items, Mode=OneWay}"
SelectedItems = "{x:Bind ViewModel.SelectedItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectionMode = "Multiple">
<local:ListViewEx.Header>
<TextBlock>
<Run Text = "Items: " />
<Run Text = "{x:Bind ViewModel.Items.Count, Mode=OneWay}" />
</TextBlock>
</local:ListViewEx.Header>
</local:ListViewEx>
<ListView
Grid.Row = "2"
ItemsSource = "{x:Bind ViewModel.SelectedItems, Mode=OneWay}">
<ListView.Header>
<TextBlock>
<Run Text = "Selected items: " />
<Run Text = "{x:Bind ViewModel.SelectedItems.Count, Mode=OneWay}" />
</TextBlock>
</ListView.Header>
</ListView>
</Grid>
MainPage.xaml.cs
[ObservableObject]
public partial class MainPageViewModel
{
[ObservableProperty]
private ObservableCollection<string> items = new();
[ObservableProperty]
private ObservableCollection<string> selectedItems = new();
public MainPageViewModel()
{
var ilist = items as IList;
}
[RelayCommand]
private void LoadItems(double itemsCount)
{
for (int i = 0; i < itemsCount; i++)
{
Items.Add(i.ToString());
}
}
}
Наконец-то я сам получил ответ с полным способом MVVM.
Чтобы получить SelectedItems из первого ListView, нужно было просто установить CommandParameter = "{x:Bind LvwFruits.SelectedItems}", а не {Binding SelectedItems, ElementName=LvwFruits}, и заменить IList на IList<object>.
MainPage.xaml
<Page
x:Class = "ListViewWinUISample.Views.MainPage"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i = "using:Microsoft.Xaml.Interactivity"
xmlns:core = "using:Microsoft.Xaml.Interactions.Core"
mc:Ignorable = "d">
<Grid x:Name = "ContentArea" Margin = "20">
<Grid.RowDefinitions>
<RowDefinition Height = "25" />
<RowDefinition Height = "150" />
<RowDefinition Height = "25" />
<RowDefinition Height = "150" />
<RowDefinition Height = "30" />
</Grid.RowDefinitions>
<TextBlock Grid.Row = "0" Text = "Fruits list :" Style = "{StaticResource PageTitleStyle}" />
<ListView Grid.Row = "1" x:Name = "LvwFruits" ItemsSource = "{x:Bind ViewModel.ListFruits}" BorderBrush = "#212121" SelectionMode = "Multiple" BorderThickness = "1">
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName = "SelectionChanged">
<core:InvokeCommandAction Command = "{x:Bind ViewModel.GetSelectedFruits, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" CommandParameter = "{x:Bind LvwFruits.SelectedItems}"/>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
</ListView>
<TextBlock Grid.Row = "2" Text = "Selected fruits :" Style = "{StaticResource PageTitleStyle}" />
<ListView Grid.Row = "3" x:Name = "LvwSelectedFruits" ItemsSource = "{x:Bind ViewModel.ListSelectedFruits, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" CanDragItems = "True" CanReorderItems = "True" AllowDrop = "True" SelectionMode = "Single" BorderBrush = "#212121" BorderThickness = "1"/>
<StackPanel Grid.Row = "4" Orientation = "Horizontal">
<TextBlock Text = "Count selected fruits : " VerticalAlignment = "Center"/>
<TextBox Text = "{x:Bind ViewModel.CountSelectedCommands, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Margin = "5,0,0,0" Width = "10"/>
</StackPanel>
</Grid>
</Page>
MainViewModel.cs
using System.Collections;
using System.Collections.ObjectModel;
using System.Diagnostics;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace ListViewWinUISample.ViewModels;
public class MainViewModel : ObservableRecipient
{
public List<string> ListFruits;
private ObservableCollection<string> listSelectedFruits;
public ObservableCollection<string> ListSelectedFruits
{
get => listSelectedFruits;
set => SetProperty(ref listSelectedFruits, value);
}
private int countSelectedCommands;
public int CountSelectedCommands
{
get => countSelectedCommands;
set => SetProperty(ref countSelectedCommands, value);
}
public RelayCommand<IList<object>> GetSelectedFruits
{
get; set;
}
public MainViewModel()
{
GetSelectedFruits = new RelayCommand<IList<object>>(GetSelectedFruitsMethod);
var fruits = new List<string>
{
"Apple",
"Banana",
"Strawberry"
};
ListFruits = fruits;
}
private void GetSelectedFruitsMethod(IList<object> selectedFruits)
{
if (selectedFruits != null)
{
var selected = new ObservableCollection<string>();
foreach (var item in selectedFruits)
{
selected.Add(item.ToString());
Debug.WriteLine("Selected fruit : " + item.ToString());
}
ListSelectedFruits = selected;
CountSelectedCommands = selected.Count;
}
else
{
Debug.WriteLine(@"/!\ NO SELECTED FRUITS /!\");
}
}
}
Спасибо Андрей за ответ. Я смог добиться этого с помощью Prism и WPF в полном объеме MVVM, а теперь с WinUI3 и Microsoft-toolkit-mvvm я не могу этого сделать, параметр не отправляется из View в ViewModel, но команда выполняется.