WPF M-V-VM: Get selected items from a ListCollectionView?

后端 未结 9 1213
暖寄归人
暖寄归人 2020-12-09 17:33

I\'ve got a WPF app using the Model-View-ViewModel pattern.
In my ViewModel I\'ve got a ListCollectionView to keep a list of items.
This ListCollectionView is bound

相关标签:
9条回答
  • 2020-12-09 17:57

    David Rogers' solution is great and is detailed at the below related question:

    Sync SelectedItems in a muliselect listbox with a collection in ViewModel

    0 讨论(0)
  • 2020-12-09 18:01

    Look at this blogpost by Josh Smith The Initially Selected Item when Binding to a Grouped ICollectionView

    0 讨论(0)
  • 2020-12-09 18:03

    Here is another variant of the View-Model-ViewModel Pattern where the ViewModel has access to the view through an IView interface.

    I encountered quite a lot scenarios where you can't use WPF binding and then you need a way in code to synchronize the state between the View and the ViewModel.

    How this can be done is shown here:

    WPF Application Framework (WAF)

    0 讨论(0)
  • 2020-12-09 18:04

    PRISM MVVM Reference Implementation has a behaviour called SynchronizeSelectedItems, used in Prism4\MVVM RI\MVVM.Client\Views\MultipleSelectionView.xaml, which synchronizes checked items with the ViewModel property named Selections:

            <ListBox Grid.Column="0" Grid.Row="1" IsTabStop="False" SelectionMode="Multiple"
                     ItemsSource="{Binding Question.Range}" Margin="5">
    
                <ListBox.ItemContainerStyle>
                    <!-- Custom style to show the multi-selection list box as a collection of check boxes -->
                    <Style TargetType="ListBoxItem">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="ListBoxItem">
                                    <Grid Background="Transparent">
                                        <CheckBox IsChecked="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" 
                                                  IsHitTestVisible="False" IsTabStop="True"
                                                  AutomationProperties.AutomationId="CheckBoxAutomationId">
                                            <ContentPresenter/>
                                        </CheckBox>
                                    </Grid>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </ListBox.ItemContainerStyle>
                <i:Interaction.Behaviors>
                    <!-- Custom behavior that synchronizes the selected items with the view models collection -->
                    <Behaviors:SynchronizeSelectedItems Selections="{Binding Selections}"/>
                </i:Interaction.Behaviors>
            </ListBox>
    

    Go to http://compositewpf.codeplex.com/ and grab it all or use this:

    //===================================================================================
    // Microsoft patterns & practices
    // Composite Application Guidance for Windows Presentation Foundation and Silverlight
    //===================================================================================
    // Copyright (c) Microsoft Corporation.  All rights reserved.
    // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
    // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
    // LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    // FITNESS FOR A PARTICULAR PURPOSE.
    //===================================================================================
    // The example companies, organizations, products, domain names,
    // e-mail addresses, logos, people, places, and events depicted
    // herein are fictitious.  No association with any real company,
    // organization, product, domain name, email address, logo, person,
    // places, or events is intended or should be inferred.
    //===================================================================================
    using System;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Diagnostics.CodeAnalysis;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Interactivity;
    
    namespace MVVM.Client.Infrastructure.Behaviors
    {
        /// <summary>
        /// Custom behavior that synchronizes the list in <see cref="ListBox.SelectedItems"/> with a collection.
        /// </summary>
        /// <remarks>
        /// This behavior uses a weak event handler to listen for changes on the synchronized collection.
        /// </remarks>
        public class SynchronizeSelectedItems : Behavior<ListBox>
        {
            public static readonly DependencyProperty SelectionsProperty =
                DependencyProperty.Register(
                    "Selections",
                    typeof(IList),
                    typeof(SynchronizeSelectedItems),
                    new PropertyMetadata(null, OnSelectionsPropertyChanged));
    
            private bool updating;
            private WeakEventHandler<SynchronizeSelectedItems, object, NotifyCollectionChangedEventArgs> currentWeakHandler;
    
            [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
                Justification = "Dependency property")]
            public IList Selections
            {
                get { return (IList)this.GetValue(SelectionsProperty); }
                set { this.SetValue(SelectionsProperty, value); }
            }
    
            protected override void OnAttached()
            {
                base.OnAttached();
    
                this.AssociatedObject.SelectionChanged += this.OnSelectedItemsChanged;
                this.UpdateSelectedItems();
            }
    
            protected override void OnDetaching()
            {
                this.AssociatedObject.SelectionChanged += this.OnSelectedItemsChanged;
    
                base.OnDetaching();
            }
    
            private static void OnSelectionsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var behavior = d as SynchronizeSelectedItems;
    
                if (behavior != null)
                {
                    if (behavior.currentWeakHandler != null)
                    {
                        behavior.currentWeakHandler.Detach();
                        behavior.currentWeakHandler = null;
                    }
    
                    if (e.NewValue != null)
                    {
                        var notifyCollectionChanged = e.NewValue as INotifyCollectionChanged;
                        if (notifyCollectionChanged != null)
                        {
                            behavior.currentWeakHandler =
                                new WeakEventHandler<SynchronizeSelectedItems, object, NotifyCollectionChangedEventArgs>(
                                    behavior,
                                    (instance, sender, args) => instance.OnSelectionsCollectionChanged(sender, args),
                                    (listener) => notifyCollectionChanged.CollectionChanged -= listener.OnEvent);
                            notifyCollectionChanged.CollectionChanged += behavior.currentWeakHandler.OnEvent;
                        }
    
                        behavior.UpdateSelectedItems();
                    }
                }
            }
    
            private void OnSelectedItemsChanged(object sender, SelectionChangedEventArgs e)
            {
                this.UpdateSelections(e);
            }
    
            private void UpdateSelections(SelectionChangedEventArgs e)
            {
                this.ExecuteIfNotUpdating(
                    () =>
                    {
                        if (this.Selections != null)
                        {
                            foreach (var item in e.AddedItems)
                            {
                                this.Selections.Add(item);
                            }
    
                            foreach (var item in e.RemovedItems)
                            {
                                this.Selections.Remove(item);
                            }
                        }
                    });
            }
    
            private void OnSelectionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                this.UpdateSelectedItems();
            }
    
            private void UpdateSelectedItems()
            {
                this.ExecuteIfNotUpdating(
                    () =>
                    {
                        if (this.AssociatedObject != null)
                        {
                            this.AssociatedObject.SelectedItems.Clear();
                            foreach (var item in this.Selections ?? new object[0])
                            {
                                this.AssociatedObject.SelectedItems.Add(item);
                            }
                        }
                    });
            }
    
            private void ExecuteIfNotUpdating(Action execute)
            {
                if (!this.updating)
                {
                    try
                    {
                        this.updating = true;
                        execute();
                    }
                    finally
                    {
                        this.updating = false;
                    }
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-09 18:04

    Drew Marsh's answer is fine if you have a small list, if you have a large list the performance hit for finding all your selected items could be nasty! My favorite solution is to create an attached property on your ListBox that then binds to an ObservableCollection which contains your selected items. Then with your attached property you subscribe to the items SelectionChanged event to add/remove items from your collection.

    0 讨论(0)
  • 2020-12-09 18:10

    You need to create a ViewModel that has the concept of IsSelected on it and is bound to the IsSelected property of the actual ListBoxItem that represents it in the View using the standard WPF bindings architecture.

    Then in your code, which knows about your ViewModel, but not the fact that it's represented by any specific View, can just use that property to find out which items from the Model are actually selected irrespective of the designers choice for how its represented in the View.

    0 讨论(0)
提交回复
热议问题