Search code examples
wpfdatagridcommandprismcommandparameter

Binding CommandParameters to Property from other View


I have a view (Main View) with a toolbar and a TabContent region (PRISM). In the TabContent region I have two tabs (two views -View A, View B-) representing the same model (Contacts) in a different way.

View A contains a DataGrid with Contacts. The toolbar in the Main View contains a "Delete Button" with a DeleteCommand. I want to send with the DeleteCommand the selected contacts from View A as Command Parameters, but with the code I show below, the command parameters are null. It looks like the Main View doesn't retrieve the Selected Items from the DataGrid located in View A. How can I accomplish this?

This is the Main View:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="30" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <DockPanel Grid.Row="0" Background="#D6D6DC">
        <ToolBar Style="{StaticResource ModuleToolBarStyle}">
            <TextBlock Margin="10,0,0,0" Text="Contacts"></TextBlock>
            <Button Name="addContactButton" ToolTip="Add Contact">
                <Image Source="/PrismApp.Controls;component/Images/add.png"/>
            </Button>
            <Button Name="deleteContactsButton" ToolTip="Delete selected Contacts"
                    Command="{Binding DeleteContactCommand}" CommandParameter="{Binding SelectedItems, ElementName=ContactsList}">
                <Image Source="/PrismApp.Controls;component/Images/delete.png"/>
            </Button>
            <ToggleButton Name="ViewAButton" ToolTip="View A" Command="{Binding NavigateToViewACommand}"
                          IsChecked="{Binding IsViewAActive}">
                <Image Source="/PrismApp.Controls;component/Images/listblack.png"/>
            </ToggleButton>
            <ToggleButton Name="ViewBButton" ToolTip="View B" Command="{Binding NavigateToViewBCommand}"
                          IsChecked="{Binding IsViewBActive}">
                <Image Source="/PrismApp.Controls;component/Images/tilesblack.png"/>
            </ToggleButton>
        </ToolBar>
    </DockPanel>

    <TabControl Grid.Row="1" prism:RegionManager.RegionName="ContactsViewRegion">
        <TabControl.ItemContainerStyle>
            <Style TargetType="{x:Type TabItem}">
                <Setter Property="Visibility" Value="Collapsed"/>
            </Style>
        </TabControl.ItemContainerStyle>
    </TabControl>
</Grid>

This is View A:

<Grid>
    <DataGrid x:Name="ContactsList" Margin="20" AutoGenerateColumns="False" IsReadOnly="True" CanUserResizeRows="False"
              CanUserResizeColumns="True" ColumnWidth="*" ItemsSource="{Binding Contacts}">

    </DataGrid>
</Grid>

The view model is the same for Main View, View A and View B.


Solution

  • To do what I think you need, the first thing you will need in your view where the DataGrid resides is the following namespace.

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

    This is something like what you will want to add to the DataGrid; specifically, the i:Interaction.Triggers part:

    <DataGrid x:Name="ContactsDataGrid" ItemsSource="{Binding Contacts}" Margin="20" CanUserAddRows="False"
                    VerticalAlignment="Top" IsReadOnly="True" AutoGenerateColumns="False" 
                    SelectionMode="Extended" SelectionUnit="FullRow">
        <DataGrid.Columns>
            ...
        </DataGrid.Columns>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectionChanged">
                <i:InvokeCommandAction Command="{Binding SelectedItemsCommand}" 
                                        CommandParameter="{Binding Path=SelectedItems,ElementName=ContactsDataGrid}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </DataGrid>
    

    The EventTrigger above is what will sync the SelectedItems in the DataGrid with a ViewModel.

    ViewModel additions:

    // A list to store the selected contacts in
    private List<Contact> _selectedContacts = new List<Contact>();
    
    // A DelegateCommand that will be invokes when selections change in the DataGrid
    public DelegateCommand<object> SelectedItemsCommand { get; set; }
    

    In your ctor, or wherever else you bind commands, add this:

    SelectedItemsCommand = new DelegateCommand<object>(SelectContacts);

    SelectContacts method that the DelegateCommand calls:

    private void SelectContacts(object contacts)
    {
        var selected_contacts = contacts as System.Collections.IList;
        if (selected_contacts != null)
        {
            _selectedContacts.Clear();
            foreach (var contact in selected_contacts)
            {
                _selectedContacts.Add((Contact)contact);
            }
        }
    }
    

    You now have a private _selectedContacts collection in the ViewModel that contains all of the items selected in the view, and will update when the SelectionChanged event fires from the DataGrid in the View.

    Do with that what you need to. Best of luck!