Populate one combobox based on selection of another
I am learning wpf mvvm and have been struggling with what I feel is probably simple but have not been able to resolve on my own.
What I want is to be able to select an item in a populated combobox and then populate another combobox based on that selection. I can't seem to get the second combobox loaded in r开发者_StackOverflow社区esponse to a selection.
I've included an example with 2 comboboxes and one textblock. When I run the app and select an item in the first combo, the textblock is updated based on the binding, but I don't know where to call the code to update the second combobox.
I tried adding the call to the SelectedString property setter, but that does not seem to ever be called. I'm sure I'm missing something simple, but I need someone to help lift the veil!
I've tried suggestions on other posts but so far I have been unsuccessful.
Here is the xaml for the view:
<Window x:Class="ThrowAwayMVVMApp.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main Window" Height="400" Width="800">
<DockPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="60*" />
<RowDefinition Height="282*" />
</Grid.RowDefinitions>
<!-- Add additional content here -->
<ComboBox ItemsSource="{Binding MyStrings}" SelectedItem="{Binding Path=SelectedString, Mode=TwoWay}" Height="23" HorizontalAlignment="Left" Margin="18,24,0,0" Name="comboBox1" VerticalAlignment="Top" Width="204" />
<TextBlock Text="{Binding SelectedString}" Height="23" HorizontalAlignment="Left" Margin="276,24,0,0" Name="textBlock1" VerticalAlignment="Top" Width="227" Background="#FFFAE7E7" />
<ComboBox ItemsSource="{Binding ResultStrings}" Height="23" HorizontalAlignment="Left" Margin="543,25,0,0" Name="comboBox2" VerticalAlignment="Top" Width="189" />
</Grid>
</DockPanel>
</Window>
Here is the view model:
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
this.MyStrings = new ObservableCollection<string>
{
"One",
"Two",
"Three",
"Four",
"Five"
};
}
public ObservableCollection<string> MyStrings { get; set; }
public ObservableCollection<string> ResultStrings { get; set; }
public string SelectedString
{
get { return (string)GetValue(SelectedStringProperty); }
set
{
SetValue(SelectedStringProperty, value);
this.ResultStrings = getResultStrings(value);
}
}
// Using a DependencyProperty as the backing store for SelectedString. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedStringProperty =
DependencyProperty.Register("SelectedString", typeof(string), typeof(MainViewModel), new UIPropertyMetadata(""));
private ObservableCollection<string> getResultStrings(string input)
{
ObservableCollection<string> result = null;
if (input == "Three")
{
result = new ObservableCollection<string> { "Six", "Seven", "Eight" };
}
else
{
result = new ObservableCollection<string> { "Nine", "Ten", "Eleven" };
}
return result;
}
}
The SelectedString dependency property implementation is wrong: you should register PropertyChangedCallback to be notified when the DP is accessed directly and not with the CLR property set (see http://msdn.microsoft.com/en-us/library/system.windows.propertychangedcallback.aspx); this way you can change the related collection even if the DP is used directly.
When binding against a dependency property (like SelectedString
), the WPF binding doesn't use the CLR property setter, so you never get getResultStrings
called.
By the way, I would consider using a POCO approach on View Models, implementing INotifyPropertyChanged: DP are a pain to write and add a lot of noise to VMs (other than a nasty dependency on System.Windows).
Have a look at this blog post for a extensive comparison: http://kentb.blogspot.com/2009/03/view-models-pocos-versus.html
Try
private string selectedString;
public string SelectedString
{
get { return selectedString; }
set
{
if (selectedString == value) return;
selectedString = value;
// Required for the UI to know the change was successful
RaisePropertyChanged("SelectedString");
LoadStringResults(value);
}
}
private ObservableCollection<string> resultStrings;
public ObservableCollection<string> ResultStrings
{
get { return resultStrings; }
set
{
if (resultStrings== value) return;
resultStrings= value;
// Required for databinding to know that ResultStrings changed
// Previously you changed this property without updating the UI
RaisePropertyChanged("ResultStrings");
}
}
private void LoadStringResults(string input)
{
ObservableCollection<string> result = null;
if (input == "Three")
{
result = new ObservableCollection<string> { "Six", "Seven", "Eight" };
}
else
{
result = new ObservableCollection<string> { "Nine", "Ten", "Eleven" };
}
ResultStrings = result;
}
精彩评论