根据另一个集合设置ListBox的SelectedItems

3

我有一个绑定到类型为 IEnumerable<string>MyCollectionListBox

 <ListBox x:Name="ListBox" ItemsSource="{Binding MyCollection, Mode=OneWay}" SelectionMode="Multiple"/>

我有另一个包含 MyCollection 子集的 List<string> SubCollection
每当 SubCollection 更改时,我希望根据 SubCollection 来突出显示 SelectedItems
有没有任何 绑定行为 或其他方法可以实现这一点? 编辑: 假设我有一个绑定到 MyCollectionListBox {"橙子", "芒果", "草莓", "菠萝"} 假设我按下一个按钮来从数据库加载数据,结果是 "Orange","Mango",然后放入 SubCollection 中。此时 ListBox 应该将 "Orange","Mango" 作为其 SelectedItems

你尝试过将ListBoxItem的IsSelected属性(multi)绑定到MyCollection当前对象和整个SubCollection吗?如果SubCollection.Contains(current),它将返回true/false。 - michele
List 的 IsSelected 属性? - Simsons
此属性:http://msdn.microsoft.com/library/system.windows.controls.listboxitem.isselected.aspx - michele
“SubCollection changes” 是什么意思?您能举个例子吗? - Haritha
@Haritha,已更新问题并添加了场景。 - Simsons
6个回答

5

我给你提个建议。

你可以绑定到 ListBoxItem 的 "IsSelected" 属性。

为此,您必须使用一组对象(假设是 MyListBoxItem),而不是字符串集合。

public class  MyListBoxItem
{
    public string Description { get; set; }
    public bool IsSelected { get; set; }
}

使用MyListBoxItem类的"IsSelected"属性来绑定ListBoxItem的"IsSelected"属性。请参考以下示例:在您的视图模型中,
this.MyCollection = new ObservableCollection<MyListBoxItem>();

MyListBoxItem item1 = new MyListBoxItem()
item1.Description = "Mango";
item1.IsSelected = true;
MyCollection .add(item1);

MyListBoxItem item2 = new MyListBoxItem()
item2 .Description = "Orange";
item2 .IsSelected = false;
MyCollection .add(item2 );

XAML(在ListBox中)
<ListBox.ItemContainerStyle>
    <Style TargetType="{x:Type ListBoxItem}">
        <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
    </Style>
</ListBox.ItemContainerStyle>

在这种情况下为什么需要说明呢? - Simsons
@Simsons:请再看一下答案。我已经修改了。我描述了Description属性的用法。 - Haritha
1
已经尝试了样例应用,运行良好。一旦在实际应用中成功实现,我会及时更新您的。 - Simsons

4
您可以为 ListBox 创建一个 List AttachedProperty,当列表更改时,它会将项目添加到 ListBox.SelectedItems 中。 AttachedProperty 解决方案使得 WPF 保持干净,同时也保留了您的 MVVM 模式。此外,它可以使该功能在所有项目中重复使用 :)
以下是示例:

附加属性:

public static class ListBoxExtensions
{
    // Using a DependencyProperty as the backing store for SearchValue.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SelectedItemListProperty =
        DependencyProperty.RegisterAttached("SelectedItemList", typeof(IList), typeof(ListBoxExtensions),
            new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnSelectedItemListChanged)));

    public static IList GetSelectedItemList(DependencyObject obj)
    {
        return (IList)obj.GetValue(SelectedItemListProperty);
    }

    public static void SetSelectedItemList(DependencyObject obj, IList value)
    {
        obj.SetValue(SelectedItemListProperty, value);
    }

    private static void OnSelectedItemListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var listbox = d as ListBox;
        if (listbox != null)
        {
            listbox.SelectedItems.Clear();
            var selectedItems = e.NewValue as IList;
            if (selectedItems != null)
            {
                foreach (var item in selectedItems)
                {
                    listbox.SelectedItems.Add(item);
                }
            }
        }
    }
}

Xaml的使用:

<ListBox ItemsSource="{Binding Items}" SelectionMode="Multiple"
         local:ListBoxExtensions.SelectedItemList="{Binding SelectedItems}" />

演示:

如果您想进行测试,这是一个可用的示例:

Xaml代码:

<Window x:Class="WpfApplication17.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication17"
        Title="MainWindow" Height="227" Width="170" Name="UI">
    <Grid DataContext="{Binding ElementName=UI}">
        <ListBox ItemsSource="{Binding Items}" SelectionMode="Multiple"
                 local:ListBoxExtensions.SelectedItemList="{Binding SelectedItems}"  Margin="0,0,0,37"   >
                 <ListBox.Resources>
                    <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="{x:Static SystemColors.HighlightColor}" />
                    <Style TargetType="ListBoxItem">
                       <Style.Triggers>
                          <Trigger Property="IsSelected" Value="True">
                             <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
                          </Trigger>
                       </Style.Triggers>
                    </Style>
                 </ListBox.Resources>
        </ListBox>
        <Button Content="Populate SelectedItemList" Click="Button_Click" Height="32" Margin="2,0,1,2" VerticalAlignment="Bottom"/>
    </Grid>
</Window>

代码:

namespace WpfApplication17
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private List<string> _selectedItems = new List<string>();
        private ObservableCollection<string> _items = new ObservableCollection<string> 
        { "Orange", "Mango", "Stawberry", "Pineapple", "Apple", "Grape", "Banana" };

        public MainWindow()
        {
            InitializeComponent();
        }

        public ObservableCollection<string> Items
        {
            get { return _items; }
            set { _items = value; }
        }

        public List<string> SelectedItems
        {
            get { return _selectedItems; }
            set { _selectedItems = value; OnPropertyChanged("SelectedItems"); }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            SelectedItems = new List<string> { "Orange", "Pineapple", "Apple" };
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string e)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(e));
        }
    }

    public static class ListBoxExtensions
    {
        // Using a DependencyProperty as the backing store for SearchValue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedItemListProperty =
            DependencyProperty.RegisterAttached("SelectedItemList", typeof(IList), typeof(ListBoxExtensions),
                new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnSelectedItemListChanged)));

        public static IList GetSelectedItemList(DependencyObject obj)
        {
            return (IList)obj.GetValue(SelectedItemListProperty);
        }

        public static void SetSelectedItemList(DependencyObject obj, IList value)
        {
            obj.SetValue(SelectedItemListProperty, value);
        }

        private static void OnSelectedItemListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var listbox = d as ListBox;
            if (listbox != null)
            {
                listbox.SelectedItems.Clear();
                var selectedItems = e.NewValue as IList;
                if (selectedItems != null)
                {
                    foreach (var item in selectedItems)
                    {
                        listbox.SelectedItems.Add(item);
                    }
                }
            }
        }
    }
}

结果:

在此输入图片描述

(注:该图片无法翻译,因为它是一个屏幕截图)

这很完美,但还有一个问题,当我手动选择时,选定的项目会显示为蓝色,但现在是浅灰色。我们能否覆盖这个设置? - Simsons
选择项变灰是因为在按下按钮后,列表框失去了焦点。我已经在上面的演示中添加了一个修复方法,它只是将非活动列表框颜色设置为与焦点颜色相同。 - sa_ddam213
类型 'SystemColors' 不包含名为 'InactiveSelectionHighlightBrushKey' 的静态成员。我正在针对.NET Framework 4.0,但 InactiveSelectionHighlightBrushKey 针对 v4.5。 - Simsons
1
尝试使用以下代码:<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="{x:Static SystemColors.HighlightColor}" /> - sa_ddam213
我不知道你是否仍在查看此内容,但是,这对包含在<DataTemplate>中的ListBox是否有效?我正在尝试像您在上面的演示中一样使用它;但是,即使选择更改已发生,任何3种方法都没有被调用。我可以看到正在处理dependencyproperty调用。 - Shintaro Takechi
显示剩余3条评论

2
标准数据绑定无法工作,因为SelectedItems属性是只读的。
一种简单的方法是手动迭代MyCollection,并根据SubCollection中的项目设置每个项目的IsSelected属性。为此,MyCollection列表中的项目应包含从ListBoxItem继承的对象,该对象将公开IsSelected属性。
以下是一个演示它的示例WPF应用程序:
点击按钮会根据SubCollection列表中的项目更新所选项目。目前我已经硬编码了SubCollection列表的值。
根据您的实现,您可以更新SubCollection,并将代码挂钩到任何其他事件中适当的按钮单击事件内。(例如,将SubCollection列表设置为ObservableCollection并挂钩到ObservableCollection.CollectionChange)
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        Title="MainWindow" Height="250" Width="255">
    <Grid>
        <StackPanel Orientation="Vertical">
            <ListBox x:Name="ListBox" ItemsSource="{Binding MyCollection}" SelectionMode="Extended" Margin="10,10"/>
            <Button Content="UpdateSelection"  Click="Button_Click" Margin="10,10"/>
        </StackPanel>
    </Grid>
</Window>

MainWindow.xaml.cs

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            _myCollection = new ObservableCollection<MyListBoxItem>() { new MyListBoxItem("Orange"), new MyListBoxItem("Mango"), new MyListBoxItem("Stawberry"), new MyListBoxItem("Pineapple") };

            //Items to be selected on this.MyCollection ListBox
            this.SubCollection = new List<string>() { "Pineapple", "Mango" };

            this.DataContext = this;
        }

        private ObservableCollection<MyListBoxItem> _myCollection;
        public ObservableCollection<MyListBoxItem> MyCollection
        {
            get { return _myCollection; }
            set
            {
                this._myCollection = value;
            }
        }

        public IList<string> SubCollection { get; set; }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // Clear all the selected items in the ListBox
            this.MyCollection.ToList().ForEach(item => item.IsSelected = false);

            // SELECT only the items in MySubCollection into this.MyCollection ListBox
            this.MyCollection.Where(item => this.SubCollection.Contains(item.Content.ToString())).ToList().ForEach(item => item.IsSelected= true);
        }
    }

    public class MyListBoxItem : ListBoxItem
    {
        public MyListBoxItem(string displayName)
        {
            this.Content = displayName;
        }
    }
}

@Simsons:你试用过它了吗? - jacob aloysious
尝试过了,运行良好,但我需要更改我的数据结构,这是需要避免的。你怎么看? - Simsons
@Simsons:你的意思是将 MyCollection 列表从 List<string> 更新为 List<MyListBoxItem> 吗? - jacob aloysious
是的,有一个返回值为List<string>的服务,所以我不能改变它,因此我需要迭代并更新新的MyListBoxItem对象。 - Simsons
好的,那我建议采纳 @sa_ddam213 的答案。祝你好运! - jacob aloysious

1
使用 ListBox 的 "SelectedValue" 属性。(因为你正在使用字符串集合来绑定 List Box。)
  1. 在你的视图模型中定义一个字符串类型属性(比如说 MySelectedValue)。
  2. 然后将它绑定到两个 List Box 的 "SelectedValue" 属性上。(记得在主 List Box 中设置 Mode = TwoWay)
  3. 假设当你在子集合 List Box 中选择一个项目时,会执行一个方法。(你必须触发 Sub Collection ListBox 的 SelectionChanged 事件)
  4. 就这样。

所以。你需要做的是:

使用同一个属性来绑定BOTH List Boxes的 "SelectedValue" 属性。

假设你想要将一个对象集合绑定到 List Box 上。那么可以使用 SelectedItem 属性来完成这个任务。


这只是一个单独的列表框,而不是两个分开的列表框。我想根据所选值选择ListBox项目。 - Simsons

1

0

如果您知道要选择的索引号,可以使用

int[] index = {1, 2, 5};
for( int i in index) 
{    
   listBox.SetSelected( i, true );
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接