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

SelectionChanged command not triggered when changing ListBox's SelectedItem programmatically

我有一个简单的 WPF 页面,其中包含一个包含列表框的自定义控件 (BrowsingPanel),以及另一个显示与在列表框中 selected 的元素相关的数据的控件 (ItemDataSheet)。当我单击 ListBox 中的一个项目时,一个命令被发送到 BrowsingPanelViewModel,它发送一条消息。消息由更新 ItemDataSheet 视图的 ItemDataSheetViewModel 接收。

这是我的 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 项默认为 selected。为此,我尝试了两件事: 首先,我尝试 select 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 显示与该项目对应的数据,但该项目未(在视觉上)select在 ListBox 中编辑。

然后,我尝试 select 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];
}

这就是我出现奇怪行为的地方。 select 列表框中的项目正确,但未触发 SelectedItemChanged 命令。我不明白为什么。

有趣的是,如果我将我的 EventTrigger 替换为我放入代码后面的 SelectionChanged 事件,如下所示,然后调用回调函数。

<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 中的位可视化 selects 列表中的项目。但是我觉得这不是很优雅...

在我看来,以编程方式更改 ListBox 的 SelectedIndex 应该会触发 SelectionChanged 命令,但事实并非如此。

任何帮助将不胜感激!

切记这是针对单个 select 列表框的解决方案。

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

它可以很简单,只需要列表框上的 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");
    }
}

setter 中更改的通知属性 是这里的关键。

然后您可以从视图模型 select 通过分配此 属性 第一个列表项。

您还需要一个 DataTemplate 用于您的 ListBox 范围内的 MyItem 对象,它可以很简单:

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

或者随便什么。