以编程方式更改 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>
或者随便什么。
我有一个简单的 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>
或者随便什么。