Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>To demonstrate how to do what you want using MVVM I assume that you have a <code>ItemViewModel</code> representing the items in the list views. You need to set the <code>ListView.ItemTemplate</code> to render each item properly (or override the <code>ToString</code> method to return a string representation of the item).</p> <p>You need a <code>MainViewModel</code> with three properties:</p> <ol> <li><p><code>Items1</code> containing the list of items. In this example the list is not updated so <code>IEnumerable&lt;ItemViewModel&gt;</code> is sufficient.</p></li> <li><p><code>SelectedItem1</code> which will reference the currently selected item in the first list view (if any).</p></li> <li><p><code>Items2</code> containing the list of items you have selected so far. Because this list will be updated <code>ObservableCollection&lt;ItemViewModel&gt;</code> is used.</p></li> </ol> <p>The only interesting code in <code>MainViewModel</code> is the setter for <code>SelectedItem1</code>. This will be modified whenever the selection in the first list view changes. When this happens the selected item is added to the <code>Items2</code> collection.</p> <pre><code>public class MainViewModel { ItemViewModel selectedItem1; public MainViewModel(IEnumerable&lt;ItemViewModel&gt; items1) { Items1 = items1; Items2 = new ObservableCollection&lt;ItemViewModel&gt;(); } public IEnumerable&lt;ItemViewModel&gt; Items1 { get; private set; } public ObservableCollection&lt;ItemViewModel&gt; Items2 { get; private set; } public ItemViewModel SelectedItem1 { get { return this.selectedItem1; } set { this.selectedItem1 = value; if (this.selectedItem1 != null &amp;&amp; !Items2.Contains(this.selectedItem1)) Items2.Add(this.selectedItem1); } } } </code></pre> <p>Note that this simple view-model doesn't implement <code>INotifyPropertyChanged</code> because it is not required for this example.</p> <p>The view is bound to the view-model with XAML like this (e.g. the <code>DataContext</code> of whatever contains this XAML should be set to an instance of <code>MainViewModel</code>):</p> <pre><code>&lt;StackPanel&gt; &lt;ListView ItemsSource="{Binding Items1}" SelectedItem="{Binding SelectedItem1}"/&gt; &lt;ListView ItemsSource="{Binding Items2}"/&gt; &lt;/StackPanel&gt; </code></pre> <p>The first <code>ListView</code> is bound to <code>Items1</code> in the view-model. When the selection in the <code>ListView</code> is changed data-binding will ensure that <code>SelectedItem1</code> is set in the view-model. The code in the setter will then update the <code>Items2</code> property and because this is an <code>ObservableCollection&lt;T&gt;</code> new items added will be pushed using data-binding - in this case to the second <code>ListView</code>.</p> <p>In your case it is possible to handle a selection event in the <code>ListView</code> by binding the <code>SelectedItem</code> 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 <a href="http://msdn.microsoft.com/en-us/library/ee341381%28v=expression.40%29.aspx" rel="nofollow">Blend Behavior</a>. By writing your own <code>Behavior</code> 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.</p> <p>Note that if you want to use a <code>Behavior</code> you no longer need the Blend SDK. You can use NuGet to add a dependency to <code>Blend.Interactivity.Wpf</code> (or a similar package depending on your framework) to get the single DLL required to use Blend Behaviors.</p> <hr> <p>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 <code>SelectedItem</code> of the second <code>ListView</code> will fail because adding a new item to the second <code>ListView</code> may immediately select this item which then promptly will remove the newly added item - that is not what you want.</p> <p>Here is a <code>MouseLeftButtonUpBehavior</code> that without code-behind will allow you to execute a command when the left mouse button is released on a control:</p> <pre><code>class MouseLeftButtonUpBehavior : Behavior&lt;Control&gt; { 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); } } </code></pre> <p>The XAML has to be modified to this (you can use NuGet to add a reference to the <code>Blend.Interactivity.Wpf</code> package to be able to add interactions to controls):</p> <pre><code>&lt;StackPanel&gt; &lt;ListView ItemsSource="{Binding Items1}" SelectedItem="{Binding SelectedItem1}"/&gt; &lt;ListView ItemsSource="{Binding Items2}" SelectedItem="{Binding SelectedItem2}"&gt; &lt;i:Interaction.Behaviors&gt; &lt;local:MouseLeftButtonUpBehavior Command="{Binding DeselectCommand}"/&gt; &lt;/i:Interaction.Behaviors&gt; &lt;/ListView&gt; &lt;/StackPanel&gt; </code></pre> <p>Two new properties is required in the view-model:</p> <pre><code>public ItemViewModel SelectedItem2 { get; set; } public ICommand DeselectCommand { get; private set; } </code></pre> <p>The <code>SelectedItem2</code> is used to track which item is selected in the second list view. The <code>DeselectCommand</code> 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 <code>DelegateCommand</code>. This class is not part of WPF but if you google it you can easily find a suitable implementation. A <code>DelegateCommand</code> is simply a way to create a WPF <code>ICommand</code> that executes a delegate of your choice.</p> <p>In the constructor of <code>MainViewModel</code>:</p> <pre><code>DeselectCommand = new DelegateCommand(_ =&gt; Deselect()); </code></pre> <p>And then you need to implement <code>Deselect</code> in <code>MainViewModel</code>:</p> <pre><code>void Deselect() { if (SelectedItem2 != null) Items2.Remove(SelectedItem2); } </code></pre> <p>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 <code>Deselect</code> on the view-model).</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload