12
votes

.Net 4 WPF DataGrid C# MMVM

When the DataGrid SelectionUnit is full row, wpf databinding and the collectionview take care of letting me know in the viewmodel what is the actively selected item via the view's currentitem property. This works great for readonly grids with the selection mode set to the fullrow.

Now I have an editable grid. So I set the SelectionUnit=Cell to make it easier to spot which cell one is in. Now all of a sudden the grid no longer has any ability to track the selection item. I can't even set SelectedItem when set to cell mode. So now the viewmodel always thinks it's on the first row. I can handle SelectedCellsChanged in the grid to figure out what row I'm on, I just have no way of letting the viewmodel know since the grid's SelectedItem can no longer be set!

I don't understand why the grid can't still have a SelectedItem when in cell select mode.

Short of hardcoding into my grid to cast the ItemSource to my collectionview to call MoveCurrentTo from the SelectedCellsChanged event, is there any other MVVM true way to keep the view's CurrentItem in sync with the grid?

Either that, or I change the grid style to remove or reduce the row highlight effect when I have an editable grid.

3
if you have to "hardcode", create an interfaceJake Berger
Yes, that's what've I've done for now.happyfirst
i just looked at the DataGrid source and in Cell mode, they don't sync with SelectedItemJake Berger

3 Answers

6
votes

I was searching for the same problems and found a simple solution

To access to the row with SelectionUnit set to Cell you have to do:

DataGridXX.SelectedCells[0].item

It works only if you can select only one cell at time (not in Extended mode).

0
votes

i too Have a similar problem, so here is the Style i used(Copied from Net). So you copy the WhistlerBlue theme from http://datagridthemesfromsl.codeplex.com/ and make following modifications. Hope this helps.

<!--Cell-->
<Style x:Key='CellStyle' TargetType="{x:Type controls:DataGridCell}" >
    <Setter Property="Foreground" Value="{StaticResource ThemeForegroundBrush}" />
    <Setter Property="Height" Value="Auto" />
    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
    <Setter Property="Cursor" Value="Arrow" />
    <Setter Property="BorderThickness" Value="1" />
    <!--Padding hack-->
    <Setter Property="Padding" Value="2 5 2 5" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type controls:DataGridCell}">
                <Grid x:Name="Root" Background="Transparent">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <Rectangle x:Name="FocusVisual" Margin="0,-2,0,0" 
                               Stroke="White"  Fill="White" 
                               Opacity="0" IsHitTestVisible="false"/>
                    <ContentPresenter Margin="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Content="{TemplateBinding Content}" 
                                      ContentTemplate="{TemplateBinding ContentTemplate}" Cursor="{TemplateBinding Cursor}"/>
                    <Rectangle x:Name="RightGridLine" VerticalAlignment="Stretch" Width="1" Grid.Column="1" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>

</Style>

<!-- DataGridRow -->
<Style x:Key='RowStyle' TargetType="{x:Type controls:DataGridRow}">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="SnapsToDevicePixels" Value="true"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type controls:DataGridRow}">
                <Border x:Name="DGR_Border"
              Background="{TemplateBinding Background}"
              BorderBrush="{TemplateBinding BorderBrush}"
              BorderThickness="{TemplateBinding BorderThickness}"
              SnapsToDevicePixels="True">
                    <primitives:SelectiveScrollingGrid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>

                        <Grid.RowDefinitions>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height='Auto' />
                        </Grid.RowDefinitions>
                        <Rectangle x:Name="Selected" Margin="0" Grid.RowSpan="2" Grid.ColumnSpan="2" 
                               Fill="{StaticResource BtnOverFill}" Stroke="{StaticResource selectedStroke}" 
                               Opacity="0"/>
                        <Rectangle x:Name="SelectedHighlight" Margin="1" Grid.RowSpan="2" Grid.ColumnSpan="2" 
                               Stroke="#A0FFFFFF" 
                               Opacity="0"/>
                        <primitives:DataGridRowHeader Grid.RowSpan="2"
                                                    primitives:SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"                                       
                                                    Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:DataGrid}}, 
                            Path=HeadersVisibility, 
                            Converter={x:Static controls:DataGrid.HeadersVisibilityConverter},
                            ConverterParameter={x:Static controls:DataGridHeadersVisibility.Row}}"/>
                        <Rectangle x:Name="Over" Margin="0" Grid.RowSpan="2" Grid.ColumnSpan="2" 
                               Fill="{StaticResource hoverGradient}" 
                               Stroke="{StaticResource hoverStroke}" 
                               Opacity="0"/>
                        <primitives:DataGridCellsPresenter Grid.Column="1"
                                        ItemsPanel="{TemplateBinding ItemsPanel}"
                                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        <primitives:DataGridDetailsPresenter Grid.Column="1" Grid.Row="1"
                                                           x:Name='DetailsPresenter' 
                                                           primitives:SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:DataGrid}}, Path=AreRowDetailsFrozen, Converter={x:Static controls:DataGrid.RowDetailsScrollingConverter}, ConverterParameter={x:Static controls:SelectiveScrollingOrientation.Vertical}}"                                                                                                                         
                                                           Visibility="{TemplateBinding DetailsVisibility}"                                                               
                                                           />
                        <Rectangle Height="1" HorizontalAlignment="Stretch" 
                                   x:Name="BottomGridLine" 
                                   Fill="{StaticResource HorizontalVerticalGridLinesBrush}" 
                                   Grid.Column="1" Grid.Row="2" />
                    </primitives:SelectiveScrollingGrid>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property='IsSelected' Value='True'>
                        <Trigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation Duration="0" Storyboard.TargetName="Selected" Storyboard.TargetProperty="Opacity" To="0.84"/>
                                    <DoubleAnimation Duration="0" Storyboard.TargetName="SelectedHighlight" Storyboard.TargetProperty="Opacity" To="1"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation Duration="0" Storyboard.TargetName="Selected" Storyboard.TargetProperty="Opacity" To="0"/>
                                    <DoubleAnimation Duration="0" Storyboard.TargetName="SelectedHighlight" Storyboard.TargetProperty="Opacity" To="0"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.ExitActions>
                        <!--<Setter Property="DetailsVisibility" Value="Visible" />-->
                    </Trigger>
                    <MultiTrigger >
                        <MultiTrigger.Conditions>
                            <Condition Property="IsMouseOver" Value="True" />
                            <Condition Property="IsSelected" Value="False" />
                        </MultiTrigger.Conditions>
                        <MultiTrigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation Duration="0" Storyboard.TargetName="Over" Storyboard.TargetProperty="Opacity" To="0.73"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </MultiTrigger.EnterActions>
                        <MultiTrigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation Duration="0" Storyboard.TargetName="Over" Storyboard.TargetProperty="Opacity" To="0"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </MultiTrigger.ExitActions>
                    </MultiTrigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsSelected" Value="True" />
                            <Condition Property="IsFocused" Value="False" />
                        </MultiTrigger.Conditions>
                        <MultiTrigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation Duration="0" Storyboard.TargetName="Selected" Storyboard.TargetProperty="Opacity" To="0.84"/>
                                    <DoubleAnimation Duration="0" Storyboard.TargetName="SelectedHighlight" Storyboard.TargetProperty="Opacity" To="1"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </MultiTrigger.EnterActions>
                        <MultiTrigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation Duration="0" Storyboard.TargetName="Selected" Storyboard.TargetProperty="Opacity" To="0"/>
                                    <DoubleAnimation Duration="0" Storyboard.TargetName="SelectedHighlight" Storyboard.TargetProperty="Opacity" To="0"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </MultiTrigger.ExitActions>
                    </MultiTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
0
votes

I found a great solution for this on MSDN using attached properties:

<DataGrid ItemsSource="{Binding}" IsReadOnly="True" SelectionUnit="Cell">
  <DataGrid.CellStyle>
    <Style TargetType="{x:Type DataGridCell}">
      <Style.Triggers>
        <Trigger Property="IsSelected" Value="True">
          <Setter Property="local:DataGridAttachedProperties.IsCellSelected" Value="True"/>
        </Trigger>
        <Trigger Property="IsSelected" Value="False">
          <Setter Property="local:DataGridAttachedProperties.IsCellSelected" Value="False"/>
        </Trigger>
      </Style.Triggers>
    </Style>
  </DataGrid.CellStyle>
  <DataGrid.ItemContainerStyle>
    <Style TargetType="{x:Type DataGridRow}">
      <Style.Triggers>
        <Trigger Property="local:DataGridAttachedProperties.IsCellSelected" Value="True">
          <Setter Property="BorderThickness" Value="2"/>
          <Setter Property="BorderBrush" Value="Red"/>
          <Setter Property="Background" Value="Yellow"/>
          <Setter Property="Opacity" Value="0.7"/>
        </Trigger>
      </Style.Triggers>
    </Style>
  </DataGrid.ItemContainerStyle>
</DataGrid>

And the C#:

public class DataGridAttachedProperties
{
    public static bool GetIsCellSelected(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsCellSelectedProperty);
    }
    public static void SetIsCellSelected(DependencyObject obj, bool value)
    {
        obj.SetValue(IsCellSelectedProperty, value);
    }
    public static readonly DependencyProperty IsCellSelectedProperty =
        DependencyProperty.RegisterAttached("IsCellSelected", typeof(bool), typeof(DataGridAttachedProperties), new UIPropertyMetadata(false,
        (o, e) =>
        {
            if (o is DataGridCell)
            {
                DataGridRow row = VisualTreeHelperEx.FindVisualParent<DataGridRow>(o as DataGridCell);
                row.SetValue(DataGridAttachedProperties.IsCellSelectedProperty, e.NewValue);
            }
        }));
}
public class VisualTreeHelperEx
{
    public static T FindVisualParent<T>(DependencyObject child)
    where T : DependencyObject
    {
        DependencyObject parentObject = VisualTreeHelper.GetParent(child);
        if (parentObject == null) return null;
        T parent = parentObject as T;
        if (parent != null)
        {
            return parent;
        }
        else
        {
            return FindVisualParent<T>(parentObject);
        }
    }
}