Using MVVM, how to pass SelectedItems of a XamDataGrid as parameter to the Command raised by the ContextMenu?

15,973

Solution 1

I had that working by improving Damian answer (which was not quite working).

Here's my solution:

First the Behaviour:

public class DataGridExtender : Behavior<XamDataGrid>
{
    public readonly static DependencyProperty SelectedDataItemsProperty
        = DependencyProperty.Register(
            "SelectedDataItems",
            typeof(ICollection<object>),
            typeof(DataGridExtender),
            new PropertyMetadata());

    public ICollection<object> SelectedDataItems
    {
        get { return (ICollection<object>)GetValue(SelectedDataItemsProperty); }
        set { SetValue(SelectedDataItemsProperty, value); }
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.SelectedItemsChanged += AssociatedObjectOnSelectedItemsChanged;
        AssociatedObjectOnSelectedItemsChanged(AssociatedObject, null);
    }

    protected override void OnDetaching()
    {
        AssociatedObject.SelectedItemsChanged -= AssociatedObjectOnSelectedItemsChanged;
        base.OnDetaching();
    }

    private void AssociatedObjectOnSelectedItemsChanged(object sender, Infragistics.Windows.DataPresenter.Events.SelectedItemsChangedEventArgs e)
    {
        if (SelectedDataItems != null)
        {
            SelectedDataItems.Clear();
            foreach (var selectedDataItem in GetSelectedDataItems())
            {
                SelectedDataItems.Add(selectedDataItem);
            }
        }
    }

    private IEnumerable<object> GetSelectedDataItems()
    {
        var selectedItems = from rec in AssociatedObject.SelectedItems.Records.OfType<DataRecord>() select rec.DataItem;
        return selectedItems.ToList().AsReadOnly();
    }
}

And then its usage:

<igDP:XamDataGrid>
[...]

<i:Interaction.Behaviors>
    <Behaviours:DataGridExtender SelectedDataItems="{Binding SelectedDataItems, Mode=TwoWay}"></Behaviours:DataGridExtender>
</i:Interaction.Behaviors>

[...]

<igDP:XamDataGrid.FieldLayoutSettings>
    [...]
</igDP:XamDataGrid.FieldLayoutSettings>

<igDP:XamDataGrid.FieldLayouts>
    <igDP:FieldLayout>
        [...]
    </igDP:FieldLayout>
</igDP:XamDataGrid.FieldLayouts>

Of course you'll need to have a "SelectedDataItems" in your view model.

Edit: The SelectedDataItems property in the view model has to be instantited first as an empty collection, otherwise it won't work.

Solution 2

For a single item, infragistics was kind enough to add a bindable DependencyProperty called 'ActiveDataItem', which is "the" selected item, if any. It even works two-way, i.e. you can reset the selection from within your ViewModel.

Unfortunately, AFAIK there is no similar thing for multi-selection. You will have to implement this on your own, iterating over the selected records, check if they are datarecords, get the record and dataitem etc...

Solution 3

Try binding your DataGrid's SelectedItem to a property in your viewmodel.

You can then access this property in your OpenTradeHistory() method.

Solution 4

For binding to the selected items I chose to create a behavior using System.Interactivity:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Interactivity;
using Infragistics.Windows.DataPresenter;

namespace Sample {
    public class DataGridExtender : Behavior<XamDataGrid> {
        public readonly static DependencyProperty SelectedDataItemsProperty
            = DependencyProperty.Register(
                "SelectedDataItems"
                , typeof(ICollection<object>)
                , typeof(OzDataGridExtender)
                , new PropertyMetadata(null));

        public ICollection<object> SelectedDataItems {
            get { return (ICollection<object>)GetValue(SelectedDataItemsProperty); }
            set { SetValue(SelectedDataItemsProperty, value); }
        }

        protected override void OnAttached() {
            base.OnAttached();
            AssociatedObject.SelectedItemsChanged += AssociatedObjectOnSelectedItemsChanged;
            AssociatedObjectOnSelectedItemsChanged(AssociatedObject, null);
        }

        protected override void OnDetaching() {
            AssociatedObject.SelectedItemsChanged -= AssociatedObjectOnSelectedItemsChanged;
            base.OnDetaching();
        }

        private void AssociatedObjectOnSelectedItemsChanged(object sender, Infragistics.Windows.DataPresenter.Events.SelectedItemsChangedEventArgs e) {
            SelectedDataItems = GetSelectedDataItems();
            //AssociatedObject.SetValue(SelectedDataItemsPropertyKey, SelectedDataItems);
        }

        private ICollection<object> GetSelectedDataItems() {
            var selectedItems = from rec in AssociatedObject.SelectedItems.Records.OfType<DataRecord>()
                                select rec.DataItem;
            return selectedItems.ToList().AsReadOnly();
        }
    }
}

Some where in your view would have something like the following (I've ommitted the namespace mappings for brevity):

Now your problem with the command binding on a context menu thats something else... I'll revisit this

Share:
15,973
user290669
Author by

user290669

Updated on June 04, 2022

Comments

  • user290669
    user290669 almost 2 years

    I'm trying to pass the item on XamDataGrid on which I do a mouse right click to open a ContextMenu, which raises a Command in my ViewModel. Somehow the method that the Command calls is not reachable in debug mode.

    This is the snipped from the view

    <ig:XamDataGrid DataSource="{Binding DrdResults}" Height="700" Width="600">
      <ig:XamDataGrid.ContextMenu>
        <ContextMenu DataContext="{Binding RelativeSource={RelativeSource Mode=Self},
                                   Path=PlacementTarget.DataContext}"
                     AllowDrop="True" Name="cmAudit">
          <MenuItem Header="View History" 
                    Command="{Binding ViewTradeHistory}"
                    CommandParameter="{Binding Path=SelectedItems}">
          </MenuItem>
        </ContextMenu>
      </ig:XamDataGrid.ContextMenu>
      <ig:XamDataGrid.FieldSettings>
        <ig:FieldSettings AllowFixing="NearOrFar"
                          AllowEdit="False" 
                          Width="auto" Height="auto"  />
      </ig:XamDataGrid.FieldSettings>
    </ig:XamDataGrid>
    

    My code in the corresponding ViewModel for this View is as follows.

    public WPF.ICommand ViewTradeHistory
    {
      get
      {
        if (_viewTradeHistory == null)
        {
          _viewTradeHistory = new DelegateCommand(
          (object SelectedItems) =>
          {
            this.OpenTradeHistory(SelectedItems); 
          });
        }
        return _viewTradeHistory;
      }
    }
    

    And lastly the actual method that gets called by the Command is as below

    private void OpenTradeHistory(object records)
    {
      DataPresenterBase.SelectedItemHolder auditRecords
        = (DataPresenterBase.SelectedItemHolder)records;
      // Do something with the auditRecords now.
    }
    

    I'm not sure what am I doing incorrectly here. Any help will be very much appreciated.

    Thanks, Shravan

  • user290669
    user290669 almost 14 years
    I'm not sure if Right click on the XamDataGrid row selects the record as a SelectedItem. And this doesn't really solve the issue as I'm trying to pass a parameter from the Command that handles the right click > context Menu on the XamDataGrid.
  • Amsakanna
    Amsakanna almost 14 years
    @saddaypally: What is meant by SelectedItems in this line CommandParameter="{Binding Path=SelectedItems}"> ? If those were the original Selected Items in the grid then why do you send them as a parameter, if right click doesn't select an item?
  • user290669
    user290669 almost 14 years
    Thanks for coming back on this. I didn't find a SelectedItem property on XamDataGrid, all I found was this and I thought, I could use this and iterate over the items myself when I'm able to get a hold of it. I actually need to be able to pass the values from Row on which I right click on and open a ContextMenu Item to view details about that item from the Grid. does that make it clear? Any help, appreciated.
  • Antoine Jaussoin
    Antoine Jaussoin over 11 years
    Thanks for this, it helped me a lot to get a working solution. Now it doesn't quite work as it is because you are, in the AssociatedObjectOnSelectedItemsChanged method, setting the SelectedDataItems reference to a new collection each time the selection changes, which breaks any binding. What I did instead is clear then add the items to that collection without changing its reference (see my answer)
  • Damian
    Damian over 11 years
    Glad it helped you get on the right track. I thought I ripped this directly from working code, but since I don't have access to that code base anymore no way to tell. Anyways, good catch.
  • Sven
    Sven over 9 years
    What a shame on Infragistics to not Support such a thing out-of-the-box. I'm currently evaluating their grid. Maybe I'll switch to another vendor.