0
votes

I have a DataGrid where I'd like to change the row border color or background if a certain property of my ViewModel class is set to true. I'm having problems getting the DataTrigger to bind to the row source item (ViewModel) property. In this case, I'd like to bind to the ViewModel Property IsExpired. Unfortunately, it won't let me and throws and exception at runtime. Any help would be appreciated.

public class ViewModel 
{
    public uint Id { get; private set;}
    public string Symbol { get; private set;}
    public bool IsExpired { get; private set;}
}

public class WindowViewModel : PropertyChangedNotifier
{
    public ObservableCollection<ViewModel> ViewModels { get; private set;}

    private DateTime _timestamp;

    public DateTime TimeStamp
    {
        get { return _timestamp; }
        set 
        {
            _timestamp = value;
            OnPropertyChanged("TimeStamp");
        }
    }
}

<Grid x:Name="Layout" d:DataContext="{StaticResource DesignerViewModel}" DataContext="{Binding}">
    <Grid.RowDefinitions>
        <RowDefinition Height="30"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <StackPanel Grid.Row="0" Orientation="Horizontal">
        <TextBlock Text="Accounts" VerticalAlignment="Center" Margin="5"/>
        <ComboBox x:Name="AccountsComboBox" ItemsSource="{Binding Accounts}"  DisplayMemberPath="ClearingNumber" Width="125" 
                  HorizontalAlignment="Left" Margin="5" IsEditable="False" IsReadOnly="True" Style="{StaticResource AccountComboBoxStyle}"/>
        <Button x:Name="LoadPositionsButton" Content="Load" Margin="5" VerticalAlignment="Center" HorizontalAlignment="Center" Width="50" Click="LoadPositionsButtonOnClick"/>
        <TextBlock x:Name="TimeStampTextBlock" HorizontalAlignment="Right" Margin="5" Width="150" Text="{Binding LastUpdate, Converter={StaticResource TimeStampConverter}}"/>
    </StackPanel>
    <DataGrid Grid.Row="1" ItemsSource="{Binding ViewModels}" CanUserAddRows="False" CanUserDeleteRows="False" CanUserReorderColumns="False" CanUserResizeColumns="False"
              CanUserResizeRows="False" CanUserSortColumns="False" AutoGenerateColumns="False" HeadersVisibility="Column">
        <DataGrid.RowStyle>
            <Style TargetType="{x:Type DataGridRow}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsExpired}" Value="True">
                        <Setter Property="BorderBrush" Value="Red"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </DataGrid.RowStyle>    
        <!-- ... -->
        <DataGrid.Columns>
            <DataGridTemplateColumn Header="Account">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate DataType="vm:ViewModel">
                        <TextBlock Text="{Binding Account}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <!--<DataGridTemplateColumn Header="Exch">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate DataType="vm:ViewModel">
                        <TextBlock Text="{Binding ExchangeCode}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>-->
            <DataGridTemplateColumn Header="Symbol">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate DataType="vm:ViewModel">
                        <TextBlock Text="{Binding Symbol}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTemplateColumn Header="Description">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate DataType="vm:ViewModel">
                        <TextBlock Text="{Binding Description}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTemplateColumn Header="Maturity">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate DataType="vm:ViewModel">
                        <TextBlock Text="{Binding MaturityMonthYear}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
       <DataGrid.Columns>
    </DataGrid>
</Grid>
1

1 Answers

1
votes

First you should implement INotifyPropertyChanged interface for ViewModel class. This is similar to what how you wrote in your WindowViewModel class. And then change the auto-property IsExpired to:

public bool IsExpired
{
    get { return _isExpired; }
    set
    {
        _isExpired = value;
        OnPropertyChanged("IsExpired");
    }
}

Second if you want to change style of certain column you should change the CellStyle property of column:

<DataGrid Grid.Row="1" ItemsSource="{Binding ViewModels}" CanUserAddRows="False" CanUserDeleteRows="False" CanUserReorderColumns="False" CanUserResizeColumns="False"
  CanUserResizeRows="False" CanUserSortColumns="False" AutoGenerateColumns="False" HeadersVisibility="Column">
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="HeaderName" >
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Symbol}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellStyle>
                <Style TargetType="{x:Type DataGridCell}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding IsExpired}" Value="True">
                            <Setter Property="BorderBrush" Value="Red"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </DataGridTemplateColumn.CellStyle>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
    <!-- ... -->
</DataGrid>

If you want to change style of data grid rows you should use the Template property, for example:

<DataGrid Grid.Row="1" ItemsSource="{Binding ViewModels}" CanUserAddRows="False" CanUserDeleteRows="False" CanUserReorderColumns="False" CanUserResizeColumns="False"
    CanUserResizeRows="False" CanUserSortColumns="False" AutoGenerateColumns="False" HeadersVisibility="Column">
    <DataGrid.RowStyle>
        <Style TargetType="{x:Type DataGridRow}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type DataGridRow}">
                        <Border x:Name="DataGridRowBorder"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            SnapsToDevicePixels="True">
                            <!-- Row template -->
                            <Grid>
                                <TextBlock Text="{Binding Path=Symbol}" />
                            </Grid>
                        </Border>
                        <ControlTemplate.Triggers>
                            <DataTrigger Binding="{Binding IsExpired}" Value="True">
                                <Setter TargetName="DataGridRowBorder" Property="BorderBrush" Value="Red"/>
                                <Setter TargetName="DataGridRowBorder" Property="BorderThickness" Value="1"/>
                            </DataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </DataGrid.RowStyle>
    <!-- ... -->
</DataGrid>