Home > Mobile >  Binding ObservableDictionary to ComboBox (WPF)
Binding ObservableDictionary to ComboBox (WPF)

Time:10-02

I would have thought this question would have been answered already, but I am not finding it. I have a simple class:

public class BinanceEndpoint : ObservableObject
    {
        private string baseAddress;
        private string socketBaseAddress;

        public string BaseAddress
        {
            get { return baseAddress; }
            set { baseAddress = value; RaisePropertyChangedEvent(nameof(BaseAddress)); }
        }

        public string SocketBaseAddress
        {
            get { return socketBaseAddress; }
            set { socketBaseAddress = value; RaisePropertyChangedEvent(nameof(SocketBaseAddress)); }
        }
    }

I am then populating an ObservableDictionary with objects from that class:

    private MainViewModel()
    {
 private BinanceEndpoint apiEndPoint;
        private ObservableDictionary<string, BinanceEndpoint> endPoints = new ObservableDictionary<string, BinanceEndpoint>();
        
        endPoints.Add("Binance.com", new BinanceEndpoint() { BaseAddress = "https://api.binance.com", SocketBaseAddress = "wss://stream.binance.com:9443", });
                    endPoints.Add("Binance.us", new BinanceEndpoint() { BaseAddress = 
    
        "https://api.binance.us", SocketBaseAddress = "wss://stream.binance.us:9443", });
                        endPoints.Add("Testnet", new BinanceEndpoint() { BaseAddress = "https://testnet.binance.vision", SocketBaseAddress = "wss://testnet.binance.vision", });
        }
        [JsonIgnore]
                public ObservableDictionary<string,BinanceEndpoint> EndPoints
                {
                    get { return endPoints; }
                    set { endPoints = value; RaisePropertyChangedEvent(nameof(EndPoints)); }
                }

public BinanceEndpoint APIEndPoint
        {
            get { return apiEndPoint; }
            set { apiEndPoint = value; RaisePropertyChangedEvent(nameof(APIEndPoint)); }
        }
    }

Then I am trying to populate a ComboBox from using the ObservableDictionary.

<ComboBox Grid.Column="1" ItemsSource="{Binding EndPoints}" DisplayMemberPath="Key" SelectedValuePath="Value" SelectedValue="{Binding Path=APIEndPoint, Mode=TwoWay}"/>

The issue I am having is that the SelectedValue does not update the value of the ComboBox when it is loaded. What am I doing wrong?

CodePudding user response:

You should avoid binding to a Dictionary in the first place. There is a reason that there is no ObservableDictionary in the .NET library - since the year of release in 2002. I guess we agree that this is not because MS developers didn't know how to implement such a dictionary after all these years.

Your are using SelectedValuePath and SelectedValue wrong. SelectedValue returns the value of the property that SelectedValuePath is pointing to. SelectedValuePath provides a property path on the SelectedItem.
When you actively set the SelectedValue, then the control will try to find and select the item where the item's property specified by SelectedValuePath matches the value of SelectedValue.

When SelectedItem returns the selected item (instance), then SelectedValue returns the value of a property on this SelectedItem that is specified using SelectedValuePath.

For example: we bind to a Dictionary<string, BinanceEndpoint>. To display the Key of your Dictionary we specify the DisplayMemberPath (as an alternative to a DataTemplate).
The SelectedItem will hold KeyValuePair<string, BinanceEndpoint>.
To have the ComboBox.SelectedValue return the selected item's SocketBaseAddress value, we must set the SelectedValuePath to "Value.SocketBaseAddress":

<ComboBox x:Name="ComboBox" 
          ItemsSource="{Binding EndPoints}" 
          DisplayMemberPath="Key" 
          SelectedValuePath="Value.SocketBaseAddress" />

<!-- Display the selected item's SocketBaseAddress value -->
<TextBlock text="{Binding ElementName=ComboBox, Path=SelectedValue}" />

If you want the SelectedValue to return the BinanceEndpoint instance then set SelectedValuePath to "Value":

<ComboBox x:Name="ComboBox" 
          ItemsSource="{Binding EndPoints}" 
          DisplayMemberPath="Key" 
          SelectedValuePath="Value" />

<!-- Display the selected item's 'SocketBaseAddress' value -->
<TextBlock text="{Binding ElementName=ComboBox, Path=SelectedValue.SocketBaseAddress}" />
  • Related