Search code examples
c#wpfdata-binding

WPF DataGrid IValueConverter only fires when selected item changes, not when bound value changed


I've set up a DataGrid with an 'Action' column (combobox). I need to make sure that two of my other columns have their backgrounds and read/write ability changed based on which action is selected. I created two converters to do this, which take in the entire DataGridRow as context since I also need to be able to call the ParentStatus property from the converter (if I only took in the current value I could not access ParentStatus). Here are relevant parts of the XAML:

<DataGrid Grid.Row="1" Grid.Column="0" ItemsSource="{Binding SelectedWcn.Lines}" Style="{StaticResource DataGridStyle}" AutoGenerateColumns="False" IsReadOnly="{Binding IsGridReadOnly}" Margin="2">
<DataGrid.Columns>
    <DataGridTextColumn Header="Part ID" Binding="{Binding PartID}" />
    <DataGridTemplateColumn Header="Action">
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <ComboBox SelectedValue="{Binding Action, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" 
                            ItemsSource="{Binding DataContext.ActionOptions, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
                            DisplayMemberPath="Value"
                            SelectedValuePath="Value"
                            IsEnabled="{Binding Path=. , Converter={StaticResource StatToEnabledConv}, ConverterParameter='Action'}"/>
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>
    <DataGridTemplateColumn Header="QTY Change">
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <TextBox Text="{Binding QTYChange, UpdateSourceTrigger=PropertyChanged}"
                            IsReadOnly="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}, Converter={StaticResource ActToReadOnlyConv}, ConverterParameter=QTY_CHANGE}"
                            Background="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}, Converter={StaticResource ActToBackConv}, ConverterParameter=QTY_CHANGE}"/>
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>
    <DataGridTemplateColumn Header="Replacement PN">
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <TextBox Text="{Binding ReplacementPN, UpdateSourceTrigger=PropertyChanged}"
                            IsReadOnly="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}, Converter={StaticResource ActToReadOnlyConv}, ConverterParameter=ALTERNATE_PART_ID}"
                            Background="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}, Converter={StaticResource ActToBackConv}, ConverterParameter=ALTERNATE_PART_ID}"/>
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>

And here is my model which implements NotifyPropertyChanged:

 public class WCN_Line : ObservableObject
{
    private int? _rowID;
    private string __partID;
    private string _action;
    private int? _qtyChange;
    private string _disposition;
    private string? _replacementPN;
    private string _reason;
    private string _note;
    private string? _parentStatus;

    public int WCN_ID { get; set; }
    // ... Other fields

    public string Action
    {
        get => _action;
        set
        {
            if (_action != value)
            {
                _action = value;
                OnPropertyChanged();
                ClearFieldsBasedOnAction();
            }
        }
    }

The converter itself works flawlessly, but I can no longer get it to fire when Action is changed. Instead of firing when a value is changed, it only fires when the SelectedWcn changes. I have debugged to confirm that a) my property is updating and notifying changes and b) the converter is not firing at all on Action change (as opposed to firing, but failing to do anything).

Edit- I should note that SelectedWcn is chosen from a collection: ObservableCollection Wcns. WcnInReview has a WCN_Lines collection simply called Lines, which is what the grid binds to.

I know it was working yesterday and so I must have made some change that I don't remember, but I have analyzed this all morning and cannot find the issue.


Solution

  • The converter will only fire when a PropertyChanged event is raised for the data-bound property. Since you bind to DataContext, it won't fire when you set the Action property`.

    You can solve this by using an IMultiValueConverter and bind to both DataContext and Action:

    <TextBox Text="{Binding QTYChange, UpdateSourceTrigger=PropertyChanged}">
        <TextBox.IsReadOnly>
            <MultiBinding Converter="{StaticResource ActToReadOnlyConv}" ConverterParameter="QTY_CHANGE">
                <Binding Path="DataContext" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}" />
                <Binding Path="Action" />
            </MultiBinding>
        </TextBox.IsReadOnly>
    </TextBox>
    

    Converter:

    public class ActionToReadOnlyConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            WCN_Line line = values[0] as WCN_Line;
            ...
            }
    
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }