Wpf 使用ItemsSource绑定ListView时,向ListView指示已选择一个或多个项的正确方法是什么?

Wpf 使用ItemsSource绑定ListView时,向ListView指示已选择一个或多个项的正确方法是什么?,wpf,listview,Wpf,Listview,我有一个WPF表单,其中包括一个ListView和以下绑定: <ListView ItemsSource="{Binding SelectableItems}" SelectionMode="Extended"> <ListView.ItemContainerStyle> <Style TargetType="ListViewItem"> <Setter Property="

我有一个WPF表单,其中包括一个ListView和以下绑定:

    <ListView ItemsSource="{Binding SelectableItems}" SelectionMode="Extended">
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="IsSelected" Value="{Binding IsSelected}" />
            </Style>
        </ListView.ItemContainerStyle>
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Data From" DisplayMemberBinding="{Binding Started}" Width="150" />
                <GridViewColumn Header="Data To" DisplayMemberBinding="{Binding Finished}" Width="150" />
            </GridView>
        </ListView.View>
    </ListView>

ViewModel的SelectableItems属性返回SelectableObject项的ObservableCollection,其中SelectableObject继承自ListViewItem

我们面临的挑战如下。初始化视图时,SelectableItems集合中的一个或多个项的IsSelected属性可能设置为true。显示视图时,从视觉上看,每个项目都处于正确的选择状态。不幸的是,“当前选择”信息似乎没有完全传送到ListView本身。在未选定的项目上执行简单的单次单击时,预期的操作是取消选择任何当前选定的项目,然后单次单击的项目将成为IsSelected设置为true的唯一项目。这似乎没有发生。而是将新单击的项添加到以前单击的项集中。。。但这只是第一次。所有后续的单击操作都按预期操作


初始化过程中是否需要执行其他操作来正确初始化ListView对当前所选项目的期望值?

基本上,
ListView
使用
SelectedItems
属性跟踪扩展的选择。当集合绑定到控件时,它无法知道/更新
SelectedItems
集合,因此不知道已选择了对象。不幸的是,
SelectedItems
属性不可绑定

过去,我创建了附加属性,用可绑定属性“包装”了
SelectedItems
集合,但这很麻烦,而且我相当肯定我的实现可能会导致泄漏

或者,当
itemsource
更改时,您可以使用一些次要的代码隐藏/附加属性来设置
SelectedItems
属性:

编辑:更改为附加属性

public class Bindable
{
    public static readonly DependencyProperty InitializeSelectedItemsProperty = DependencyProperty.RegisterAttached(
                                                            "InitializeSelectedItems", typeof(bool), typeof(Bindable), new PropertyMetadata(default(bool), InitializeSelectedItemsChanged));

    private static void InitializeSelectedItemsChanged(DependencyObject depObject, DependencyPropertyChangedEventArgs args)
    {
        var listView = depObject as ListBox;

        if (listView != null && (bool)args.NewValue)
        {
            TypeDescriptor.GetProperties(listView)["ItemsSource"]
                    .AddValueChanged(listView, new EventHandler(ListViewItemsSourceChanged));
        }
    }

    private static void ListViewItemsSourceChanged(object sender, EventArgs eventArgs)
    {
        var listView = sender as ListBox;

        listView.SelectedItems.Clear();

        foreach (var item in listView.ItemsSource.OfType<ISelectable>().Where(i => i.IsSelected))
        {
            listView.SelectedItems.Add(item);
        }
    }
}

<ListView ItemsSource="{Binding SelectableItems}" Bindable.InitializeSelectedItems="True" SelectionMode="Extended" ...>
公共类可绑定
{
public static readonly dependencProperty InitializeSelectedItemsProperty=dependencProperty.RegisterAttached(
“InitializeSelectedItems”、typeof(bool)、typeof(Bindable)、新属性元数据(默认值(bool)、InitializeSelectedItemsChanged));
私有静态无效初始化SelectedItemsChanged(DependencyObject deObject,DependencyPropertyChangedEventArgs args)
{
var listView=deObject作为ListBox;
if(listView!=null&&(bool)args.NewValue)
{
TypeDescriptor.GetProperties(listView)[“ItemsSource”]
.AddValueChanged(listView,新事件处理程序(ListViewItemsSourceChanged));
}
}
私有静态无效ListViewItemsSourceChanged(对象发送方,EventArgs EventArgs)
{
var listView=发送方作为列表框;
listView.SelectedItems.Clear();
foreach(listView.ItemsSource.OfType()中的var项,其中(i=>i.IsSelected))
{
listView.SelectedItems.Add(项目);
}
}
}

解决方案是将所选项目存储在列表中,如果listview是可识别为一对键/值的项目列表,则最好将其存储在词汇表中

然后,当绑定更新列表时,您应该遍历字典并测试listview中是否存在一对键/值,然后检查true是否设置为所选的
属性


这是一种方式。

SelectableObject不应从ListViewItem派生。相反,它应该是视图模型中的纯数据项类。。。但同样的问题仍然存在。(讨论中的项目最初是另一种类型。其他地方的一篇[显然错误]的博客文章建议它应该是ListViewItem。)感谢您的建议@andrew hanlon我试图尽可能避免代码隐藏,但如果这是唯一的选择…-)正如我提到的,您当然可以使用Behavior/Attached属性来创建SelectedItems属性的可绑定包装。。。但是,要确保集合更改事件绑定不会导致泄漏是很困难的。@RichardJFoster另外,如果您只关心最初设置
SelectedItems
,您可以将上面的代码移到可重用的附加属性中-并且由于所有事件绑定都是本地的,因此不应该有泄漏;)@RichardJFoster我把代码移到了一个附加的属性中。。。它应该会起作用。您必须确保有一个公开IsSelected属性的公共ViewModel接口。@AndrewHanlon:当我尝试使用您的代码时,在Bindable类型中找不到InitializeSelectedItems。有什么想法吗?