eye_am_groot
eye_am_groot

Reputation: 682

Prevent Two Comboboxes from Selecting the Same Item

I am looking for a way to prevent two (or more) ComboBoxes from having the same SelectedItem. I have multiple ComboBoxes that all have the same ItemsSource, each bound to a separate property in the view model.

<ComboBox Width="50" ItemsSource="{Binding Keys}" SelectedItem="{Binding LoadedBackgroundKey, NotifyOnValidationError=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<ComboBox Width="50" ItemsSource="{Binding Keys}" SelectedItem="{Binding LoadedForegroundKey, NotifyOnValidationError=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<ComboBox Width="50" ItemsSource="{Binding Keys}" SelectedItem="{Binding IncreaseSizeKey, NotifyOnValidationError=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

etc. In the ViewModel:

public Key LoadedBackgroundKey { get; set; }
public Key LoadedForegroundKey { get; set; }
public Key IncreaseSizeKey { get; set; }
private ObservableCollection<Key> _availableKeys;
public IEnumerable<Key> Keys => _availableKeys;

When the view model is loaded, _availableKeys gets populated with the appropriate keys (A, B, etc.). I want to be able to prevent the selection of the same key in multiple comboboxes or (at the very least) give an error when the same key is selected in multiple boxes.

This question may be similar to what I am looking for, but I am not sure it quite works (maybe I just don't understand it properly). It also only accounts for the case of two ComboBoxes. While I only displayed 3, I want this to be scalable over multiple Comboboxes. Is a ValidationRule the best approach? If so, how would I implement this, checking over all of the ComboBoxes? Or is there an easier approach?

Upvotes: 0

Views: 89

Answers (1)

mm8
mm8

Reputation: 169200

You could implement the INotifyDataErrorInfo interface in your view model and perform your validation logic whenever any of the involved properties are set, e.g.:

public class ViewModel : INotifyDataErrorInfo
{
    public Key LoadedBackgroundKey
    {
        get => keys[0];
        set
        {
            Validate(nameof(LoadedBackgroundKey), value);
            keys[0] = value;
        }
    }

    public Key LoadedForegroundKey
    {
        get => keys[1];
        set
        {
            Validate(nameof(LoadedForegroundKey), value);
            keys[1] = value;
        }
    }

    public Key IncreaseSizeKey
    {
        get => keys[2];
        set
        {
            Validate(nameof(IncreaseSizeKey), value);
            keys[2] = value;
        }
    }

    public IEnumerable<Key> Keys { get; } = new ObservableCollection<Key> { ... };

    private void Validate(string propertyName, Key value)
    {
        if (keys.Contains(value))
            _validationErrors[propertyName] = "duplicate...";
        else
            _validationErrors.Remove(propertyName);

        ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
    }

    private readonly Key[] keys = new Key[3];
    private readonly Dictionary<string, string> _validationErrors = new Dictionary<string, string>();
    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
    public bool HasErrors => _validationErrors.Count > 0;
    public IEnumerable GetErrors(string propertyName) =>
        _validationErrors.TryGetValue(propertyName, out string error) ? new string[1] { error } : null;
}

Upvotes: 2

Related Questions