user2619824
user2619824

Reputation: 488

WPF combo box does not update currently selected item

I'm having an issue with my combo box. Somehow it can get out of sync with itself. For example, after I change out my BlockSequenceFields, only the dropdown text gets altered. Below, the Field 1 has been updated but you can see that it doesn't reflect in the currently selected item.

enter image description here

My IsSynchronizedWithCurrentItem=true should make the currently selected item behave as expected but it doesn't seem to work. I've read many stackoverflow posts where the current item doesn't match but they just set IsSynchronizedWithCurrentItem to true and it fixes their issue.

Can anyone explain why this isn't working for me?

    <ComboBox x:Name="SequenceFieldComboBox" 
              SelectedItem="{Binding BlockSequenceFieldIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
              ItemsSource="{Binding BlockSequenceFields, UpdateSourceTrigger=PropertyChanged}"   
              IsSynchronizedWithCurrentItem="True">

        <ComboBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <CheckBox
                        IsChecked="{Binding IsCalibrated, Mode=OneWay}"
                        IsEnabled="False">
                    </CheckBox>
                    <TextBlock 
                        Text="{Binding}">
                    </TextBlock>
                </StackPanel>
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>

EDIT: Further details for Mr. Chamberlain

// ViewModelBase implements INotifyPropertyChanged
public class BlockFieldViewModel : ViewModelBase
{
    public BlockSequenceField SequenceField { get; set; }

    public List<BlockSequenceCalibrationItemViewModel> Calibrations => this.SequenceField?.CalibrationList;

    public bool IsCalibrated => this.Calibrations.TrueForAll(x => x.IsCalibrated == null || x.IsCalibrated == true);

    public double AmplitudeThreshold => this.Calibrations.Max(x => x.Amplitude);

    public int FieldNumber { get; set; }

    public override string ToString()
    {
        string ret = string.Format(CultureInfo.CurrentCulture, "Field {0} ", this.FieldNumber);

        if (Math.Abs(this.AmplitudeThreshold) > .00001)
        {
            ret = string.Concat(ret, string.Format(CultureInfo.CurrentCulture, "({0} mA)", this.AmplitudeThreshold));
        }

        return ret;
    }
}

And here is the larger viewmodel, call it MainViewModel.cs. Here are the relevant fields in the class

    private ObservableCollection<BlockFieldViewModel> blockSequenceFields;

    public ObservableCollection<BlockFieldViewModel> BlockSequenceFields
    {
        get => this.blockSequenceFields;
        set
        {
            this.blockSequenceFields = value;
            this.OnPropertyChanged("BlockSequenceFields");
        }
    }

    private void RefreshFieldList()
    {
        // In order for the combo box text to update, we need to reload the items
        var savedIndex = this.BlockSequenceFieldIndex;  // to restore to current field.
        var fieldList = this.CalibrationViewModel.FieldViewModels;
        this.BlockSequenceFields = new ObservableCollection<BlockFieldViewModel>(fieldList); 
        this.BlockSequenceFieldIndex = savedIndex;
    }

Upvotes: 0

Views: 1668

Answers (1)

Scott Chamberlain
Scott Chamberlain

Reputation: 127603

Your problem is caused because BlockFieldViewModel does not raise INPC when FieldNumber is updated. You need to raise it for that property at the minimum.

//Assuming the base class looks like
public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}


public class BlockFieldViewModel : ViewModelBase
{
   //...

    public int FieldNumber 
    { 
        get
        {
            return _fieldNumber;
        }
        set
        {
            if(_fieldNumber.Equals(value))
                return;

            OnPropertyChanged();
        }
    }
    //...
}

I don't know for sure if this will solve your problem or not, due to the fact that you are using .ToString() to display the name. If you find the above does not fix it trigger a property changed for the entire object by passing a empty string in to your OnPropertyChanged method

public int FieldNumber 
{ 
    get
    {
        return _fieldNumber;
    }
    set
    {
        if(_fieldNumber.Equals(value))
            return;

        //Refresh all properties due to the .ToString() not updating.
        OnPropertyChanged("");
    }
}

Also, if List<BlockSequenceCalibrationItemViewModel> Calibrations can be added to or removed from, or .Amplitude could be changed you need to trigger a refresh of the name from that too.

Upvotes: 1

Related Questions