DataGrid Selected Row in ViewModel?

Jan 28, 2009 at 2:57 PM
Hi,

We are trying to figure out how to get the selected row (event and item) from the DataGrid in our ViewModel in our WPF app. I have searched and not found any examples of this. I would have thought that this would be a common situation to solve. Any help or ideas would be greatly appreciated as we are stuck at this point.

thanks!
Bill
Coordinator
Jan 28, 2009 at 6:20 PM
In your ViewModel you can create a property that will represent row selection:

public

 

bool IsSelected
{
    get { return _isSelected; }
    set
    {
        _isSelected =
value;
        OnPropertyChanged(
"IsSelected");
    }
}


Then in a DataGridRow Style you can bind the DataGridRow.IsSelected DP with your ViewModel Property.

<Style x:Key="RowStyle" TargetType="{x:Type toolkit:DataGridRow}">
    <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>

 

Jan 28, 2009 at 8:17 PM
Hi Vincent,

Thanks for the quick reply! This makes sense to me and I added this to our code but we are never getting a "IsSelected" property occurring. We had to add the OnPropertyChanged method as well. I would think that selecting a row in the DataGrid would cause the set property to get called but I put a breakpoint on both the get and set and it never hit either. Maybe there's something else I am missing. I'm sure it something really simple and unfortunately I'm a newbie and am missing it..

thanks,
Bill
Coordinator
Jan 28, 2009 at 8:49 PM
Could you post a code snippet?
Jan 28, 2009 at 10:09 PM
Hi, Here we go... public class ShipmentWindowViewModel : DependencyObject, INotifyPropertyChanged { private CollectionView shipment; private bool isSelected; //public ObservableCollection<shipmentobj /> shipment {get; set;} public List<shipmentobj /> modelShipmentList; private ShipmentService shipmentService = new ShipmentService(); private DelegateCommand clickCommand; public event PropertyChangedEventHandler PropertyChanged; public ShipmentWindowViewModel() { this.shipment = new CollectionView(shipmentService.shipmentCollection); this.clickCommand = new DelegateCommand(this.ClickCommandHandler); } public CollectionView Shipment { get { return this.shipment; } } public DelegateCommand ClickCommand { get { return this.clickCommand; } } private void ClickCommandHandler(Object sender, EventArgs e) { ShipmentObj so = this.shipment.CurrentItem as ShipmentObj; MessageBox.Show(so.ShipmentID); } public bool IsSelected { get { return isSelected; } set { //if (value == isSelected) // return; isSelected = value; OnPropertyChanged("IsSelected"); ShipmentObj so = this.shipment.CurrentItem as ShipmentObj; MessageBox.Show(so.ShipmentID); } } protected void OnPropertyChanged(string name) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(name)); } } } <custom:DocumentContent title="StopWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Icon="images/stop.png" Width="662" Height="357" xmlns:col="clr-namespace:System.Collections;assembly=mscorlib" xmlns:custom="clr-namespace:DockingLibrary;assembly=DockingLibrary" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:vRA.WPF.UI="http://schemas.microsoft.com/wpf/2008/toolkit" xmlns:local="clr-namespace:vRA.WPF.UI" xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="vRA.WPF.UI.ShipmentWindow"><custom:DocumentContent.DataContext><local:ShipmentWindowViewModel></local:ShipmentWindowViewModel></custom:DocumentContent.DataContext><custom:DocumentContent.Resources> <style TargetType="{x:Type dg:DataGridRow}" x:Key="ShipmentItemStyle"> <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" /> </style> </custom:DocumentContent.Resources><grid Background="Beige" Name="stopGrid" /><grid.columndefinitions /><columndefinition Width="*" /><columndefinition Width="auto" /><grid Grid.Column="0" /><groupbox Width="auto" Height="68" Background="Beige" Name="groupBox1" VerticalAlignment="Top" Foreground="Black" Margin="6,0" Header="Search" /><grid Width="auto" Height="auto" /><label Width="77" Name="lblSearchShipMent" Margin="11,9,0,11" Content="Shipment ID:" HorizontalAlignment="Left"></label><textbox Width="120" Name="txtShipmentID" Margin="90,11,0,14.723" HorizontalAlignment="Left" TextChanged="txtShipmentID_TextChanged" /><button name="btnSearch" type="button" Width="120" Margin="225,12,0,13.723" HorizontalAlignment="Left" Click="btnSearch_Click">Search</button> <!--SelectionChanged="shipmentDataGrid_SelectionChanged"--><dg:DataGrid Background="GhostWhite" Margin="6,74,0,0" ItemContainerStyle="{StaticResource ShipmentItemStyle}" IsSynchronizedWithCurrentItem="true" ItemsSource="{Binding Shipment}" FlowDirection="LeftToRight" AutoGenerateColumns="False" x:Name="shipmentDataGrid"><dg:DataGrid.Columns><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=DepotID}" Header="Depot ID"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=OrginID}" Header="Orgin ID"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=DestID}" Header="Dest ID"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=Quantity1}" Header="Q1"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=Quantity2}" Header="Q2"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=Quantity3}" Header="Q3"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=ServiceRestrictionFlg}" Header="Service Restriction Flg"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=ShipmentStatus}" Header="Shipment Status"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=LoadType}" Header="Load Type"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=CommodityType}" Header="Commodity Code"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=StopType}" Header="Stop Type"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=RouteZone}" Header="Route Zone"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=ShipmentSCAC}" Header="SCAC"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=ShipmentPoint}" Header="Shipment Point"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=Mode}" Header="Mode"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=PSD}" Header="PSD"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=ShipmentFrequency}" Header="Shipment Frequency"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=ServiceCharge}" Header="Service Charge"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=HelperRequired}" Header="Helper Assigned"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=Comments}" Header="Service Comment"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=ShipmentType}" Header="Shipment Type"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=ServiceCode}" Header="Service Code"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=ShipmentOpenTime}" Header="Open Time"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=ShipmentCloseTime}" Header="Close Time"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=ShipmentID}" Header="Shipment ID"></dg:DataGridTextColumn><dg:DataGridTextColumn Width="Auto" Binding="{Binding Path=ShimentDate}" Header="Shipment Date"></dg:DataGridTextColumn></dg:DataGrid.Columns></dg:DataGrid><grid Grid.Column="1" /><custom:DockManager Name="dockManager"></custom:DockManager></custom:DocumentContent>Thanks! Bill
Jan 28, 2009 at 10:11 PM

So sorry - that's sure no good..
When I hit the Post it reformatted all the code.

I'm not sure how to keep the code formatted.

 

Thanks,

Bill

Jan 29, 2009 at 1:42 PM
Hi Vincent:

XAML file:

<custom:DocumentContent x:Class="vRA.WPF.UI.ShipmentWindow"
                        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit"
                        xmlns:local="clr-namespace:vRA.WPF.UI"
                        xmlns:vRA.WPF.UI="http://schemas.microsoft.com/wpf/2008/toolkit"   
                        xmlns:sys="clr-namespace:System;assembly=mscorlib"
                        xmlns:custom="clr-namespace:DockingLibrary;assembly=DockingLibrary"
                        xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
                        Title="StopWindow"
                        Height="357"
                        Width="662" 
                        Icon="images/stop.png"                     
    >   
   
    <custom:DocumentContent.DataContext >
        <local:ShipmentWindowViewModel />
    </custom:DocumentContent.DataContext>
   
    <custom:DocumentContent.Resources>
        <Style x:Key="ShipmentItemStyle" TargetType="{x:Type dg:DataGridRow}">
            <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
        </Style>
    </custom:DocumentContent.Resources>
   
    <Grid Name="stopGrid"  Background="Beige" >
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="auto" />
        </Grid.ColumnDefinitions>
        <Grid Grid.Column="0" >
            <GroupBox Header="Search" Width="auto" Height="68" Margin="6,0" Foreground="Black" Background="Beige" Name="groupBox1" VerticalAlignment="Top">
                 <Grid Width="auto" Height="auto">
                    <Label HorizontalAlignment="Left" Margin="11,9,0,11" Name="lblSearchShipMent"  Width="77" Content="Shipment ID:"></Label>
                    <TextBox HorizontalAlignment="Left" Margin="90,11,0,14.723" Name="txtShipmentID" Width="120" TextChanged="txtShipmentID_TextChanged" />
                    <Button Width="120" Margin="225,12,0,13.723" Name="btnSearch" HorizontalAlignment="Left" Click="btnSearch_Click">Search</Button>
                </Grid>
                    </GroupBox>
            <!--SelectionChanged="shipmentDataGrid_SelectionChanged"-->
           
            <dg:DataGrid x:Name="shipmentDataGrid" AutoGenerateColumns="False"
                         Margin="6,74,0,0" FlowDirection="LeftToRight" Background="GhostWhite"                         
                         ItemsSource="{Binding Shipment}"  IsSynchronizedWithCurrentItem="true" ItemContainerStyle="{StaticResource ShipmentItemStyle}">
    
                <dg:DataGrid.Columns>
                    <dg:DataGridTextColumn Width="Auto" Header="Depot ID"    Binding="{Binding Path=DepotID}" ></dg:DataGridTextColumn>
                    <dg:DataGridTextColumn Width="Auto" Header="Orgin ID"    Binding="{Binding Path=OrginID}"></dg:DataGridTextColumn>
                    <dg:DataGridTextColumn Width="Auto" Header="Dest ID" Binding="{Binding Path=DestID}"></dg:DataGridTextColumn>
                    ...

ViewModel

namespace vRA.WPF.UI
{
    public class ShipmentWindowViewModel : DependencyObject, INotifyPropertyChanged
    {
        private CollectionView shipment;
        private bool isSelected;
        //public ObservableCollection<ShipmentObj> shipment {get; set;}
        public List<ShipmentObj> modelShipmentList;
        private ShipmentService shipmentService = new ShipmentService();
        private DelegateCommand clickCommand;

        public event PropertyChangedEventHandler PropertyChanged;       

        public ShipmentWindowViewModel()
        {
            this.shipment = new CollectionView(shipmentService.shipmentCollection);
            this.clickCommand = new DelegateCommand(this.ClickCommandHandler);
        }

        public CollectionView Shipment
        {
            get { return this.shipment; }
        }

        public DelegateCommand ClickCommand
        {
            get { return this.clickCommand; }
        }

        private void ClickCommandHandler(Object sender, EventArgs e)
        {
            ShipmentObj so = this.shipment.CurrentItem as ShipmentObj;

            MessageBox.Show(so.ShipmentID);
        }

        public bool IsSelected
        {
            get { return isSelected; }
            set
            {
                //if (value == isSelected)
                //    return;
                isSelected = value;
              
                OnPropertyChanged("IsSelected");
                ShipmentObj so = this.shipment.CurrentItem as ShipmentObj;
                MessageBox.Show(so.ShipmentID);
            }

        }

        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }

    }
}



I have added the code as you suggested. Do you see anything obvious?

thanks!
Bill
Jan 30, 2009 at 1:21 PM
Hi Bill,

Why don't you use the CurrentItem of ListCollectionView. You can keep this synchronize with the datagrid by setting the property IsSynchronizedWithCurrentItem="True" 

In your model / presenter you should have no knowledge of your view.

Cees

Coordinator
Jan 30, 2009 at 8:36 PM

I took a look at the code and I don't see anything obvious.  Is ShipmentWindowVM.IsSelected being called when selection is updated on the DataGrid?

CeesVersteeg, for the problem that's being addressed (modeling selection in the viewModel), it does not have any knowledge of the view.  LCV.CurrentItem is not really the same thing as IsSelected.  Let's say you want to to full row selection through your viewModel, you cannot do that with CurrentItem.

Jan 31, 2009 at 11:15 AM

Hi,

Thanks Cees for the reply but what Vincent is saying is true. I am using the WPF DataGrid and want to be able to wire up an IsSelected property so that when I select a row in the UI in the DataGrid that I get that notification in the ViewModel. It seems like it should be quite simple, but to your question, Vincent, I never get the ShipmentWindowVM.IsSelected being called. I re-read the latest MSDN article that Josh Smith wrote on MVVM and although he's using a ListView, he is also using IsSelected in a similar fashion and it gets called when you select an item in the list. If I use the code behind I can do all of these things, but the whole idea of using M-V-VM is to get away from having all this code in the code behind where I can't write unit tests for it, etc.

thanks!

Bill