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}" />