it-swarm.com.de

Wählen Sie mehrere Elemente aus einem DataGrid in einem MVVM-WPF-Projekt aus

Wie kann ich mehrere Elemente aus einer DataGrid in einem MVVM WPF-Projekt auswählen?

55
MegaMind

Sie können einfach eine custom-Abhängigkeitseigenschaft hinzufügen, um dies auszuführen:

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
}

Jetzt können Sie diese dataGrid in der XAML verwenden:

<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>

Meine 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));
    }
}

Mein Modell:

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

Und schließlich ist hier der Code hinter MainWindow:

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

Ich hoffe, dieses saubere MVVM-Design hilft.

97
Sandesh

Ich würde Behaviors mit System.Windows.Interactivity erstellen. Sie müssten in Ihrem Projekt manuell darauf verweisen.

Bei einem Steuerelement, das SelectedItems nicht verfügbar macht, z. B. (ListBox, DataGrid)

Sie können so eine Verhaltensklasse erstellen

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); }
    }
}

Und auf Ihrer XAML würde ich die Binding so machen, wo ixmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity" und behaviors der Namespace Ihrer Behavior-Klasse ist

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

Wenn Sie davon ausgehen, dass Ihre DataContext für die ListBox die SelectedItems-Eigenschaft in der ViewModel hat, aktualisiert sie automatisch die SelectedItems. Sie haben die event, die von der View abonniert wurde, eingekapselt, d. H.

<ListBox SelectionChanged="ListBox_SelectionChanged"/>

Sie können die Behavior-Klasse in den Typ DataGrid ändern, wenn Sie möchten.

26
123 456 789 0

Ich verwende diese Lösung in meiner App:

XAML:

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

fügen Sie am Anfang Ihrer XAML-Datei diese Codezeile ein:

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

SelectedItemsCommand ist der ICommand-Typ, der in Ihr Viewmodel geschrieben wird.

Verwendete DLL:

System.Windows.Interactivity.dll

17
Allen4Tech

Mit dem Standardwert DataGrid von WPF ist es nicht möglich, eine Bindung zu verwenden, da dies mit der SelectedItem- Eigenschaft möglich ist, da die SelectedItems- Eigenschaft keine DependencyProperty ist.

Eine Möglichkeit dazu ist, das SelectionChanged- Event des DataGrid zu registrieren, um die Eigenschaft Ihres ViewModel zu aktualisieren, in der die ausgewählten Elemente gespeichert werden. 

Die Eigenschaft SelectedItems des DataGrid ist vom Typ IList. Daher müssen Sie die Elemente in der Liste in Ihren spezifischen Typ umwandeln.

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

Sie können eine wiederverwendbare generische Basisklasse erstellen. Auf diese Weise können Sie Zeilen sowohl aus Code als auch aus der Benutzeroberfläche auswählen.

Dies ist meine Beispielklasse, die ich auswählbar machen möchte

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

Erstellen Sie eine generische Basisklasse für auswählbare Klassen. INotifyPropertyChanged führt die Aktualisierung der Benutzeroberfläche durch, wenn Sie IsSelected festlegen.

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;
}

Wählbare Klasse erstellen

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

Erstellen Sie eine Eigenschaft, an die Sie binden möchten 

ObservableCollection<MySelectableItem> MyObservableCollection ...

Propety einstellen 

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

Binden Sie an datagrid und fügen Sie der DataGridRow einen Stil hinzu, der an das IsSelected-Element des MySelectedItem gebunden wird

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

Um ausgewählte Zeilen/Elemente zu erhalten 

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

Zeilen/Elemente auswählen

MyObservableCollection[0].IsSelected = true;

Bearbeiten ———> Es scheint, dass es nicht funktioniert, wenn EnableRowVirtualization wahr ist

2
AxdorphCoder

Sie können die "IsSelected" -Eigenschaft zum Modell hinzufügen und ein Kontrollkästchen in die Zeile einfügen.

2
Angular_Learn

WPF DataGrid ermöglicht dies . Setzen Sie einfach DataGrid.Rows.SelectionMode und DataGrid.Rows.SelectionUnit auf "Extended" bzw. "CellOrRowHeader". Dies kann in Blend erfolgen, wie in dem von mir enthaltenen Bild gezeigt. Auf diese Weise kann der Benutzer jede Zelle, ganze Reihe usw. beliebig auswählen, indem Sie entweder die Umschalttaste oder die Strg-Taste drücken, um mit der Auswahl von ..__ fortzufahren.enter image description here

0
dylansweb

Das Projekt, an dem ich arbeite, verwendet MVVM Light und ich habe dieses Blogpost gefunden, um die einfachste Lösung zu sein. Ich werde die Lösung hier wiederholen:

Modell anzeigen:

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