【问题标题】:How do I make binding work for a list property in a custom control?如何为自定义控件中的列表属性进行绑定?
【发布时间】:2018-07-10 16:01:28
【问题描述】:

由于ListBox 不允许您双向绑定到其SelectedItems 属性,因此我创建了自己的自定义控件MultipleSelectionListBox。它添加了属性BindableSelectedItems,让您可以将IEnumerable 绑定到它。

public class MultipleSelectionListBox : ListBox
{
    public static readonly DependencyProperty BindableSelectedItemsProperty =
        DependencyProperty.Register("BindableSelectedItems",
            typeof(IEnumerable), typeof(MultipleSelectionListBox),
            new FrameworkPropertyMetadata(default(IEnumerable),
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnBindableSelectedItemsChanged));

    public IEnumerable BindableSelectedItems
    {
        get => (IEnumerable)GetValue(BindableSelectedItemsProperty);
        set => SetValue(BindableSelectedItemsProperty, value);
    }

    protected override void OnSelectionChanged(SelectionChangedEventArgs e)
    {
        base.OnSelectionChanged(e);
        BindableSelectedItems = SelectedItems;
    }

    private static void OnBindableSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is MultipleSelectionListBox listBox)
            listBox.SetSelectedItems(listBox.BindableSelectedItems);
    }
}

我绑定到的视图模型的属性是IEnumerable<string> 类型,并且绑定不起作用。当列表框的选择改变时,视图模型的属性总是接收null

如果我将BindableSelectedItems 的类型更改为IEnumerable<string>,则绑定有效。这让我相信绑定不喜欢类型不匹配。我是否不得不明确指定列表项的类型,还是有办法让它保持通用?如果能够将相同的MultipleSelectionListBox 用于其他类型的项目,那就太好了。

【问题讨论】:

  • 你错过了什么吗?在这两种情况下,您都提到 ViewModel 属性是 IEnumerable<String> 类型。
  • @dhilmathy 是的,谢谢。我更正了这个问题。
  • 尝试在此处使用 IEnumerable 甚至 IEnumerable 的任何地方
  • “由于 ListBox 不允许您双向绑定到其 SelectedItems 属性”。你的意思是你不能从vm设置selectedItem?你可以这样做。您既可以从视图中设置它,也可以从视图模型中设置它。你的虚拟机是否实现了 INotifyPropertyChanging、INotifyPropertyChanged?
  • @GianlucaConte SelectedItem 没有问题。问题在于SelectedItems(注意复数)。

标签: c# wpf data-binding listbox


【解决方案1】:

这是因为,SelectedItems 将始终是 System.Collections.IList {System.Windows.Controls.SelectedItemCollection} 类型。因此,当您尝试使用IEnumerable 时,您直接将SelectedItems 分配给BindableSelectedItems。演员表无效并且已将 null 发送到您的 ViewModel 属性。

其中,当您将类型设置为 IEnumerable<string> 时,您会将 SelectedItems 转换为 string 集合,例如 BindableSelectedItems = SelectedItems.Cast<string>();,它与 ViewModel 中的属性类型匹配并且有效。

我看到你想让它通用。为此,您可以尝试类似的方法,

ListBox控件代码

public class MultipleSelectionListBox : ListBox
{
    public static readonly DependencyProperty BindableSelectedItemsProperty =
        DependencyProperty.Register("BindableSelectedItems",
            typeof(IList), typeof(MultipleSelectionListBox),
            new FrameworkPropertyMetadata(default(IList),
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnBindableSelectedItemsChanged));

    public IList BindableSelectedItems
    {
        get => (IList)GetValue(BindableSelectedItemsProperty);
        set => SetValue(BindableSelectedItemsProperty, value);
    }

    protected override void OnSelectionChanged(SelectionChangedEventArgs e)
    {
        base.OnSelectionChanged(e);

        IList selectedItemsList = BindableSelectedItems;
        if (selectedItemsList == null)
            selectedItemsList = (IList)Activator.CreateInstance(ItemsSource.GetType());
        selectedItemsList.Clear();

        foreach (var item in SelectedItems)
            selectedItemsList.Add(item);

        BindableSelectedItems = selectedItemsList;
    }

    private static void OnBindableSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is MultipleSelectionListBox listBox)
            listBox.SetSelectedItems(listBox.BindableSelectedItems);
    }
}

视图模型

public class ViewModel
{
    public IEnumerable Items { get; set; }
    public IEnumerable SelectedItems { get; set; }

    public ViewModel()
    {
        Items = new List<string> { "Test", "Test1" };
        SelectedItems = new List<string> { "Test" };
    }
}

我已经用常见的场景进行了测试,它工作正常。

【讨论】:

  • 它在ItemsSelectedItems 都是List&lt;string&gt; 的场景中有效,但在Itemsstring[] 的场景中不起作用。另外,您是否有理由将selectedItemsList 分配给BindableSelectedItems 然后清除它?为什么不总是将selectedItemsList 分配给Activator 创建的实例?
  • @redcurry 是的,我看到你了。数组将是固定大小的,它将不起作用。所有其他通用集合类型 (IList) 应该可以工作。关于第二个问题,ItemsSelectedItems 可能属于不同类型(如 List&lt;string&gt;ObservableCollection&lt;string&gt;)。因此,对于这种情况,我尝试在 IList selectedItemsList = BindableSelectedItems;selectedItemsList.Clear(); 使用相同的集合类型来清除任何现有项目。
猜你喜欢
  • 1970-01-01
  • 2011-03-20
  • 2011-05-11
  • 1970-01-01
  • 2014-08-11
  • 2012-08-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多