以编程方式更改ListBox的SelectedItem时未触发SelectionChanged命令

时间:2015-03-20 17:30:36

标签: c# wpf xaml mvvm listbox

我有一个简单的WPF页面,其中包含一个包含ListBox的自定义控件(BrowsingPanel),另一个控件(ItemDataSheet)显示与在ListBox中选择的元素相关的数据。当我单击ListBox中的某个项目时,会向BrowsingPanelViewModel发送一个命令,该命令会发送一条消息。 ItemDataSheetViewModel接收消息,更新ItemDataSheet视图。

这是我的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>

它很好用,除了我希望默认选择第一个ListBox项。为此,我尝试了两件事: 首先,我尝试选择BrowsingPanelViewModel构造函数中的第一项,如下所示。

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));
}

这很好用,ItemDataSheetViewModel显示与此项目对应的数据,但在ListBox中没有(视觉上)选择该项目。

然后,我尝试从BrowsingPanel视图中选择第一项。在后面的代码中,我有一个itemsList_Loaded的处理程序,如下所示:

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];
}

这就是我得到奇怪行为的地方。这会在ListBox中正确选择项目,但不会触发SelectedItemChanged命令。我不明白为什么。

有趣的是,如果我用一个SelectionChanged事件替换我的EventTrigger,我将其置于后面的代码中,如下所示,则调用回调函数。

<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>

显然,通过组合我提到的2个解决方案,它可以工作:视图模型构造函数中的位在ItemDataSheet视图中显示相应的数据,而itemsList_Loaded中的位可视地选择List中的项目。但我发现这不是很优雅......

在我看来,以编程方式更改ListBox的SelectedIndex应该触发SelectionChanged命令,但它没有。

任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:3)

请记住,这是单个选择列表框的解决方案。

你真的不需要大部分代码。

它可以像只需要列表框中的SelectedValue属性一样简单:

<Grid>
    <ListBox x:Name="itemsList"
         ItemsSource="{Binding MyItems}"
         SelectedValue="{Binding Path=MySelectedItem, Mode=TwoWay}"
         Background="DarkGray">
    </ListBox>
</Grid>

然后可以使用MySelectedItem属性将其绑定到BrowsingPanelViewModel:

private MyItem m_MySelectedItem;
public MyItem MySelectedItem
{
    get
    {
        return m_MySelectedItem;
    }
    set
    {
       m_MySelectedItem = value;

       NotifyPropertyChanged("MySelectedItem");
    }
}

设置器中的notifypropertychanged是关键所在。

然后,您可以通过分配此属性从viewmodel中选择第一个列表项。

您还需要在ListBox范围内为MyItem对象创建一个DataTemplate,它可以简单如下:

<DataTemplate DataType={x:Type MyItem}>
    <Textblock Text="{Binding Path=MyItemDescription"/>
</DataTemplate>

或者其他什么。