Items.Count = 0 in SelectionChanged 当有项目时

Items.Count = 0 in SelectionChanged when there are items

我想在单击按钮时显示一些自定义内容(使用数据模板):

<ContentControl x:Name="content" />
<Button Content="Test" Click="button_Click" />

按钮shows/hides这样的内容

VM _vm = new VM();
void button_Click(object sender, RoutedEventArgs e) =>
     content.Content = content.Content == null ? _vm : null;

这是数据模板:

<DataTemplate DataType="{x:Type local:VM}">
    <ListBox ItemsSource="{Binding Items}" SelectionChanged="listBox_SelectionChanged">
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem">
                <Setter Property="IsSelected" Value="{Binding IsSelected}" />
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>
</DataTemplate>

事件处理器:

void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e) =>
    Title = "Items: " + ((ListBox)sender).Items.Count;

视图模型:

public class VM
{
    public List<Item> Items { get; } = new List<Item> { new Item(), new Item(), new Item() };
}
public class Item
{
    public bool IsSelected { get; set; }
}

问题:当卸载数据模板时,SelectionChanged for ListBox 事件出现,但没有任何项目。

我不想参加这个活动。我不想在选择某些内容并卸载数据模板后看到 "Items: 0"。

问题:发生了什么,我该如何防止这种情况发生?


注意:这是非常简短和简化的 MCVE,即不是所有的东西都很漂亮,尽管有关键点:内部带有 ListBox 的数据模板,它使用 IsSelected 绑定,我需要摆脱来自 SelectionChanged 卸载事件。

调用堆栈:

我认为这是因为您总是覆盖内容:

VM _vm = new VM();
void button_Click(object sender, RoutedEventArgs e) =>
     content.Content = content.Content == null ? _vm : null;

改成这样,列表只被分配一次,所以它只被分配一次。

void button_Click(object sender, RoutedEventArgs e)
{
    if (content.Content == null )
    {
        content.Content = _vm; 

        // I also recommend you add the event handler for the ListBox here so it's not fired until you have content.
    }
}

我认为 listBox_SelectionChanged 当您卸载列表时会触发此事件,因为部分实际上已更改,检查那里的项目计数,如果是 0,则将标题设置为默认值。

void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e) =>
Title = (((ListBox)sender).Items.Count > 0)? "Items: " + ((ListBox)sender).Items.Count: "Your title";

这完全按照设计工作。您通过单击列表框中的项目进行了选择。卸载模板后,ItemsSource 绑定断开,项目源变为空。此时,当前选择不再有效(项目源中不存在该项目),因此选择被清除。这是选择的变化:选择从有到无。 预计将在这些情况下引发该事件。

很少需要订阅SelectionChanged。通常最好将 SelectedItem 绑定到视图模型上的 属性。只要选择发生变化,属性 就会更新。您可以响应 属性 变化而不是响应 SelectionChanged 事件。

这种方法很好地避免了您遇到的问题。卸载模板后,SelectedItem 绑定将断开连接,因此您的视图模型将不再更新。因此,清除选择后您将看不到最后的更改。


多选的备选方案

如果您的ListBox支持多选,您可以继续订阅SelectionChanged。但是,不要查询 listBoxItems;相反,扫描 _vm.Items 并查看哪些项目 IsSelected 设置为 true。这应该会告诉您实际的选择,并且结果应该不受正在卸载的模板的影响。

您还可以通过检查处理程序中的 (sender as ListBox)?.ItemsSource 是否为 null 来确定模板已卸载。但是,这应该不是必需的。