Reputation: 95
I have a simple WPF page with a custom control (BrowsingPanel) containing a ListBox, and another control (ItemDataSheet) which displays data related to the element which is selected in the ListBox. When I click on an item in the ListBox, a command is sent to the BrowsingPanelViewModel, which sends a message. The message is received by the ItemDataSheetViewModel, which updates the ItemDataSheet view.
This is my BrowsingPanel.xaml:
<Grid>
<ListBox x:Name="itemsList"
ItemsSource="{Binding MyItems}"
Background="DarkGray">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"
CommandParameter="{Binding ElementName=itemsList, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
</Grid>
It works well, except that I would like the first ListBox item to be selected by default. To do so, I've tried two things: First, I've tried to select the first item in the BrowsingPanelViewModel's constructor as shown below.
public RelayCommand<MyItem> SelectedItemChangedCommand { get; private set; }
public BrowsingPanelViewModel()
{
SelectedItemChangedCommand = new RelayCommand<MyItem>(SelectedItemChanged);
MyItems = new ObservableCollection<MyItem>();
MyItems.Add(ParsetemFromResourceName("Resources/toto.txt"));
MyItems.Add(ParseItemFromResourceName("Resources/tata.txt"));
MyItems.Add(ParseItemFromResourceName("Resources/titi.txt"));
//Select the first item if there's one
if (MyItems.Any())
SelectedItemChanged(MyItems.First());
}
void SelectedItemChanged(MyItem selectedItem)
{
Messenger.Default.Send(new NotificationMessage<MyItem>(selectedItem, Notification.SelectedMyChanged));
}
This works fine, the ItemDataSheetViewModel displays the data corresponding to this item, but the item is not (visually) selected in the ListBox.
Then, I've tried to select the first item from the BrowsingPanel view. In the code behind, I have a handler for itemsList_Loaded which looks like this:
private void itemsList_Loaded(object sender, RoutedEventArgs e)
{
//Select the first item by default
itemsList.Focus();
if (itemsList.Items.Count > 0)
itemsList.SelectedItem = itemsList.Items[0];
}
And this is where I get a weird behavior. This selects the item correctly in the ListBox, but the SelectedItemChanged command is not triggered. And I don't understand why.
The funny part is that if I replace my EventTrigger with a SelectionChanged event that I put in the code behind as shown below, then the callback function is called.
<Grid>
<ListBox x:Name="itemsList"
ItemsSource="{Binding MyItems}"
Background="DarkGray"
Loaded="itemsList_Loaded"
SelectionChanged="itemsList_SelectionChanged"> <!-- This is called when changing SelectedItem in the Loaded -->
</ListBox>
</Grid>
Obviously, by combining the 2 solutions I have mentioned, it works: the bit in the view model constructor displays the appropriate data in the ItemDataSheet view, while the bit in the itemsList_Loaded visually selects the item in the List. But I don't find this very elegant...
It seems to me that programmatically changing the ListBox's SelectedIndex should trigger the SelectionChanged command, but it doesn't.
Any help will be appreciated!
Upvotes: 0
Views: 2665
Reputation: 336
Bare in mind this is a solution for a single select listbox.
You really don't need most of that code.
It can be as simple as only needing the SelectedValue property on the listbox:
<Grid>
<ListBox x:Name="itemsList"
ItemsSource="{Binding MyItems}"
SelectedValue="{Binding Path=MySelectedItem, Mode=TwoWay}"
Background="DarkGray">
</ListBox>
</Grid>
This can then be bound to your BrowsingPanelViewModel with the MySelectedItem property:
private MyItem m_MySelectedItem;
public MyItem MySelectedItem
{
get
{
return m_MySelectedItem;
}
set
{
m_MySelectedItem = value;
NotifyPropertyChanged("MySelectedItem");
}
}
The notifypropertychanged in the setter is key here.
You can then from your viewmodel select the first list item by assigning this property.
You will also need a DataTemplate for your MyItem object in the scope of your ListBox which can be as simple as:
<DataTemplate DataType={x:Type MyItem}>
<Textblock Text="{Binding Path=MyItemDescription"/>
</DataTemplate>
Or whatever.
Upvotes: 3