我想将Listbox selectedItems绑定到数组。但.NET在运行时抛出异常。
d.SetBinding(ListBox.SelectedItemsProperty, new Binding { Source = SomeArray });
其中d
是来自XAML的一些ListBox。
例外:
无法绑定所选项目。
为什么?
答案 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;
}
}