C# WPF数据网格多选

C# WPF数据网格多选,c#,wpf,mvvm,datagrid,C#,Wpf,Mvvm,Datagrid,我已经读过几篇关于这个主题的文章,但很多都是来自VS或framework的早期版本。我试图做的是从dataGrid中选择多行,并将这些行返回到绑定的可观察集合中 我曾尝试创建一个属性(类型)并将其添加到一个可观察的集合中,它可以处理单个记录,但代码从来不会处理多个记录 在VS2013中,是否有一种干净的方法可以使用MVVM模式实现这一点 如有任何想法,将不胜感激 <DataGrid x:Name="MainDataGrid" Height="390" Width="720"

我已经读过几篇关于这个主题的文章,但很多都是来自VS或framework的早期版本。我试图做的是从dataGrid中选择多行,并将这些行返回到绑定的可观察集合中

我曾尝试创建一个属性(类型)并将其添加到一个可观察的集合中,它可以处理单个记录,但代码从来不会处理多个记录

在VS2013中,是否有一种干净的方法可以使用MVVM模式实现这一点

如有任何想法,将不胜感激

<DataGrid x:Name="MainDataGrid" Height="390" Width="720" 
                  VerticalAlignment="Center" CanUserAddRows="False" CanUserDeleteRows="False" AutoGenerateColumns="False"  
                  ItemsSource="{Binding Path=DisplayInDataGrid}"
                  SelectedItem="{Binding Path=DataGridItemSelected}"
                  SelectionMode="Extended"

private ObservableCollection<ScannedItem> _dataGridItemsSelected;
    public ObservableCollection<ScannedItem> DataGridItemsSelected
    {
        get { return _dataGridItemsSelected; }
        set 
        {
            _dataGridItemsSelected = value;
            OnPropertyChanged("DataGridItemsSelected");

        }
    }


    private ScannedItem _dataGridItemSelected;
    public ScannedItem DataGridItemSelected
    {
        get { return _dataGridItemSelected;}
        set
        {
            _dataGridItemSelected = value;
            OnPropertyChanged("DataGridItemSelected");
            EnableButtons();
            LoadSelectedCollection(DataGridItemSelected); 
        }
    }

    void LoadSelectedCollection(ScannedItem si)
    {

        if (DataGridItemsSelected == null)
        {
            DataGridItemsSelected = new ObservableCollection<ScannedItem>();
        }

        DataGridItemsSelected.Add(si);

    }

创建一个命令,在
DataGrid
SelectionChanged
事件上触发,并传入
DataGrid'
s
SelectedItems

在ViewModel中,具有选定对象的列表

然后,您的
SelectionChangedCommand
执行方法将更新所选对象的集合

例如:

在我的XAML中:

<DataGrid ItemsSource="{Binding Datasets, NotifyOnTargetUpdated=True}" Name="dsDatagrid" SelectionMode="Extended" MouseDoubleClick="ViewDataset">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <cmd:EventToCommand Command="{Binding SelectionChangedCommand}" CommandParameter="{Binding ElementName=dsDatagrid, Path=SelectedItems}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</DataGrid>

在我的ViewModel中:

私有列表选择的数据集

private void SelectionChangedExecuted(object datasets)
{
    this.selectedDatasets = new List<ObservableDataset>((datasets as IList).Cast<ObservableDataset>());
}
private void SelectionChangedExecuted(对象数据集)
{
this.selectedDatasets=新列表((数据集为IList.Cast());
}

编辑:我正在使用MVVMLight。

我已经使用附加的行为模式为属性实现了双向数据绑定

下图显示了它的工作原理:

有两个数据网格绑定到同一个模型,它们共享选定的项。左侧数据网格处于活动状态,因此选定项为蓝色;右侧数据网格处于非活动状态,因此选定项为灰色

下面是如何使用它的示例代码:

main window.xaml

<Window x:Class="WpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainWindowModel/>
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <DataGrid ItemsSource="{Binding People}" local:MultiSelectorExtension.SelectedItems="{Binding SelectedPeople}" CanUserAddRows="True"/>
        <DataGrid Grid.Column="1" ItemsSource="{Binding People}" local:MultiSelectorExtension.SelectedItems="{Binding SelectedPeople}" CanUserAddRows="True"/>
        <StackPanel Grid.Row="1" Grid.ColumnSpan="2">
            <Button DockPanel.Dock="Top" Content="Select All" Command="{Binding SelectAllCommand}"/>
            <Button DockPanel.Dock="Top" Content="Unselect All" Command="{Binding UnselectAllCommand}"/>
            <Button DockPanel.Dock="Top" Content="Select Next Range" Command="{Binding SelectNextRangeCommand}"/>
        </StackPanel>
    </Grid>
</Window>

Model.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows.Input;

namespace WpfApplication
{
    abstract class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
                handler(this, e);
        }

        protected void Set<T>(ref T field, T value, string propertyName)
        {
            if (!EqualityComparer<T>.Default.Equals(field, value))
            {
                field = value;
                this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    sealed class DelegateCommand : ICommand
    {
        private readonly Action action;

        public DelegateCommand(Action action)
        {
            if (action == null)
                throw new ArgumentNullException("action");

            this.action = action;
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute()
        {
            this.action();
        }

        bool ICommand.CanExecute(object parameter)
        {
            return true;
        }

        void ICommand.Execute(object parameter)
        {
            this.Execute();
        }
    }

    class Person : ObservableObject
    {
        private string name, surname;

        public Person()
        {
        }

        public Person(string name, string surname)
        {
            this.name = name;
            this.surname = surname;
        }

        public string Name
        {
            get { return this.name; }
            set { this.Set(ref this.name, value, "Name"); }
        }

        public string Surname
        {
            get { return this.surname; }
            set { this.Set(ref this.surname, value, "Surname"); }
        }

        public override string ToString()
        {
            return this.name + ' ' + this.surname;
        }
    }

    class MainWindowModel : ObservableObject
    {
        public ObservableCollection<Person> People { get; private set; }

        public SelectedItemCollection<Person> SelectedPeople { get; private set; }

        public DelegateCommand SelectAllCommand { get; private set; }
        public DelegateCommand UnselectAllCommand { get; private set; }
        public DelegateCommand SelectNextRangeCommand { get; private set; }

        public MainWindowModel()
        {
            this.People = new ObservableCollection<Person>(Enumerable.Range(1, 1000).Select(i => new Person("Name " + i, "Surname " + i)));
            this.SelectedPeople = new SelectedItemCollection<Person>();
            for (int i = 0; i < this.People.Count; i += 2)
                this.SelectedPeople.Add(this.People[i]);

            this.SelectAllCommand = new DelegateCommand(() => this.SelectedPeople.Reset(this.People));

            this.UnselectAllCommand = new DelegateCommand(() => this.SelectedPeople.Clear());

            this.SelectNextRangeCommand = new DelegateCommand(() =>
            {
                var index = this.SelectedPeople.Count > 0 ? this.People.IndexOf(this.SelectedPeople[this.SelectedPeople.Count - 1]) + 1 : 0;

                int count = 10;

                this.SelectedPeople.Reset(Enumerable.Range(index, count).Where(i => i < this.People.Count).Select(i => this.People[i]));
            });

            this.SelectedPeople.CollectionChanged += this.OnSelectedPeopleCollectionChanged;
        }

        private void OnSelectedPeopleCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            Debug.WriteLine("Action = {0}, NewItems.Count = {1}, NewStartingIndex = {2}, OldItems.Count = {3}, OldStartingIndex = {4}, Total.Count = {5}", e.Action, e.NewItems != null ? e.NewItems.Count : 0, e.NewStartingIndex, e.OldItems != null ? e.OldItems.Count : 0, e.OldStartingIndex, this.SelectedPeople.Count);
        }
    }

    class SelectedItemCollection<T> : ObservableCollection<T>
    {
        public void Reset(IEnumerable<T> items)
        {
            int oldCount = this.Count;

            this.Items.Clear();
            foreach (var item in items)
                this.Items.Add(item);

            if (!(oldCount == 0 && this.Count == 0))
            {
                this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));

                if (this.Count != oldCount)
                    this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));

                this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
            }
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用System.Collections.Specialized;
使用系统组件模型;
使用系统诊断;
使用System.Linq;
使用System.Windows.Input;
命名空间WpfApplication
{
抽象类ObserveObject:INotifyPropertyChanged
{
公共事件属性更改事件处理程序属性更改;
PropertyChanged上受保护的虚拟无效(PropertyChangedEventArgs e)
{
var handler=this.PropertyChanged;
if(处理程序!=null)
处理者(本,e);
}
受保护的无效集(参考T字段、T值、字符串propertyName)
{
如果(!EqualityComparer.Default.Equals(字段,值))
{
字段=值;
this.OnPropertyChanged(新PropertyChangedEventArgs(propertyName));
}
}
}
密封类DelegateCommand:ICommand
{
私人只读操作;
公共委托人命令(操作)
{
if(action==null)
抛出新的异常(“操作”);
这个动作=动作;
}
公共事件事件处理程序CanExecuteChanged
{
添加{CommandManager.RequerySuggested+=value;}
删除{CommandManager.RequerySuggested-=value;}
}
public void Execute()
{
这个动作();
}
bool ICommand.CanExecute(对象参数)
{
返回true;
}
void ICommand.Execute(对象参数)
{
这是Execute();
}
}
类人:ObservieObject
{
私有字符串名称、姓氏;
公众人士()
{
}
公众人物(字符串名称、字符串姓氏)
{
this.name=名称;
this.姓氏=姓氏;
}
公共字符串名
{
获取{返回this.name;}
set{this.set(ref this.name,value,“name”);}
}
公共字符串姓氏
{
获取{返回this.name;}
set{this.set(参考this.names,值,“姓氏”);}
}
公共重写字符串ToString()
{
返回this.name+“”+this.姓氏;
}
}
类MainWindowModel:ObservableObject
{
公共可观察集合人员{get;private set;}
public SelectedItemCollection SelectedPeople{get;private set;}
public DelegateCommand SelectAllCommand{get;private set;}
public DelegateCommand UnselectAllCommand{get;private set;}
public DelegateCommand SelectNextRangeCommand{get;private set;}
公共主窗口模型()
{
this.People=newobserveCollection(Enumerable.Range(11000).Select(i=>newperson(“Name”+i,“姓氏”+i));
this.SelectedPeople=new SelectedItemCollection();
for(int i=0;ithis.SelectedPeople.Reset(this.People));
this.UnselectAllCommand=newdelegatecommand(()=>this.SelectedPeople.Clear());
this.SelectNextRangeCommand=new DelegateCommand(()=>
{
var index=this.SelectedPeople.Count>0?this.People.IndexOf(this.SelectedPeople[this.SelectedPeople.Count-1])+1:0;
整数计数=10;
this.SelectedPeople.Reset(Enumerable.Range(index,count).Where(i=>ithis.People[i]);
});
this.SelectedPeople.CollectionChanged+=this.OnSelectedPeopleCollectionChanged;
}
SelectedPeopleCollectionChanged上的私有无效(对象发送方,NotifyCollectionChangedEventArgs e)
{
Debug.WriteLine(“Action={0},NewItems.Count={1},NewStartingIndex={2},OldItems.Count={3},OldStartingIndex={4},Total.Count={
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Markup;

namespace WpfApplication
{
    static class MultiSelectorExtension
    {
        public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.RegisterAttached("SelectedItems", typeof(IList), typeof(MultiSelectorExtension), new PropertyMetadata(new PropertyChangedCallback(OnSelectedItemsChanged)));

        private static readonly DependencyProperty SelectedItemsBinderProperty = DependencyProperty.RegisterAttached("SelectedItemsBinder", typeof(SelectedItemsBinder), typeof(MultiSelectorExtension));

        [AttachedPropertyBrowsableForType(typeof(MultiSelector))]
        [DependsOn("ItemsSource")]
        public static IList GetSelectedItems(this MultiSelector multiSelector)
        {
            if (multiSelector == null)
                throw new ArgumentNullException("multiSelector");

            return (IList)multiSelector.GetValue(SelectedItemsProperty);
        }

        public static void SetSelectedItems(this MultiSelector multiSelector, IList selectedItems)
        {
            if (multiSelector == null)
                throw new ArgumentNullException("multiSelector");

            multiSelector.SetValue(SelectedItemsProperty, selectedItems);
        }

        private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var multiSelector = d as MultiSelector;

            if (multiSelector == null)
                return;

            var binder = (SelectedItemsBinder)multiSelector.GetValue(SelectedItemsBinderProperty);

            var selectedItems = e.NewValue as IList;

            if (selectedItems != null)
            {
                if (binder == null)
                    binder = new SelectedItemsBinder(multiSelector);

                binder.SelectedItems = selectedItems;
            }
            else if (binder != null)
                binder.Dispose();
        }

        private sealed class SelectedItemsBinder : IDisposable
        {
            private static readonly IList emptyList = new object[0];

            private static readonly Action<MultiSelector> multiSelectorBeginUpdateSelectedItems, multiSelectorEndUpdateSelectedItems;

            private readonly MultiSelector multiSelector;
            private IList selectedItems;
            private IResetter selectedItemsResetter;

            private bool suspendMultiSelectorUpdate, suspendSelectedItemsUpdate;

            static SelectedItemsBinder()
            {
                GetMultiSelectorBeginEndUpdateSelectedItems(out multiSelectorBeginUpdateSelectedItems, out multiSelectorEndUpdateSelectedItems);
            }

            public SelectedItemsBinder(MultiSelector multiSelector)
            {
                this.multiSelector = multiSelector;
                this.multiSelector.SelectionChanged += this.OnMultiSelectorSelectionChanged;
                this.multiSelector.Unloaded += this.OnMultiSelectorUnloaded;
                this.multiSelector.SetValue(SelectedItemsBinderProperty, this);
            }

            public IList SelectedItems
            {
                get { return this.selectedItems; }
                set
                {
                    this.SetSelectedItemsChangedHandler(false);
                    this.selectedItems = value;
                    this.selectedItemsResetter = GetResetter(this.selectedItems.GetType());
                    this.SetSelectedItemsChangedHandler(true);

                    if (this.multiSelector.IsLoaded)
                        this.OnSelectedItemsCollectionChanged(this.selectedItems, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
                    else
                    {
                        RoutedEventHandler multiSelectorLoadedHandler = null;
                        this.multiSelector.Loaded += multiSelectorLoadedHandler = new RoutedEventHandler((sender, e) =>
                        {
                            this.OnSelectedItemsCollectionChanged(this.selectedItems, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
                            this.multiSelector.Loaded -= multiSelectorLoadedHandler;
                        });
                    }
                }
            }

            private int ItemsSourceCount
            {
                get
                {
                    var collection = this.multiSelector.ItemsSource as ICollection;
                    return collection != null ? collection.Count : -1;
                }
            }

            public void Dispose()
            {
                this.multiSelector.ClearValue(SelectedItemsBinderProperty);
                this.multiSelector.Unloaded -= this.OnMultiSelectorUnloaded;
                this.multiSelector.SelectionChanged -= this.OnMultiSelectorSelectionChanged;
                this.SetSelectedItemsChangedHandler(false);
            }

            private void OnSelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                if (this.suspendMultiSelectorUpdate || e.Action == NotifyCollectionChangedAction.Move)
                    return;

                this.suspendSelectedItemsUpdate = true;

                if (this.selectedItems.Count == 0)
                    this.multiSelector.UnselectAll();
                else if (this.selectedItems.Count == this.ItemsSourceCount)
                    this.multiSelector.SelectAll();
                else if (e.Action != NotifyCollectionChangedAction.Reset && (e.NewItems == null || e.NewItems.Count <= 1) && (e.OldItems == null || e.OldItems.Count <= 1))
                    UpdateList(this.multiSelector.SelectedItems, e.NewItems ?? emptyList, e.OldItems ?? emptyList);
                else
                {
                    if (multiSelectorBeginUpdateSelectedItems != null)
                    {
                        multiSelectorBeginUpdateSelectedItems(this.multiSelector);
                        this.multiSelector.SelectedItems.Clear();
                        UpdateList(this.multiSelector.SelectedItems, this.selectedItems, emptyList);
                        multiSelectorEndUpdateSelectedItems(this.multiSelector);
                    }
                    else
                    {
                        this.multiSelector.UnselectAll();
                        UpdateList(this.multiSelector.SelectedItems, this.selectedItems, emptyList);
                    }
                }

                this.suspendSelectedItemsUpdate = false;
            }

            private void OnMultiSelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                if (this.suspendSelectedItemsUpdate)
                    return;

                this.suspendMultiSelectorUpdate = true;

                if (e.AddedItems.Count <= 1 && e.RemovedItems.Count <= 1)
                    UpdateList(this.selectedItems, e.AddedItems, e.RemovedItems);
                else
                {
                    if (this.selectedItemsResetter != null)
                        this.selectedItemsResetter.Reset(this.selectedItems, this.multiSelector.SelectedItems.Cast<object>().Where(item => item != CollectionView.NewItemPlaceholder));
                    else
                        UpdateList(this.selectedItems, e.AddedItems, e.RemovedItems);
                }

                this.suspendMultiSelectorUpdate = false;
            }

            private void OnMultiSelectorUnloaded(object sender, RoutedEventArgs e)
            {
                this.Dispose();
            }

            private void SetSelectedItemsChangedHandler(bool add)
            {
                var notifyCollectionChanged = this.selectedItems as INotifyCollectionChanged;
                if (notifyCollectionChanged != null)
                {
                    if (add)
                        notifyCollectionChanged.CollectionChanged += this.OnSelectedItemsCollectionChanged;
                    else
                        notifyCollectionChanged.CollectionChanged -= this.OnSelectedItemsCollectionChanged;
                }
            }

            private static void UpdateList(IList list, IList newItems, IList oldItems)
            {
                int addedCount = 0;

                for (int i = 0; i < oldItems.Count; ++i)
                {
                    var index = list.IndexOf(oldItems[i]);
                    if (index >= 0)
                    {
                        object newItem;
                        if (i < newItems.Count && (newItem = newItems[i]) != CollectionView.NewItemPlaceholder)
                        {
                            list[index] = newItem;
                            ++addedCount;
                        }
                        else
                            list.RemoveAt(index);
                    }
                }

                for (int i = addedCount; i < newItems.Count; ++i)
                {
                    var newItem = newItems[i];
                    if (newItem != CollectionView.NewItemPlaceholder)
                        list.Add(newItem);
                }
            }

            private static void GetMultiSelectorBeginEndUpdateSelectedItems(out Action<MultiSelector> beginUpdateSelectedItems, out Action<MultiSelector> endUpdateSelectedItems)
            {
                try
                {
                    beginUpdateSelectedItems = (Action<MultiSelector>)Delegate.CreateDelegate(typeof(Action<MultiSelector>), typeof(MultiSelector).GetMethod("BeginUpdateSelectedItems", BindingFlags.NonPublic | BindingFlags.Instance));
                    endUpdateSelectedItems = (Action<MultiSelector>)Delegate.CreateDelegate(typeof(Action<MultiSelector>), typeof(MultiSelector).GetMethod("EndUpdateSelectedItems", BindingFlags.NonPublic | BindingFlags.Instance));
                }
                catch
                {
                    beginUpdateSelectedItems = endUpdateSelectedItems = null;
                }
            }

            private static IResetter GetResetter(Type listType)
            {
                try
                {
                    MethodInfo genericReset = null, nonGenericReset = null;
                    Type genericResetItemType = null;
                    foreach (var method in listType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
                    {
                        if (method.Name != "Reset")
                            continue;

                        if (method.ReturnType != typeof(void))
                            continue;

                        var parameters = method.GetParameters();

                        if (parameters.Length != 1)
                            continue;

                        var parameterType = parameters[0].ParameterType;

                        if (parameterType.IsGenericType && parameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                        {
                            genericResetItemType = parameterType.GetGenericArguments()[0];
                            genericReset = method;
                            break;
                        }
                        else if (parameterType == typeof(IEnumerable))
                            nonGenericReset = method;
                    }

                    if (genericReset != null)
                        return (IResetter)Activator.CreateInstance(typeof(GenericResetter<,>).MakeGenericType(genericReset.DeclaringType, genericResetItemType), genericReset);
                    else if (nonGenericReset != null)
                        return (IResetter)Activator.CreateInstance(typeof(NonGenericResetter<>).MakeGenericType(nonGenericReset.DeclaringType), nonGenericReset);
                    else
                        return null;
                }
                catch
                {
                    return null;
                }
            }

            private interface IResetter
            {
                void Reset(IList list, IEnumerable items);
            }

            private sealed class NonGenericResetter<TTarget> : IResetter
            {
                private readonly Action<TTarget, IEnumerable> reset;

                public NonGenericResetter(MethodInfo method)
                {
                    this.reset = (Action<TTarget, IEnumerable>)Delegate.CreateDelegate(typeof(Action<TTarget, IEnumerable>), method);
                }

                public void Reset(IList list, IEnumerable items)
                {
                    this.reset((TTarget)list, items);
                }
            }

            private sealed class GenericResetter<TTarget, T> : IResetter
            {
                private readonly Action<TTarget, IEnumerable<T>> reset;

                public GenericResetter(MethodInfo method)
                {
                    this.reset = (Action<TTarget, IEnumerable<T>>)Delegate.CreateDelegate(typeof(Action<TTarget, IEnumerable<T>>), method);
                }

                public void Reset(IList list, IEnumerable items)
                {
                    this.reset((TTarget)list, items.Cast<T>());
                }
            }
        }
    }
}