问题描述:

I'm using MVVM. I have two listview. The first listview is fine, i'm able to populate it, it's an IEnuerable. What I want to achieve is when the items (row) in the first listview is clicked, I would like to add the selected to the second listview. Can anyone advise on this.

网友答案:

To demonstrate how to do what you want using MVVM I assume that you have a ItemViewModel representing the items in the list views. You need to set the ListView.ItemTemplate to render each item properly (or override the ToString method to return a string representation of the item).

You need a MainViewModel with three properties:

  1. Items1 containing the list of items. In this example the list is not updated so IEnumerable<ItemViewModel> is sufficient.

  2. SelectedItem1 which will reference the currently selected item in the first list view (if any).

  3. Items2 containing the list of items you have selected so far. Because this list will be updated ObservableCollection<ItemViewModel> is used.

The only interesting code in MainViewModel is the setter for SelectedItem1. This will be modified whenever the selection in the first list view changes. When this happens the selected item is added to the Items2 collection.

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);
    }
  }

}

Note that this simple view-model doesn't implement INotifyPropertyChanged because it is not required for this example.

The view is bound to the view-model with XAML like this (e.g. the DataContext of whatever contains this XAML should be set to an instance of MainViewModel):

<StackPanel>
  <ListView ItemsSource="{Binding Items1}" SelectedItem="{Binding SelectedItem1}"/>
  <ListView ItemsSource="{Binding Items2}"/>
</StackPanel>

The first ListView is bound to Items1 in the view-model. When the selection in the ListView is changed data-binding will ensure that SelectedItem1 is set in the view-model. The code in the setter will then update the Items2 property and because this is an ObservableCollection<T> new items added will be pushed using data-binding - in this case to the second ListView.

In your case it is possible to handle a selection event in the ListView by binding the SelectedItem property. However, sometimes it is impossible to use data-binding to "handle events". A solution may be to add an event handler in the code-behind of the view but that will often lead to unwanted dependencies between your view-model and the view. Instead you can use a Blend Behavior. By writing your own Behavior class you can handle the event and convert into something that you can data-bind to thereby breaking the unwanted dependency. However, to solve your particular problem it is not required.

Note that if you want to use a Behavior you no longer need the Blend SDK. You can use NuGet to add a dependency to Blend.Interactivity.Wpf (or a similar package depending on your framework) to get the single DLL required to use Blend Behaviors.


So to expand on how to deselect items from the second list when they are clicked you need to use a behavior. Trying to use the same trick as above where an action is performed in the setter of a property bound to SelectedItem of the second ListView will fail because adding a new item to the second ListView may immediately select this item which then promptly will remove the newly added item - that is not what you want.

Here is a MouseLeftButtonUpBehavior that without code-behind will allow you to execute a command when the left mouse button is released on a control:

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);
  }

}

The XAML has to be modified to this (you can use NuGet to add a reference to the Blend.Interactivity.Wpf package to be able to add interactions to controls):

<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>

Two new properties is required in the view-model:

public ItemViewModel SelectedItem2 { get; set; }

public ICommand DeselectCommand { get; private set; }

The SelectedItem2 is used to track which item is selected in the second list view. The DeselectCommand is executed when a left mouse button up event is fired in the second list view. To actually do something useful you need to create a command. You can use a DelegateCommand. This class is not part of WPF but if you google it you can easily find a suitable implementation. A DelegateCommand is simply a way to create a WPF ICommand that executes a delegate of your choice.

In the constructor of MainViewModel:

DeselectCommand = new DelegateCommand(_ => Deselect());

And then you need to implement Deselect in MainViewModel:

void Deselect() {
  if (SelectedItem2 != null)
    Items2.Remove(SelectedItem2);
}

Putting all this together will remove items from the second list view when they are clicked and this without any code-behind in your view that otherwise could create unwanted dependencies from you view to your view-model (e.g. the code in the view would have to know that it should call Deselect on the view-model).

网友答案:

Add two listviews on a panel/convas/grid and add this code. Make sure you register the SelectionChanged event for the listView1 (event handler 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");
        }
    }
}
网友答案:

Use the SelectionChanged event. Then you can do something like

Listview2.Items.Add(Listview1.SelectedItem);
相关阅读:
Top