Binding WPF Datagrid's row color to a property of an item inside an observable collection

1.2k views Asked by At

I am hoping someone can help me with this. I have an MVVMLight application in which I have an observable collection of objects, in my view-model, bound to the WPF DataGrid's ItemsSource property. Now I want to change the row background color whenever a specific value called "TaskAssignmentStatus" changes for any item inside the observable collection.

However I am getting a XAML Parse error as follows:

{"Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead."}

I have read everything I can find regarding that error but I haven't been able to see how to apply anything I have learned from the answers given thus far to my specific situation. I am very much a new to WPF and databiding so any help would be very much appreciated. Thanks!

The relevant code from my view-model is as follows:

//Contains the actual tasks on the currently selected team member's    
//work-list
public ObservableCollection<WorklistTask> TasksForSelectedTeamMember
{
    get { return _tasksForSelectedTeamMember; }
    set
    {
        if (value != null)
        {
            _tasksForSelectedTeamMember = value;
            RaisePropertyChanged("TasksForSelectedTeamMember");
        }
    }
}

Here is the class definition for the types inside my observable collection in the view-model:

public class WorklistTask : ICloneable
{
    #region Fields

    private int _taskId;
    private int _activityId;
    private string _activityName;
    private string _activityStatus;
    private string _taskAssignDate;
    private string _activityStartDate;
    private string _activityDueDate;
    private string _caseStartDate;
    private string _caseDueDate;
    private int _caseId;
    private string _caseType;

    //an enum which indicates whether or not the task has been reassigned 
    private WorkListTaskAssignmentStatus _taskAssignmentStatus;

    private int _sourceTaskId;
    private bool _selectedByUserInUI = false;


    #endregion

    #region Constructors

    public WorklistTask() { }

    #endregion

    #region Properties

    [DisplayName("Task Assignment Status")]
    public WorkListTaskAssignmentStatus TaskAssignmentStatus
    {
        get { return _taskAssignmentStatus; }
        set { _taskAssignmentStatus = value; }
    }


    public int TaskId
    {
        get { return _taskId; }
        set { _taskId = value; }
    }


    public int ActivityId
    {
        get { return _activityId; }
        set { _activityId = value; }
    }

    [DisplayName("Activity Name")]
    public string ActivityName
    {
        get { return _activityName; }
        set { _activityName = value; }
    }
    [DisplayName("Activity Status")]
    public string ActivityStatus
    {
        get { return _activityStatus; }
        set { _activityStatus = value; }
    }
    [DisplayName("Task Assigned Date")]
    public string TaskAssignDate
    {
        get { return _taskAssignDate; }
        set { _taskAssignDate = value; }
    }
    [DisplayName("Activity Start Date")]
    public string ActivityStartDate
    {
        get { return _activityStartDate; }
        set { _activityStartDate = value; }
    }
    [DisplayName("Activity Due Date")]
    public string ActivityDueDate
    {
        get { return _activityDueDate; }
        set { _activityDueDate = value; }
    }
    [DisplayName("Case Start Date")]
    public string CaseStartDate
    {
        get { return _caseStartDate; }
        set { _caseStartDate = value; }
    }
    [DisplayName("Case Due Date")]
    public string CaseDueDate
    {
        get { return _caseDueDate; }
        set { _caseDueDate = value; }
    }

    [DisplayName("Case Id")]
    public int CaseId
    {
        get { return _caseId; }
        set { _caseId = value; }
    }
    [DisplayName("Case Type")]
    public string CaseType
    {
        get { return _caseType; }
        set { _caseType = value; }
    }


    public int SourceTaskId
    {
        get { return _sourceTaskId; }
        set { _sourceTaskId = value; }
    }

    public bool SelectedByUserInUI
    {
        get { return _selectedByUserInUI; }
        set { _selectedByUserInUI = value; }
    }

    #endregion

    #region Methods

    public object Clone()
    {
        WorklistTask newWorklistTask = (WorklistTask)this.MemberwiseClone();
        newWorklistTask.ActivityName = string.Copy(ActivityName);
        newWorklistTask.ActivityStatus = string.Copy(ActivityStatus);
        newWorklistTask.TaskAssignDate = string.Copy(TaskAssignDate);
        newWorklistTask.ActivityStartDate = string.Copy(ActivityStartDate);
        newWorklistTask.ActivityDueDate = string.Copy(ActivityDueDate);
        newWorklistTask.CaseStartDate = string.Copy(CaseStartDate);
        newWorklistTask.CaseDueDate = string.Copy(CaseDueDate);
        newWorklistTask.CaseType = string.Copy(CaseType);

        return newWorklistTask;
    }

    #endregion
}

Here is the enum:

public enum WorkListTaskAssignmentStatus
{
    Automatic,
    Added,
    Removed,
    DeleteLocal
}

Here is the XAML for the datagrid I am trying to format:

<DataGrid x:Name="WorklistOfSelectedTeamMember"
          ItemsSource="{Binding TasksForSelectedTeamMember, Mode=TwoWay,   
          UpdateSourceTrigger=PropertyChanged}"                 
          AutoGeneratingColumn="GenerateColumnsForWorklist"                                   
          IsReadOnly="True"
          FontWeight="Normal"
          FontSize="13" 
          SelectionMode="Extended"
          AlternatingRowBackground="LightGray"
          Grid.Column="0"
          Grid.ColumnSpan="2"
          Grid.Row="3" 
          Margin="0,0,5,0"
          HorizontalScrollBarVisibility="Auto"
          Height="150"
          Style="{StaticResource DataGridColoring}">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectionChanged">
                <cmd:EventToCommand Command="{Binding 
                WorklistTaskSelectionChangedCommand}" 
                CommandParameter="{Binding SelectedItems, 
                ElementName=WorklistOfSelectedTeamMember}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
        <Style TargetType="DataGridRow">
            <Style.Triggers>
                <DataTrigger Binding="{Binding TaskAssignmentStatus}" 
                 Value="Automatic">  
                    <Setter Property="Background" Value="Transparent"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding TaskAssignmentStatus}" 
                 Value="Removed">
                    <Setter Property="Background" Value="#C4FF0000"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding TaskAssignmentStatus}" 
                 Value="Added">
                     <Setter Property="Background" Value="#00FF7F"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding TaskAssignmentStatus}" 
                 Value="DeleteLocal">
                     <Setter Property="Background" Value="#00FFFFFF"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>                            
</DataGrid>
2

There are 2 answers

1
mm8 On BEST ANSWER

You can get rid of the error you are getting by adding the implicit RowStyle to the Resources dictionary of the DataGrid:

<DataGrid x:Name="WorklistOfSelectedTeamMember"
          ItemsSource="{Binding TasksForSelectedTeamMember, Mode=TwoWay,   
          UpdateSourceTrigger=PropertyChanged}"                                                   
          IsReadOnly="True"
          FontWeight="Normal"
          FontSize="13" 
          SelectionMode="Extended"
          AlternatingRowBackground="LightGray"
          Grid.Column="0"
          Grid.ColumnSpan="2"
          Grid.Row="3" 
          Margin="0,0,5,0"
          HorizontalScrollBarVisibility="Auto"
          Height="150">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <cmd:EventToCommand Command="{Binding 
                WorklistTaskSelectionChangedCommand}" 
                CommandParameter="{Binding SelectedItems, 
                ElementName=WorklistOfSelectedTeamMember}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <DataGrid.Resources>
        <Style TargetType="DataGridRow">
            <Style.Triggers>
                <DataTrigger Binding="{Binding TaskAssignmentStatus}" 
                 Value="Automatic">
                    <Setter Property="Background" Value="Transparent"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding TaskAssignmentStatus}" 
                 Value="Removed">
                    <Setter Property="Background" Value="#C4FF0000"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding TaskAssignmentStatus}" 
                 Value="Added">
                    <Setter Property="Background" Value="#00FF7F"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding TaskAssignmentStatus}" 
                 Value="DeleteLocal">
                    <Setter Property="Background" Value="#00FFFFFF"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGrid.Resources>
</DataGrid>

Also note that if you want the background colour of the row to change when you set the value of the TaskAssignmentStatus property of an item in your ObservableCollection to a new value dynamically at runtime, the WorklistTask class should implement the INotifyPropertyChanged interface and raise the PropertyChanged event in the setter of the TaskAssignmentStatus property.

1
Ponciano On

Your code to change background color seems just fine and it should work.

The error string you mention isn't about that, it means that you're trying somehow to add/remove elements directly from the ItemsSource property of the DataGrid DataGrid.ItemsSource

You can't use ItemsSource to directly modify your elements you must use the collection instead.