0
votes

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;
    }
1
Please show us your viewmodel that your DataContext is set to for the combo box. I am looking for the code for the T class in your public ObserveableCollection<T> BlockSequenceFields { get; }Scott Chamberlain
Maybe you forgot to add INotifyPropertyChanged interface to your viewModel ?Demon
@ScottChamberlain I'm fairly certain that the datacontext is set correctly as part of the combobox updates. However, I will explicitly set the datacontext and see if it works. I don't have a code behind file to set the datacontext, so I'm going to do it in the xaml.user2619824
@Demon, yep, my VM implements INotifyPropertyChanged.user2619824
@user2619824 i had the same consern as Daemon, your VM may implment INotifyPropertyChanged, but does the line items in the collection BlockSequenceFields also implment INotifiyPropertyChanged? Can you show your VM, and the class for whatever the items in the collection are? I am not conserned about the datacontext itself, i am more conserned with the data types in the view model. More specificly, I want to see the code for the class that contains the IsCalibrated property.Scott Chamberlain

1 Answers

1
votes

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.