auticus
auticus

Reputation: 183

Setting SelectedItem of Listbox or ComboBox through ViewModel MVVM WPF

Objective: having bound the SelectedItem of a ListBox (or ComboBox) to an instance of an object through xaml, I would like to set the selected instance of the object through the view model and have it reflect on the ListBox or ComboBox.

<ComboBox x:Name="cboServers" HorizontalAlignment="Left" Margin="535,694,0,0" VerticalAlignment="Top" Width="225"
          ItemsSource="{Binding Settings.Servers}"
          SelectedItem="{Binding Settings.SelectedServer, Mode=TwoWay}"
          DisplayMemberPath="UserFriendlyName">

C# Model View code

public ObservableCollection<AutoSyncServer> Servers { get; set; }

private AutoSyncServer _selectedServer;

public AutoSyncServer SelectedServer
{
    get { return _selectedServer;}
    set
    {
        _selectedServer = value;
        OnPropertyChanged("SelectedServer");
    }
}

The list or combo box populates correctly. Selecting an item on the ListBox or ComboBox will correctly set the SelectedServer object.

However, if I try to write a set statement in C# such as:

Servers.Add(newServer);
SelectedServer = newServer;

The ListBox or ComboBox will correctly add the item and the SelectedServer object will be correctly set on the MVVM model, but the front end will not reflect this selection.

In this specific case, an xml file is read saying what the user had selected last, and when the window opens the ComboBox has nothing selected (the servers are all loaded correctly within it though)

What's missing here?

Upvotes: 0

Views: 467

Answers (2)

The actual object in SelectedItem must be an object instance which is found in the Servers collection, in the Object.ReferenceEquals(a, b) sense. Not just the same Name and ID (or whatever) properties; the same exact class instance.

The classic case where people run afoul of this is deserializing equivalent items in multiple places. Servers has a collection of deserialized AutoSyncServer instances, and Settings.SelectedServer is a separately deserialized AutoSyncServer instance, which has identical property values to one of the items in Servers. But it's still a different object, and the ComboBox has no way of knowing that you intend otherwise.

You could override AutoSyncServer.Equals() to return true if the two instances of AutoSyncServer are logically equivalent. I don't like doing that because it changes the semantics of the = operator for that class, which has bitten me before. But it's an option.

Another option is to have one canonical static collection of AutoSyncServer and make sure every class gets its instances from that.


I don't understand why this code didn't work, given the above:

Servers.Add(newServer);
SelectedServer = newServer;

Once newServer is in Servers, it should be selectable. I tested that and it's working for me as you would expect.

Upvotes: 2

GCamel
GCamel

Reputation: 622

i think you must avoid "sub-bindings", they work once when the view ask for, but not well after Settings.SelectedServer ==> SelectedServer

and if you comment OnServerChanged?.Invoke(this, _selectedServer); what is happening ? it works ?

Upvotes: 0

Related Questions