我正在使用 MVVM。我有两个列表视图。第一个列表视图很好,我可以填充它,它是一个 IEnuerable。我想要实现的是当单击第一个列表视图中的项目(行)时,我想将所选内容添加到第二个列表视图中。任何人都可以就此提出建议。
3 回答
为了演示如何使用 MVVM 做你想做的事,我假设你有一个ItemViewModel
代表列表视图中的项目。您需要设置ListView.ItemTemplate
以正确呈现每个项目(或覆盖该ToString
方法以返回项目的字符串表示形式)。
你需要一个MainViewModel
具有三个属性的:
Items1
包含项目列表。在此示例中,列表未更新,因此IEnumerable<ItemViewModel>
已足够。SelectedItem1
这将引用第一个列表视图(如果有)中当前选定的项目。Items2
包含您迄今为止选择的项目列表。因为这个列表会被更新ObservableCollection<ItemViewModel>
使用。
中唯一有趣的代码MainViewModel
是SelectedItem1
. 每当第一个列表视图中的选择发生更改时,这将被修改。发生这种情况时,所选项目将添加到Items2
集合中。
public class MainViewModel {
ItemViewModel selectedItem1;
public MainViewModel(IEnumerable<ItemViewModel> items1) {
Items1 = items1;
Items2 = new ObservableCollection<ItemViewModel>();
}
public IEnumerable<ItemViewModel> Items1 { get; private set; }
public ObservableCollection<ItemViewModel> Items2 { get; private set; }
public ItemViewModel SelectedItem1 {
get { return this.selectedItem1; }
set {
this.selectedItem1 = value;
if (this.selectedItem1 != null && !Items2.Contains(this.selectedItem1))
Items2.Add(this.selectedItem1);
}
}
}
请注意,这个简单的视图模型没有实现INotifyPropertyChanged
,因为本示例不需要它。
视图通过这样的 XAML 绑定到视图模型(例如DataContext
,包含此 XAML 的任何内容都应设置为 的实例MainViewModel
):
<StackPanel>
<ListView ItemsSource="{Binding Items1}" SelectedItem="{Binding SelectedItem1}"/>
<ListView ItemsSource="{Binding Items2}"/>
</StackPanel>
第一个ListView
绑定Items1
在视图模型中。当ListView
更改中的选择时,数据绑定将确保SelectedItem1
在视图模型中设置。然后,setter 中的代码将更新Items2
属性,因为这是一个ObservableCollection<T>
添加的新项目,所以将使用数据绑定推送 - 在本例中为第二个ListView
。
在您的情况下,可以ListView
通过绑定SelectedItem
属性来处理选择事件。然而,有时不可能使用数据绑定来“处理事件”。一种解决方案可能是在视图的代码隐藏中添加事件处理程序,但这通常会导致视图模型和视图之间出现不必要的依赖关系。相反,您可以使用混合行为。通过编写自己的Behavior
类,您可以处理事件并将其转换为可以数据绑定的东西,从而打破不需要的依赖关系。但是,要解决您的特定问题,它不是必需的。
请注意,如果您想使用 aBehavior
您不再需要 Blend SDK。您可以使用 NuGet 向Blend.Interactivity.Wpf
(或取决于您的框架的类似包)添加依赖项,以获取使用混合行为所需的单个 DLL。
因此,要扩展如何在单击第二个列表时从第二个列表中取消选择项目,您需要使用一种行为。尝试使用与上述相同的技巧,其中在第二个绑定到的属性的设置器中执行操作SelectedItem
将ListView
失败,因为向第二个添加新项目ListView
可能会立即选择该项目,然后会立即删除新添加的项目 - 那不是你想要的。
这是一个MouseLeftButtonUpBehavior
没有代码隐藏的方法,允许您在控件上释放鼠标左键时执行命令:
class MouseLeftButtonUpBehavior : Behavior<Control> {
public static readonly DependencyProperty CommandProperty
= DependencyProperty.Register(
"Command",
typeof(ICommand),
typeof(MouseLeftButtonUpBehavior)
);
public ICommand Command
{
get { return (ICommand) GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
protected override void OnAttached() {
AssociatedObject.MouseLeftButtonUp += OnMouseLeftButtonUp;
}
protected override void OnDetaching() {
AssociatedObject.MouseLeftButtonUp -= OnMouseLeftButtonUp;
}
void OnMouseLeftButtonUp(Object sender, MouseButtonEventArgs mouseButtonEventArgs) {
if (Command != null)
Command.Execute(mouseButtonEventArgs);
}
}
XAML 必须修改为此(您可以使用 NuGet 添加对Blend.Interactivity.Wpf
包的引用,以便能够向控件添加交互):
<StackPanel>
<ListView ItemsSource="{Binding Items1}" SelectedItem="{Binding SelectedItem1}"/>
<ListView ItemsSource="{Binding Items2}" SelectedItem="{Binding SelectedItem2}">
<i:Interaction.Behaviors>
<local:MouseLeftButtonUpBehavior Command="{Binding DeselectCommand}"/>
</i:Interaction.Behaviors>
</ListView>
</StackPanel>
视图模型中需要两个新属性:
public ItemViewModel SelectedItem2 { get; set; }
public ICommand DeselectCommand { get; private set; }
SelectedItem2
用于跟踪在第二个列表视图中选择了哪个项目。在第二DeselectCommand
个列表视图中触发鼠标左键向上事件时执行。要真正做一些有用的事情,您需要创建一个命令。您可以使用DelegateCommand
. 这个类不是 WPF 的一部分,但如果你用谷歌搜索它,你可以很容易地找到一个合适的实现。ADelegateCommand
只是一种创建ICommand
执行您选择的委托的 WPF 的方法。
在的构造函数中MainViewModel
:
DeselectCommand = new DelegateCommand(_ => Deselect());
然后你需要Deselect
实现MainViewModel
:
void Deselect() {
if (SelectedItem2 != null)
Items2.Remove(SelectedItem2);
}
将所有这些放在一起将在单击第二个列表视图时从第二个列表视图中删除项目,并且在您的视图中没有任何代码隐藏,否则可能会从您的视图创建不需要的依赖项到您的视图模型(例如,视图中的代码必须知道它应该调用Deselect
视图模型)。
在面板/画布/网格上添加两个列表视图并添加此代码。确保为 listView1 注册了 SelectionChanged 事件(事件处理程序 listView1_SelectionChanged)。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void listView1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
listView2.Items.Add((listView1.SelectedItem));
}
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
listView1.Items.Add("test 1");
listView1.Items.Add("test 2");
}
}
}
使用 SelectionChanged 事件。然后你可以做类似的事情
Listview2.Items.Add(Listview1.SelectedItem);