Как я могу выбрать несколько элементов из DataGrid
в проекте MVVM MVPM?
Выберите несколько элементов из DataGrid в проекте MVVM MVPM
Ответ 1
Вы можете просто добавить свойство пользовательской зависимости, чтобы сделать это:
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 поможет.
Ответ 2
Что бы я сделал, создайте 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
i.e.,
<ListBox SelectionChanged="ListBox_SelectionChanged"/>
Вы можете изменить класс Behavior
на тип DataGrid
, если хотите.
Ответ 3
Я использую это решение в своем приложении:
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
Ответ 4
При использовании по умолчанию DataGrid
для WPF невозможно использовать привязку, как это возможно при использовании SelectedItem
-Property, потому что SelectedItems
-Property не является DependencyProperty.
Один из способов сделать то, что вы хотите, - зарегистрировать SelectionChanged
-Event DataGrid, чтобы обновить свойство ViewModel, которое хранит выбранные элементы.
Свойство SelectedItems DataGrid имеет тип IList, поэтому вам нужно отнести элементы в списке к вашему конкретному типу.
С#
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>
Ответ 5
Вы можете добавить свойство IsSelected в Model и добавить checkBox в строку.
Ответ 6
Вы можете использовать maka многоразовый базовый класс. Таким образом вы можете выбрать строки как из кода, так и из интерфейса пользователя.
Это мой примерный класс, который я хочу выбрать
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 ...
Установите propety
MyObservableCollection = myItems.Select(x => new MySelectableItem(x));
Привяжите к datagrid и добавьте стиль в DataGridRow, который привязан к IsSelected propety на 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;
Ответ 7
Для этого используется WPF DataGrid. Просто установите DataGrid.Rows.SelectionMode и DataGrid.Rows.SelectionUnit в "Extended" и "CellOrRowHeader" соответственно. Это можно сделать в Blend, как показано на изображении, которое я включил. Это позволит пользователю выбирать каждую ячейку, целые строки и т.д. Столько, сколько им нравится, используя клавишу shift или ctrl для продолжения выбора.
Ответ 8
В проекте, над которым я работаю, используется 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>
...