WPF, property bound to dependent ComboBox always gives initial value
I have a ComboBox
that needs to depend on the value of another ComboBox
. This part already works, with the dependent ComboBox
refreshing when a new value is chosen in the independent ComboBox
:
<!-- Independent -->
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="2"
x:Name="cbo_product" VerticalAlignment="Center" Width="120"
ItemsSource="{Binding Source={StaticResource productsXml}}"
DisplayMemberPath="@name" SelectedValuePath="@name"
SelectionChanged="cbo_product_SelectionChanged"
SelectedValue="{Binding Path=Product}" />
<!-- Dependent -->
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Row="3" Grid.Column="2"
x:Name="cbo_component" VerticalAlignment="Center" Width="201"
DataContext="{开发者_JS百科Binding SelectedItem, ElementName=cbo_product}"
ItemsSource="{Binding XPath=Components/Component}"
DisplayMemberPath="@name" SelectedValuePath="@name"
SelectedValue="{Binding Path=Component}"
SelectionChanged="cbo_component_SelectionChanged" />
In the C# class behind this, I have:
public MyUserControlConstructor()
{
MyViewModelInstance= new MyViewModel();
DataContext = MyViewModelInstance;
}
And in MyViewModel
, I have:
public string Component
{
get { return _component; }
set
{
if (value == _component)
{
return;
}
_component = value;
onPropertyChanged(PropertyNames.Component);
}
}
private void onPropertyChanged(PropertyNames fieldName)
{
if (null == PropertyChanged)
{
return;
}
string propertyName = Enum.GetName(typeof(PropertyNames), fieldName);
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
When I change the dependent ComboBox
(Component), it shows up with the new value in my app, of course. However, when I hit a button that causes the value of the Component
property to be displayed, it is always the initial value, and not the value I just chose in the ComboBox
. I think there must be an error in my XAML. For the C#, I tried to follow a combination of this and this guide. How do I tie my dependent ComboBox
to XML values nested in the SelectedItem
of the independent ComboBox
, but still update the Component
property in my class?
Edit: my suspicion is that things are wonky because I set the DataContext
for the dependent ComboBox
in two places: first in the constructor in C#, to my view model, and second in the XAML, to DataContext="{Binding SelectedItem, ElementName=cbo_product}"
.
Edit: I had been setting initial values in the constructor to my view model class. When I take out the initial value for the Component
property, then even after I change the selected value in the dependent ComboBox
, I still get no value from the Component
property. This pretty much just rehashes what I already knew: the dependent ComboBox
is tied to the independent ComboBox
(it gets its data from the independent ComboBox
, that is), but not to the Component
property.
Edit: by request, here's a sample of my XML:
<Products xmlns="">
<Product name="Awesomeness">
<Components>
<Component name="Component of Glory"/>
<Component name="Component of Doom"/>
</Components>
</Product>
</Products>
Edit: I'm guessing a MultiBinding
would be of use, after looking at this and this.
Edit: it seems like I should be able to get the dependent ComboBox
to work without setting DataContext
, just by using ItemsSource
:
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Row="3" Grid.Column="2"
x:Name="cbo_component" VerticalAlignment="Center" Width="201"
ItemsSource="{Binding ElementName=cbo_product, Path=SelectedItem,
XPath=Components/Component}"
DisplayMemberPath="@name" SelectedValuePath="@name"
SelectedValue="{Binding Path=Component}"
SelectionChanged="cbo_component_SelectionChanged"/>
However, this doesn't work: the dependent ComboBox
is empty, instead of showing all the Component names.
The way I found of getting around this involves setting the ItemsSource in C# instead of XAML, which I would prefer not to do. However, it works, and after a day and a half of banging on this, it's the best I came up with.
In XAML:
<!-- Independent -->
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="2"
x:Name="cbo_product" VerticalAlignment="Center" Width="120"
ItemsSource="{Binding Source={StaticResource productsXml}}"
DisplayMemberPath="@name" SelectedValuePath="@name"
SelectionChanged="cbo_product_SelectionChanged"
SelectedItem="{Binding Path=ProductNode}"
SelectedValue="{Binding Path=Product}" />
<!-- Dependent -->
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Row="3" Grid.Column="2"
x:Name="cbo_component" VerticalAlignment="Center" Width="201"
DisplayMemberPath="@name" SelectedValuePath="@name"
SelectedValue="{Binding Path=Component, Mode=TwoWay}"
SelectionChanged="cbo_component_SelectionChanged"/>
In C#, the event handler for when the independent ComboBox
changes:
private void cbo_product_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
// Set ItemsSource of dependent ComboBox
cbo_component.ItemsSource = getChildNodesFromComboBox(
sender as ComboBox, "Components/Component"
);
cbo_component.SelectedIndex = 0;
}
// Helper method to do XPath query and get child nodes from selected value of
// independent ComboBox
private XmlNodeList getChildNodesFromComboBox(ComboBox comboBox,
string xpath)
{
if (null == comboBox)
{
return null;
}
var xml = comboBox.SelectedItem as XmlElement;
if (null == xml)
{
return null;
}
return xml.SelectNodes(xpath);
}
Now the Component
property in my view model class, to which my dependent ComboBox
is bound in XAML, gets populated with the value selected in the dependent ComboBox
because I didn't have to change the DataContext
of the dependent ComboBox
.
精彩评论