According to MVVM pattern the ViewModel interacts with the Model but doesn’t know anything about its View. As a result dependency properties of controls get bound to ViewModel properties by specifying bindings in the corresponding View (XAML). DataGrid control has a property called SelectedItems, which in multi selection mode holds the items that are selected. Unfortunately SelectedItems of DataGrid is not a dependency property and therefore not bindable. There are several approaches to make the selected items of a DataGrid available in ViewModel. One of these approaches is to extend DataGrid control with a bindable version of the SelectedItems property as described below.

First we have to create a new class, that derives from DataGrid. Beside the dependency property for the selected items an event handler for selection changed events has to be defined. In the event handler the DataGrids SelectedItems property gets assigned to the custom dependency property. To execute the event handler on every selection change, it has to be registered to the SelectionChanged event in the constructor of the class.

CustomDataGrid.cs

/**
 * Copyright 2017 d-fens GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System.Collections;
using System.Windows;
using System.Windows.Controls;

namespace biz.dfch.CS.ArbitraryWpfApplication.UI.Controls
{
    public class CustomDataGrid : DataGrid
    {
        public CustomDataGrid()
        {
            SelectionChanged += CustomDataGridSelectionChanged;
        }

        void CustomDataGridSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            SelectedItemsList = SelectedItems;
        }

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

In the ViewModel we add a property of type IList to bind the SelectedItemsList dependency property to.

public IList _selectedProtocols = new ArrayList();
public IList SelectedProtocols
{
    get
    {
        return _selectedProtocols;
    }
    set
    {
        _selectedProtocols = value;
        RaisePropertyChangedEvent(nameof(SelectedProtocols));
    }
}

In the corresponding View (XAML) the CustomDataGrid control can be used as follows.

<Page x:Class="biz.dfch.CS.ArbitraryWpfApplication.UI.Pages.Protocols"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:vm="clr-namespace:biz.dfch.CS.ArbitraryWpfApplication.UI.ViewModels"
      xmlns:p="clr-namespace:biz.dfch.CS.ArbitraryWpfApplication.UI.Properties"
      xmlns:cc="clr-namespace:biz.dfch.CS.ArbitraryWpfApplication.UI.Controls"
      xmlns:gl="clr-namespace:System.Globalization;assembly=mscorlib"
      mc:Ignorable="d"

      Title="{x:Static p:Resources.Page_Protocols_Title}" >

    <Page.DataContext>
        <vm:ProtocolsViewModel></vm:ProtocolsViewModel>
    </Page.DataContext>

    <DockPanel HorizontalAlignment="Stretch">
        <cc:CustomDataGrid x:Name="ProtocolList" VerticalAlignment="Top" HorizontalAlignment="Stretch" CanUserAddRows="False" AutoGenerateColumns="False" ScrollViewer.PanningDeceleration="20" ScrollViewer.PanningMode="Both" ScrollViewer.CanContentScroll="True" SelectionMode="Extended" SelectionUnit="FullRow" Style="{StaticResource AzureDataGrid}" IsReadOnly="True" ItemsSource="{Binding Protocols}" SelectedItemsList="{Binding SelectedProtocols, Mode=OneWayToSource}">
            <DataGrid.Columns>
                <DataGridTextColumn Width="*" Header="{x:Static p:Resources.Page_Protocols_DataGridTextColumn__ProtocolId}" Binding="{Binding Id}" />
                <DataGridTextColumn Width="*" Header="{x:Static p:Resources.Page_Protocols_DataGridTextColumn__Municipality}" Binding="{Binding Metadata.MunicipalityName}" />
                <DataGridTextColumn Width="*" Header="{x:Static p:Resources.Page_Protocols_DataGridTextColumn__Operator}" Binding="{Binding Metadata.Operator}" />
            </DataGrid.Columns>
        </cc:CustomDataGrid>
    </DockPanel>
</Page>
« WPF Series -7- ViewModel Validation using DataAnnotations WPF Series -9- CustomListBox »

3 Comments »

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.