ListBox SelectedItems绑定

时间:2010-04-29 10:30:07

标签: c# wpf data-binding

我想将Listbox selectedItems绑定到数组。但.NET在运行时抛出异常。

d.SetBinding(ListBox.SelectedItemsProperty, new Binding { Source = SomeArray });

其中d是来自XAML的一些ListBox。

例外:

  

无法绑定所选项目。

为什么?

6 个答案:

答案 0 :(得分:8)

您可以订阅ListBox的SelectionChanged事件,并在处理程序中同步所选项目的集合。

在此示例中,Windows DataContext在其构造函数中设置为自身(this)。您也可以从事件处理程序轻松调用逻辑层(如果您使用MVVM,则为ViewModel。)

在Xaml中:

<StackPanel>

    <ListBox
        ItemsSource="{Binding ListBoxItems}"
        SelectionMode="Multiple"
        SelectionChanged="ListBox_SelectionChanged">
    </ListBox>

    <ItemsControl
        ItemsSource="{Binding SelectedItems}">
    </ItemsControl>

</StackPanel>

在代码隐藏中:

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    foreach (string item in e.RemovedItems)
    {
        SelectedItems.Remove(item);
    }

    foreach (string item in e.AddedItems)
    {
        SelectedItems.Add(item);
    }
}

答案 1 :(得分:3)

这是工作解决方案,但是当选择更改时,SelectedItemsProperty不会刷新绑定...

您可以按照以下方式创建自定义控件

public class MyListBox: ListBox{

    public MyListBox()
    { 
         this.SelectionChanged += (s,e)=>{ RefreshBindings(); };
    }

    private void RefreshBindings()
    {
         BindingExpression be = 
             (BindingExpression) GetBindingExpression(
                                      SelectedItemsProperty);
         if(be!=null){
               bd.UpdateTarget();
         }
    }

}

或在您的应用中,您可以在每个列表框中定义事件,如下所示..

myListBox.SelectionChanged += (s,e) => {
    BindingExpression be = 
         (BindingExpression) myListBox.GetBindingExpression(
                                      ListBox.SelectedItemsProperty);
    if(be!=null){
        bd.UpdateTarget();
    }
};

答案 2 :(得分:1)

ListBox.SelectedItems是只读的。你的意思是绑定到ListBox.SelectedItem吗?

答案 3 :(得分:1)

我不确定我是否正确理解您的问题或确切的情况 - 但假设您想要一个列表框“d”显示在另一个列表框“MyOtherListbox”中选择的项目,那么您只需要设置绑定模式“单向”否则会出现错误。

您可以执行类似

的操作
d.SetBinding(ListBox.ItemsSourceProperty, new Binding { Source = MyOtherListbox.SelectedItems, Mode = BindingMode.OneWay});

答案 4 :(得分:1)

这是一个可行的解决方案,您可以轻松适应您的需求:

xaml中:

<Window x:Class="ListBoxSelectedItems.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="MainWindow" Height="200" Width="200">

<StackPanel>
    <ListBox 
        ItemsSource="{Binding ProductListSource, NotifyOnSourceUpdated=True}" 
        SelectedItem="{Binding SelectedProduct, UpdateSourceTrigger=PropertyChanged}" 
        SelectionMode="Multiple" >

        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Item}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>

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

    </ListBox>
    <Label Content="{Binding Text}"/>
</StackPanel>

在代码中:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows;

namespace ListBoxSelectedItems
{
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
}

public class ViewModel : MyNotifyPropertyChanged
{

    public ViewModel()
    {
        ProductListSource.Add(new Product() { Item = "Item_1", IsSelected = false });
        ProductListSource.Add(new Product() { Item = "Item_2", IsSelected = false });
        ProductListSource.Add(new Product() { Item = "Item_3", IsSelected = false });
    }

    private ObservableCollection<Product> _productList = new ObservableCollection<Product>();

    public ObservableCollection<Product> ProductListSource
    {
        get => _productList;
        set
        {
            _productList = value;
            RaisePropertyChanged(nameof(ProductListSource));
        }
    }

    private readonly Product _selectedProduct;

    public Product SelectedProduct
    {
        get => _selectedProduct;
        set
        {
            var selectedItems = ProductListSource.Where(x => x.IsSelected).ToList();
            this.RaisePropertyChanged(nameof(SelectedProduct));

            string s = "{";
            int n = selectedItems.Count;
            for (int i=0; i< n; i++)
            {
                s += selectedItems[i].ToString();
                if (i < n - 1) s += ", ";
            }
            s += "}";
            Debug.WriteLine(s + ".Count= " + n);

        }
    }
}

public class Product : MyNotifyPropertyChanged
{
    private string _item;

    public string Item
    {
        get => _item;
        set
        {
            _item = value;
            RaisePropertyChanged(nameof(Item));
        }
    }

    private bool _isSelected;

    public bool IsSelected
    {
        get => _isSelected;
        set
        {
            _isSelected = value;
            RaisePropertyChanged(nameof(IsSelected));
        }
    }

    public new string ToString() => _item;

}

public class MyNotifyPropertyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

希望有帮助!

答案 5 :(得分:0)

我的诀窍:在xaml中,使用MultiBinding,在Count属性更改中强制执行转换器(它可以工作!)。

<MultiBinding Converter="{StaticResource SelectedRowsTotal }">
    <Binding Path="SelectedItems"  ElementName="listBox1" />
    <Binding Path="SelectedItems.Count"  ElementName="listBox1" />
</MultiBinding>

转换器:

public class SelectedRowsTotal : IMultiValueConverter 
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    {
        var selecteds = values as IEnumerable;

        if (selected == null) 
            return null;

        return selecteds.Cast<SomeType>().Sum(x=> x.SomeProperty) = total;
    }

    object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    {
        return null;
    }
}