it-swarm.com.ru

Выберите несколько элементов из DataGrid в проекте MVFM WPF

Как я могу выбрать несколько элементов из DataGrid в проекте MVFM WPF?

55
MegaMind

Вы можете просто добавить пользовательское свойство зависимости , чтобы сделать это:

public class CustomDataGrid : DataGrid
{
    public CustomDataGrid ()
    {
        this.SelectionChanged += CustomDataGrid_SelectionChanged;
    }

    void CustomDataGrid_SelectionChanged (object sender, SelectionChangedEventArgs e)
    {
        this.SelectedItemsList = this.SelectedItems;
    }
    #region SelectedItemsList

    public IList SelectedItemsList
    {
        get { return (IList)GetValue (SelectedItemsListProperty); }
        set { SetValue (SelectedItemsListProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemsListProperty =
            DependencyProperty.Register ("SelectedItemsList", typeof (IList), typeof (CustomDataGrid), new PropertyMetadata (null));

    #endregion
}

Теперь вы можете использовать эту dataGrid в XAML:

<Window x:Class="DataGridTesting.MainWindow"
    xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
    xmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity"
    xmlns:local="clr-namespace:DataGridTesting.CustomDatagrid"
    Title="MainWindow"
    Height="350"
    Width="525">
  <DockPanel>
    <local:CustomDataGrid ItemsSource="{Binding Model}"
        SelectionMode="Extended"
        AlternatingRowBackground="Aquamarine"
        SelectionUnit="FullRow"
        IsReadOnly="True"
        SnapsToDevicePixels="True"
        SelectedItemsList="{Binding TestSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
  </DockPanel>
</Window>

Мой ViewModel:

public class MyViewModel : INotifyPropertyChanged
{
    private static object _lock = new object ();
    private List<MyModel> _myModel;

    public IEnumerable<MyModel> Model { get { return _myModel; } }

    private IList _selectedModels = new ArrayList ();

    public IList TestSelected
    {
        get { return _selectedModels; }
        set
        {
            _selectedModels = value;
            RaisePropertyChanged ("TestSelected");
        }
    }

    public MyViewModel ()
    {
        _myModel = new List<MyModel> ();
        BindingOperations.EnableCollectionSynchronization (_myModel, _lock);

        for (int i = 0; i < 10; i++)
        {
            _myModel.Add (new MyModel
            {
                Name = "Test " + i,
                Age = i * 22
            });
        }
        RaisePropertyChanged ("Model");
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged (string propertyName)
    {
        var pc = PropertyChanged;
        if (pc != null)
            pc (this, new PropertyChangedEventArgs (propertyName));
    }
}

Моя модель:

public class MyModel
{
    public string Name { get; set; }
    public int Age { get; set; }
}

И, наконец, вот код позади MainWindow:

public partial class MainWindow : Window
{
    public MainWindow ()
    {
        InitializeComponent ();
        this.DataContext = new MyViewModel ();
    }
}

Я надеюсь, что этот чистый дизайн MVVM поможет.

97
Sandesh

То, что я хотел бы сделать, это создать Behaviors, используя System.Windows.Interactivity. Вы должны будете ссылаться на это вручную в вашем проекте.

Имеется элемент управления, который не предоставляет SelectedItems, например, (ListBox, DataGrid)

Вы можете создать класс поведения примерно так

public class ListBoxSelectedItemsBehavior : Behavior<ListBox>
{
    protected override void OnAttached()
    {
        AssociatedObject.SelectionChanged += AssociatedObjectSelectionChanged;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.SelectionChanged -= AssociatedObjectSelectionChanged;
    }

    void AssociatedObjectSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var array = new object[AssociatedObject.SelectedItems.Count];
        AssociatedObject.SelectedItems.CopyTo(array, 0);
        SelectedItems = array;
    }

    public static readonly DependencyProperty SelectedItemsProperty =
        DependencyProperty.Register("SelectedItems", typeof(IEnumerable), typeof(ListBoxSelectedItemsBehavior), 
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

    public IEnumerable SelectedItems
    {
        get { return (IEnumerable)GetValue(SelectedItemsProperty); }
        set { SetValue(SelectedItemsProperty, value); }
    }
}

И для вашего XAML я бы сделал Binding следующим образом, где i это xmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity", а behaviors это пространство имен вашего Behavior класса

<ListBox>
 <i:Interaction.Behaviors>
    <behaviors:ListBoxSelectedItemsBehavior SelectedItems="{Binding SelectedItems, Mode=OneWayToSource}" />
 </i:Interaction.Behaviors>

Предполагая, что ваш DataContext для ListBox имеет свойство SelectedItems в ViewModel, он автоматически обновит SelectedItems. Вы инкапсулировали подписку event от View, т.е.

<ListBox SelectionChanged="ListBox_SelectionChanged"/>

Вы можете изменить класс Behavior на тип DataGrid, если хотите.

26
123 456 789 0

Я использую это решение в моем приложении:

XAML:

<i:Interaction.Triggers>
     <i:EventTrigger EventName="SelectionChanged">
         <i:InvokeCommandAction Command="{Binding SelectItemsCommand}" CommandParameter="{Binding Path=SelectedItems,ElementName=TestListView}"/>
     </i:EventTrigger>
</i:Interaction.Triggers>

в верхней части xaml-файла добавьте следующую строку кода:

xmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity"

SelectedItemsCommand - это тип ICommand, который записан в вашей модели представления.

Используемая DLL:

System.Windows.Interactivity.dll

17
Allen4Tech

При использовании по умолчанию DataGrid для WPF невозможно использовать привязку, как это возможно с помощью свойства SelectedItem-, поскольку свойство SelectedItems- не является DependencyProperty.

Один из способов добиться того, что вы хотите, - зарегистрировать SelectionChanged- Событие DataGrid, чтобы обновить свойство вашей ViewModel, в которой хранятся выбранные элементы. 

Свойство SelectedItems объекта DataGrid имеет тип IList, поэтому вам необходимо привести элементы в списке к вашему определенному типу.

C #

public MyViewModel {
  get{
    return this.DataContext as MyViewModel;
  }
}

private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) {
  // ... Get SelectedItems from DataGrid.
  var grid = sender as DataGrid;
  var selected = grid.SelectedItems;

  List<MyObject> selectedObjects = selected.OfType<MyObject>().ToList();

  MyViewModel.SelectedMyObjects = selectedObjects;
}

XAML

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
    <DataGrid
        SelectionChanged="DataGrid_SelectionChanged"
        />
    </Grid>
</Window>
9
Jehof

Вы можете создать многоразовый базовый класс . Таким образом, вы можете выбрать строки как из кода, так и из пользовательского интерфейса .

Это мой пример класса, я хочу быть выбираемым

public class MyClass
{
    public string MyString {get; set;}   
}

Создать общий базовый класс для выбираемых классов. INotifyPropertyChanged делает обновление пользовательского интерфейса, когда вы устанавливаете IsSelected.

public class SelectableItem<T> : System.ComponentModel.INotifyPropertyChanged
{
    public SelectableItem(T item)
    {
        Item = item;
    }

    public T Item { get; set; }

    bool _isSelected;

    public bool IsSelected {
        get {
            return _isSelected;
        }
        set {
            if (value == _isSelected)
            {
                return;
            }

            _isSelected = value;

            if (PropertyChanged != null)
            { 
                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("IsSelected"));
            }
        }
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
}

Создать выбираемый класс

public class MySelectableItem: SelectableItem<MyClass>
{
    public MySelectableItem(MyClass item)
       :base(item)
    {
    }
}

Создать свойство для привязки к 

ObservableCollection<MySelectableItem> MyObservableCollection ...

Установить пропеты 

MyObservableCollection = myItems.Select(x => new MySelectableItem(x));

Привязать к сетке данных и добавить стиль в DataGridRow, который привязывается к объекту IsSelected объекта MySelectedItem.

<DataGrid  
    ItemsSource="{Binding MyObservableCollection}"
    SelectionMode="Extended">
    <DataGrid.Resources>
        <Style TargetType="DataGridRow">
            <Setter Property="IsSelected" Value="{Binding IsSelected}" />
        </Style>
    </DataGrid.Resources>
</DataGrid>

Чтобы получить выбранные строки/элементы 

var selectedItems = MyObservableCollection.Where(x=>x.IsSelected).Select(y=>y.Item);

Чтобы выбрать строки/элементы

MyObservableCollection[0].IsSelected = true;

Редактировать ———> Кажется, что это не работает, когда EnableRowVirtualization истина

2
AxdorphCoder

Вы можете добавить свойство IsSelected в Model и добавить флажок в строке.

2
Angular_Learn

WPF DataGrid допускает это . Просто установите для DataGrid.Rows.SelectionMode и DataGrid.Rows.SelectionUnit значения «Extended» и «CellOrRowHeader» соответственно. Это можно сделать в Blend, как показано на изображении, которое я включил. Это позволит пользователю выбирать каждую ячейку, целые строки и т.д. Столько раз, сколько они захотят, используя клавиши Shift или Ctrl для продолжения выбора .enter image description here

0
dylansweb

Проект, над которым я работаю, использует MVVM Light и я нашел этот блогpost самым простым решением. Я повторю решение здесь:

Посмотреть модель:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
...

public class SomeVm : ViewModelBase {

    public SomeVm() {
        SelectItemsCommand = new RelayCommand<IList>((items) => {
            Selected.Clear();
            foreach (var item in items) Selected.Add((SomeClass)item);
        });

        ViewCommand = new RelayCommand(() => {
            foreach (var selected in Selected) {
                // todo do something with selected items
            }
        });
    }

    public List<SomeClass> Selected { get; set; }
    public RelayCommand<IList> SelectionChangedCommand { get; set; }
    public RelayCommand ViewCommand { get; set; }
}

XAML:

<Window
    ...
    xmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity"
    xmlns:command="http://www.galasoft.ch/mvvmlight"
    ...
    <DataGrid
        Name="SomeGrid"
        ...
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectionChanged">
                <command:EventToCommand
                    Command="{Binding SelectionChangedCommand}"
                    CommandParameter="{Binding SelectedItems, ElementName=SomeGrid}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
        ...
        <DataGrid.ContextMenu>
            <ContextMenu>
                <MenuItem Header="View" Command="{Binding ViewCommand}" />
            </ContextMenu>
        </DataGrid.ContextMenu>
        ...
0
Seth Reno