2
votes

I have the SelectedIndex property of my ComboBox binded to a property in my ViewModel. Everytime a user selects an item from the ComboBox, nothing should appear in the SelectedItem area (The SelectedIndex should remain -1). To do this, I have tried the following:

cs

private int selectedIndexDoor;

    public int SelectedIndexDoor
    {
        get { return selectedIndexDoor; }
        set
        {
            selectedIndexDoor = -1;
            OnPropertyChanged("SelectedIndexDoor");
        }
    }

xaml

                             <ComboBox Width="150"

                              materialDesign:HintAssist.Hint="Door"
                              ItemsSource="{Binding Doors}"
                              Margin="0, 0, 50, 0"
                              SelectedIndex="{Binding SelectedIndexDoor, 
                             UpdateSourceTrigger=PropertyChanged}">

                            </ComboBox>

However it doesn't work. The value I manually set the index to is ignored. Why is this?

1
When the binding updates the source property (SelectedIndexDoor), it doesn't then read the value back and update the control. The "accepted" way of doing that is to start another thread that updates the vm property after the binding finishes updating it. If you want a viewmodel property to reject a change coming from the binding, have it throw an exception. The UI will treat that as a validation error -- probably not what you want. I think you may want to handle SelectedIndexChanged, and set SelectedItem=null; in the handler, or something. How are you using the selection from the combobox? - 15ee8f99-57ff-4f92-890c-b56153
You could also write an ItemTemplate for the combobox which displays content only when it has an ancestor that's a ListBoxItem. When applied to the selected item outside of the dropdown, it won't be visible. - 15ee8f99-57ff-4f92-890c-b56153
@DavidAndrewThorpe Code in .xaml.cs is legitimate if it's logic that belongs to the view: Any code that does stuff to controls belongs there, for example, drag/drop, etc. We like to put that kind of thing in behaviors when possible, but it isn't always possible or practical. I've used mm8's solution in production code, but I don't like it very much because you're solving a view problem in the viewmodel, which should not know the view even exists. That said, this isn't religion, it's software development. - 15ee8f99-57ff-4f92-890c-b56153
@DavidAndrewThorpe A viewmodel should be self-contained and agnostic about how it's displayed. Maybe this viewmodel is displayed via multiple views, one of which will need to use SelectedIndex differently. SelectedIndexDoor should be the selected door index. It's true that you can be reasonably confident you won't end up doing that in this case, but it's a very good way to think about designing the two different types of classes: Couple them as loosely as possible. If somebody comes along and writes a new viewmodel for your view or vice versa, make his life maximally easy. - 15ee8f99-57ff-4f92-890c-b56153
Thanks @EdPlunkett. I really appreciate you explaining that to me :) Yes there are some things on my xaml.cs which I hadn't been able to figure out how to get on the view model such as some drag/drop methods and it was really annoying me! But okay, you say it's reasonable to have some code on there so I will keep that in mind - David Andrew Thorpe

1 Answers

0
votes

The value returned from the getter of the source property is overruled by some internal logic that executes when an item is selected.

You could workaround this by setting the value in another dispatcher cycle:

public int SelectedIndexDoor
{
    get { return selectedIndexDoor; }
    set
    {
        selectedIndexDoor = value;
        Application.Current.Dispatcher.BeginInvoke(new Action(() =>
        {
            selectedIndexDoor = -1;
            OnPropertyChanged("SelectedIndexDoor");
        }));
    }
}